如何使用列名向量作为dplyr :: group_by()的输入?

时间:2019-07-11 22:00:19

标签: r dplyr eval tidyeval

我想创建一个基于dplyr的函数,该函数对数据子集执行某些操作。子集由数据集中一个或多个键列的值定义。当只有一列用于标识子集时,我的代码可以正常工作:

set.seed(1)
df <- tibble(
  g1 = c(1, 1, 2, 2, 2),
  g2 = c(1, 2, 1, 2, 1),
  a = sample(5)
)
group_key <- "g1"
aggregate <- function(df, by) {
  df %>% group_by(!!sym(by)) %>% summarize(a = mean(a))
}
aggregate(df, by = group_key)

这可以按预期工作,并返回如下内容:

# A tibble: 2 x 2
     g1     a
  <dbl> <dbl>
1     1   1.5
2     2   4  

不幸的是,如果我更改group_key,一切都会崩溃:

group_key <- c("g1", "g2")
aggregate(df, by = group_key)

我收到一个错误:Only strings can be converted to symbols,我认为它来自rlang::sym()。用syms()代替它是行不通的,因为我得到了一个被group_by()扼制的名字列表。

任何建议将不胜感激!

3 个答案:

答案 0 :(得分:5)

您需要使用unquote-splice运算符!!!

aggregate <- function(df, by) {
  df %>% group_by(!!!syms(by)) %>% summarize(a = mean(a))
}

group_key <- c("g1", "g2")

aggregate(df, by = group_key)
## A tibble: 4 x 3
## Groups:   g1 [2]
#     g1    g2     a
#  <dbl> <dbl> <dbl>
#1     1     1   1  
#2     1     2   4  
#3     2     1   2.5
#4     2     2   5 

答案 1 :(得分:2)

或者,您可以使用dplyr::group_by_at

agg <- function(df, by) {
  require(dplyr)
  df %>% group_by_at(vars(one_of(by))) %>% summarize(a = mean(a))}

group_key <- "g1"
group_keys <- c("g1","g2")

agg(df, by = group_key)
#> # A tibble: 2 x 2
#>      g1     a
#>   <dbl> <dbl>
#> 1     1  2.5 
#> 2     2  3.33

agg(df, by = group_keys)
#> # A tibble: 4 x 3
#> # Groups:   g1 [2]
#>      g1    g2     a
#>   <dbl> <dbl> <dbl>
#> 1     1     1   1  
#> 2     1     2   4  
#> 3     2     1   2.5
#> 4     2     2   5

答案 2 :(得分:1)

使用 dplyr 1.0.0 更新

新的 across() 允许像 all_of 这样的 tidyselect 函数,它取代了 NSE 的 quote-unqote 过程。代码看起来更简单:

aggregate <- function(df, by) {
  df %>% 
    group_by(across(all_of(by))) %>% 
    summarize(a = mean(a))
}

df %>% aggregate(group_key)