如何避免省略号......在dplyr?

How to avoid ellipsis ... in dplyr?

我想创建一个接受分组参数的函数。其中可以是单个或多个变量。我希望它看起来像这样:

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

这仅在给定单个组时有效,但在有多个组时中断。我知道可以将以下内容与省略号 ... 一起使用(但我想要语法 groups = something):

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

完整代码如下:

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

rlang 包中的三重爆炸运算符和 parse_quos 可以解决问题。有关详细信息,请参见例如

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

reprex package (v0.3.0)

于 2020-01-06 创建

这是避免在函数调用中使用引号的另一种方法。虽然我承认它不是很漂亮。

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

你们把事情复杂化了,这很好用:

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