Search code examples
rdplyrtidyeval

Passing a list of arguments to a function with quasiquotation


I am trying to write a function in R that summarizes a data frame according to grouping variables. The grouping variables are given as a list and passed to group_by_at, and I would like to parametrize them.

What I am doing now is this:

library(tidyverse)

d = tribble(
  ~foo, ~bar, ~baz,
  1, 2, 3,
  1, 3, 5
  4, 5, 6,
  4, 5, 1
)

sum_fun <- function(df, group_vars, sum_var) {
  sum_var = enquo(sum_var)
  return(
    df %>% 
      group_by_at(.vars = group_vars) %>% 
      summarize(sum(!! sum_var))
  )
}

d %>% sum_fun(group_vars = c("foo", "bar"), baz)

However, I would like to call the function like so:

d %>% sum_fun(group_vars = c(foo, bar), baz)

Which means the grouping vars should not be evaluated in the call, but in the function. How would I go about rewriting the function to enable that?

I have tried using enquo just like for the summary variable, and then replacing group_vars with !! group_vars, but it leads to this error:

Error in !group_vars : invalid argument type

Using group_by(!!!group_vars) yields:

Column `c(foo, bar)` must be length 2 (the number of rows) or one, not 4 

What would be the proper way to rewrite the function?


Solution

  • You can rewrite the function using a combination of dplyr::group_by(), dplyr::across(), and curly curly embracing {{. This works with dplyr version 1.0.0 and greater.

    I've edited the original example and code for clarity.

    library(tidyverse)
    
    my_data <- tribble(
      ~foo, ~bar, ~baz,
       "A",  "B",    3,
       "A",  "C",    5,
       "D",  "E",    6,
       "D",  "E",    1
    )
    
    sum_fun <- function(.data, group, sum_var) {
        .data %>% 
          group_by(across({{ group }})) %>% 
          summarize("sum_{{sum_var}}" := sum({{ sum_var }}))
    }
    
    sum_fun(my_data, group = c(foo, bar), sum_var = baz)
    #> `summarise()` has grouped output by 'foo'. You can override using the `.groups` argument.
    #> # A tibble: 3 x 3
    #> # Groups:   foo [2]
    #>   foo   bar   sum_baz
    #>   <chr> <chr>   <dbl>
    #> 1 A     B           3
    #> 2 A     C           5
    #> 3 D     E           7
    

    Created on 2021-09-06 by the reprex package (v2.0.0)