Search code examples
rtidyeval

Is it possible to pass multible variables to the same curly curly?


I am building a function that uses {{ }} (curly curly or double mustache)

I would like the user to be able to pass multiple variables into the same {{ }}, but I am not sure if this is possible using {{ }}. I can't find any examples showing how to do this.

Can you tell me if it possible, and if yes help me make the below minimal reprex work?

library(tidyverse)

group_mean <- function(.data, group){
  .data %>% 
    group_by({{group}}) %>% 
    summarise_all(mean)

}

# Works
mtcars %>% 
  group_mean(group = cyl)

# Fails
mtcars %>% 
  group_mean(group = c(cyl, am)) 

Error: Column `c(cyl, am)` must be length 32 (the number of rows) or one, not 64 

Solution

  • Edit 2022: Nowadays we'd tend to use the c() syntax of tidyselect for taking in multiple groups of variables.

    library(dplyr)
    
    my_mean <- function(data, group_vars, summary_vars) {
      data |>
        group_by(across({{ group_vars }})) |>
        summarise(across({{ summary_vars }}, \(x) mean(x, na.rm = TRUE)))
    }
    
    mtcars |> my_mean(c(cyl, am), c(mpg, disp))
    #> `summarise()` has grouped output by 'cyl'. You can override using the
    #> `.groups` argument.
    #> # A tibble: 6 × 4
    #> # Groups:   cyl [3]
    #>     cyl    am   mpg  disp
    #>   <dbl> <dbl> <dbl> <dbl>
    #> 1     4     0  22.9 136.
    #> 2     4     1  28.1  93.6
    #> 3     6     0  19.1 205.
    #> 4     6     1  20.6 155
    #> 5     8     0  15.0 358.
    #> 6     8     1  15.4 326
    

    See also the Bidge patterns section in https://rlang.r-lib.org/reference/topic-data-mask-programming.html


    If your function takes several groups of multiple variables, you need external quoting with vars(). This function simply capture its inputs as a list of expressions:

    vars(foo, bar)
    #> [[1]]
    #> <quosure>
    #> expr: ^foo
    #> env:  global
    #>
    #> [[2]]
    #> <quosure>
    #> expr: ^bar
    #> env:  global
    

    Take an argument that you splice with !!!:

    group_mean <- function(.data, .vars, ...) {
      .data <- doingsomethingelse(.data, ...)
    
      .data %>% 
         group_by(!!!.vars) %>% 
         summarise_all(mean)
    }
    

    Use it like this:

    data %>% group_mean(vars(foo, bar), baz, quux)