Search code examples
rtidyverserlangtidyeval

simplifying tidyeval with multiple symbols


The following function behaves as desired: several variables can be passed to group_by without the need to put them into alist() or dplyr::vars:


mean_by_grp <- function(df, meanvar, grp) {
  
  grouping <- enexpr(grp) %>% 
    expr_deparse %>% 
    str_split(",",simplify = T) %>% `[`(1,) %>% 
    map(str_remove,"c\\(") %>% map(str_remove,"\\)") %>% map(str_trim) %>% 
    unlist %>% syms
  
  df %>% 
    group_by(!!!syms(grouping)) %>% 
    summarise("average_{{meanvar}}" := mean({{meanvar}}, na.rm = TRUE),
              .groups = 'drop')
}

starwars %>% mean_by_grp(height, species)
starwars %>% mean_by_grp(height, c(species, homeworld))

However, it is complicated. I need to turn c(var1,....varn) into a string, split it and turn it into a list of symbols so I can use with with syms.

Isn't there a much easier way to do this?

Of course, I could use ellipses instead of grp, but then I can only have one argument that passes multiple symbols to another function.


Solution

  • One option would be dplyr::across:

    mean_by_grp <- function(df, meanvar, grp) {
      df %>% 
        group_by(across({{ grp }})) %>% 
        summarise("average_{{meanvar}}" := mean({{meanvar}}, na.rm = TRUE),
                  .groups = 'drop')
    }
    
    library(dplyr)
    
    starwars %>% mean_by_grp(height, species)
    #> # A tibble: 38 × 2
    #>    species   average_height
    #>    <chr>              <dbl>
    #>  1 Aleena               79 
    #>  2 Besalisk            198 
    #>  3 Cerean              198 
    #>  4 Chagrian            196 
    #>  5 Clawdite            168 
    #>  6 Droid               131.
    #>  7 Dug                 112 
    #>  8 Ewok                 88 
    #>  9 Geonosian           183 
    #> 10 Gungan              209.
    #> # … with 28 more rows
    starwars %>% mean_by_grp(height, c(species, homeworld))
    #> # A tibble: 58 × 3
    #>    species  homeworld   average_height
    #>    <chr>    <chr>                <dbl>
    #>  1 Aleena   Aleen Minor             79
    #>  2 Besalisk Ojom                   198
    #>  3 Cerean   Cerea                  198
    #>  4 Chagrian Champala               196
    #>  5 Clawdite Zolan                  168
    #>  6 Droid    Naboo                   96
    #>  7 Droid    Tatooine               132
    #>  8 Droid    <NA>                   148
    #>  9 Dug      Malastare              112
    #> 10 Ewok     Endor                   88
    #> # … with 48 more rows