R:将字符向量传递给 dplyr::all_of() 的预期输出是什么?

R: What is the expected output of passing a character vector to dplyr::all_of()?

我试图结合使用 dplyr::all_of() 来理解 dplyr::group_by() 的预期输出。我的理解是,使用 dplyr::all_of() 应该将包含变量名的字符向量转换为裸名,以便 group_by(),但这似乎没有发生。

下面,我生成一些假数据,将不同的对象传递给 group_by() with(out) all_of() 并计算每组中的观察数。在该示例中,传递不带 dplyr::all_of() 的单个裸列名称会产生正确的输出:每个列的唯一值一行。但是,传递字符向量或使用 dplyr::all_of() 会产生不正确的输出:无论一列中的值有多少,都会产生一行。

使用 all_of 时会发生什么情况?我如何将字符向量传递给 group_by 以作为裸名向量进行处理?

library(dplyr)

# Create a 20-row data.frame with
# 2 variables each with 2 unique values.
df <- data.frame(var = rep(c("a", "b"), 10),
                 bar = rep(c(1, 2), 20))

# Output 1: 2x2 tibble - GOOD
df %>% group_by(var) %>% summarize(n = n())

# Output 2: 1x2 tibble - BAD
foo <- "var"
df %>% group_by(all_of(foo)) %>% summarize(n = n())

# Output 3: 1x2 tibble
df %>% group_by("var") %>% summarize(n = n())

# Output 4: Error in_var not found - BAD
foo2 <- list("var", "bar")
lapply(foo2, function(in_var) {
  df %>%
    group_by(in_var) %>%
    summarize(n = n())
})

# Output 5: list of length 2 where
# each element is a 1x2 tibble - BAD
foo2 <- list("var", "bar")
lapply(foo2, function(in_var) {
  df %>%
    group_by(all_of(in_var)) %>%
    summarize(n = n())
})

我们可以使用group_by_at

lapply(foo2, function(in_var) df %>% 
      group_by_at(all_of(in_var)) %>% 
      summarise(n = n()))

-输出

#[[1]]
# A tibble: 2 x 2
#  var       n
#* <chr> <int>
#1 a        20
#2 b        20

#[[2]]
# A tibble: 2 x 2
#    bar     n
#* <dbl> <int>
#1     1    20
#2     2    20

由于 across 替换了 group_by_at 的一些功能,我们可以将其与 all_of:

一起使用
lapply(foo2, function(in_var) df %>% 
      group_by(across(all_of(in_var))) %>% 
      summarise(n = n()))

或转换为 symbol 并计算 (!!)

lapply(foo2, function(in_var) df %>% 
      group_by(!! rlang::sym(in_var)) %>% 
      summarise(n = n()))

或使用map

library(purrr)
map(foo2, ~ df %>%
              group_by(!! rlang::sym(.x)) %>%
              summarise(n = n()))

或者代替group_by,可以是count

map(foo2, ~ df %>%
              count(across(all_of(.x))))
                       

添加@akrun 的多种方法来实现所需输出的答案 - 我对 all_of() 的理解是,它是选择存储为 dplyr 函数字符的变量的帮助器,并且在下面使用 vctrs。与 any_of() 相比,后者是 all_of() 的不太严格的版本和一些方便的用例。 阅读 ?tidyselect::all_off() 很有帮助。此页面也有助于跟上 dplyr 的变化和整洁的评估 https://dplyr.tidyverse.org/articles/programming.html

根据 RStudio 开发人员的决定,dplyr 范围内的动词将来会被 across 取代。请参阅 ?group_by_at() 或其他 *_if*_at*_all 文档。所以我想这真的取决于您在工作流程中使用的 dplyr 版本以及最适合您的版本。

SO post 还提供了解决方案随时间变化的上下文,将字符传递给 dplyr 函数,并且可能还有更多帖子。