4

I want to create a function that takes a grouping argument. Which can be a single or multiple variables. I want it to look like this:

wanted <- function(data, groups, other_params){
  data %>% group_by( {{groups}} ) %>% count()
}

This work only when a single group is given but breaks when there are multiple groups. I know it's possible to use the following with ellipsis ... (But I want the syntax groups = something):

not_wanted <- function(data, ..., other_params){
  data %>% group_by( ... ) %>% count()
}

Here is the entire code:

library(dplyr)
library(magrittr)

iris$group2 <- rep(1:5, 30)

wanted <- function(data, groups, other_params){
  data %>% group_by( {{groups}} ) %>% count()
}

not_wanted <- function(data, ..., other_params){
  data %>% group_by( ... ) %>% count()
}

# works
wanted(iris, groups = Species )
not_wanted(iris, Species, group2)

# doesn't work
wanted(iris, groups = vars(Species, group2) )
wanted(iris, groups = c(Species, group2) )
wanted(iris, groups = vars("Species", "group2") )
#  Error: Column `vars(Species, group2)` must be length 150 (the number of rows) or one, not 2

Ismail Müller
  • 395
  • 1
  • 7
  • could you clarify what variable should work for `group`, a character vector of `length >= 1`? also, why not do the grouping in `count()` call – Benjamin Schwetz Jan 06 '20 at 12:19
  • 4
    Just change your `{{...}}` to `!!!` - then the call using `vars(Species, group2)` will work - see https://dplyr.tidyverse.org/articles/programming.html#capturing-multiple-variables – cwthom Jan 06 '20 at 13:01

3 Answers3

5

You guys are over complicating things, this works just fine:

library(tidyverse)

wanted <- function(data, groups){
  data %>%  count(!!!groups)
}

mtcars %>% wanted(groups = vars(mpg,disp,hp))

# A tibble: 31 x 4
     mpg  disp    hp     n
   <dbl> <dbl> <dbl> <int>
 1  10.4  460    215     1
 2  10.4  472    205     1
 3  13.3  350    245     1
 4  14.3  360    245     1
 5  14.7  440    230     1
 6  15    301    335     1
 7  15.2  276.   180     1
 8  15.2  304    150     1
 9  15.5  318    150     1
10  15.8  351    264     1
# … with 21 more rows
ulfelder
  • 5,305
  • 1
  • 22
  • 40
Bruno
  • 4,109
  • 1
  • 9
  • 27
0

The triple bang operator and parse_quos from the rlang package will do the trick. For more info, see e.g. https://stackoverflow.com/a/49941635/6086135

library(dplyr)

library(magrittr)

iris$group2 <- rep(1:5, 30)


vec <- c("Species", "group2")


wanted <- function(data, groups){
  data %>%  count(!!!rlang::parse_quos(groups, rlang::current_env()))
}

wanted(iris, vec)
#> # A tibble: 15 x 3
#>    Species    group2     n
#>    <fct>       <int> <int>
#>  1 setosa          1    10
#>  2 setosa          2    10
#>  3 setosa          3    10
#>  4 setosa          4    10
#>  5 setosa          5    10
#>  6 versicolor      1    10
#>  7 versicolor      2    10
#>  8 versicolor      3    10
#>  9 versicolor      4    10
#> 10 versicolor      5    10
#> 11 virginica       1    10
#> 12 virginica       2    10
#> 13 virginica       3    10
#> 14 virginica       4    10
#> 15 virginica       5    10

Created on 2020-01-06 by the reprex package (v0.3.0)

Benjamin Schwetz
  • 624
  • 5
  • 17
  • Thank you for your answer. The bang-bang-bang !!! solves the problem but I was looking for Bruno's approach as it leaves the possibility of writing regular dplyr variable with `vars(group1, group2)` – Ismail Müller Jan 06 '20 at 13:33
0

Here is another option to avoid quotations in the function call. I admit its not very pretty though.

library(tidyverse)

wanted <- function(data, groups){
  grouping <- gsub(x = rlang::quo_get_expr(enquo(groups)), pattern = "\\((.*)?\\)", replacement = "\\1")[-1]
  data %>% group_by_at(grouping) %>% count()
}


iris$group2 <- rep(1:5, 30)

wanted(iris, groups = c(Species, group2) )
#> # A tibble: 15 x 3
#> # Groups:   Species, group2 [15]
#>    Species    group2     n
#>    <fct>       <int> <int>
#>  1 setosa          1    10
#>  2 setosa          2    10
#>  3 setosa          3    10
#>  4 setosa          4    10
#>  5 setosa          5    10
#>  6 versicolor      1    10
#>  7 versicolor      2    10
#>  8 versicolor      3    10
#>  9 versicolor      4    10
#> 10 versicolor      5    10
#> 11 virginica       1    10
#> 12 virginica       2    10
#> 13 virginica       3    10
#> 14 virginica       4    10
#> 15 virginica       5    10
AndS.
  • 7,748
  • 2
  • 12
  • 17