通过带有不带引号的元素的显式参数指定要分组的多个变量
Specifying multiple variables to group by via explicit argument with unquoted elements
根据 Programming with dplyr 中关于 捕获多个参数 的部分,我试图指定
多个变量分组 dplyr::group_by
不依赖...
而是使用显式列表参数group_vars
无需引用 arg 中的列表元素 group_vars
示例数据
df <- tibble::tribble(
~a, ~b, ~c,
"A", "a", 10,
"A", "a", 20,
"A", "b", 1000,
"B", "a", 5,
"B", "b", 1
)
中的 ...
的方法
# Approach 1 -----
my_summarise <- function(df, ...) {
group_vars <- dplyr::enquos(...)
df %>%
dplyr::group_by(!!!group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise(df, a, b)
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
基于带引号元素的列表参数的方法:
# Approach 2 -----
my_summarise_2 <- function(df, group_vars = c("a", "b")) {
group_vars <- dplyr::syms(group_vars)
df %>%
dplyr::group_by(!!!group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise_2(df)
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
my_summarise_2(df, group_vars = "a")
#> # A tibble: 2 x 2
#> a x
#> <chr> <dbl>
#> 1 A 343.
#> 2 B 3
我找不到让我提供不带引号的列名的方法:
# Approach 3 -----
my_summarise_3 <- function(df, group_vars = list(a, b)) {
group_vars <- dplyr::enquos(group_vars)
df %>%
dplyr::group_by(!!!group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise_3(df)
#> Error: Column `list(a, b)` must be length 5 (the number of rows) or one, not 2
我想关键是最终得到一个与
调用 group_vars <- dplyr::enquos(...)
:
之后的一个
<list_of<quosure>>
[[1]]
<quosure>
expr: ^a
env: global
[[2]]
<quosure>
expr: ^b
env: global
我试图用 group_vars %>% purrr::map(dplyr::enquo)
来解决它,但当然 R 会抱怨 a
和 b
,因为它们需要评估。
主要问题是 list(a, b)
不捕获未计算的表达式 a
和 b
,而是计算这些表达式并创建包含结果的 two-element 列表。您基本上有两个选择:
解决方法一:使用rlang::exprs()
捕捉实际的表情。由于表达式已经未计算,您不再需要在函数中使用 enquos
,它只是变成了
my_summarise_3 <- function(df, group_vars = rlang::exprs(a, b)) {
df %>%
dplyr::group_by(!!!group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise_3(df)
# # A tibble: 4 x 3
# # Groups: a [2]
# a b x
# <chr> <chr> <dbl>
# 1 A a 15
# 2 A b 1000
# 3 B a 5
# 4 B b 1
此界面的缺点是用户现在负责引用(即捕获表达式)参数:
# Note that it can be done using quote() from base R
my_summarise_3(df, group_vars=quote(a))
# # A tibble: 2 x 2
# a x
# <chr> <dbl>
# 1 A 343.
# 2 B 3
解决方案二:完整捕获未计算的表达式list(a,b)
并手动解析它。
## Helper function to recursively construct an abstract syntax tree
getAST <- function( ee ) { as.list(ee) %>% map_if(is.call, getAST) }
my_summarise_3 <- function(df, group_vars = list(a,b)) {
## Capture the expression and parse it
ast <- rlang::enexpr(group_vars) %>% getAST()
## Identify symbols present in the data
gvars <- unlist(ast) %>% map_chr(deparse) %>%
intersect(names(df)) %>% rlang::syms()
df %>%
dplyr::group_by(!!!gvars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise_3(df, list(a,b))
# # A tibble: 4 x 3
# # Groups: a [2]
# a b x
# <chr> <chr> <dbl>
# 1 A a 15
# 2 A b 1000
# 3 B a 5
# 4 B b 1
my_summarise_3(df, b)
# # A tibble: 2 x 2
# b x
# <chr> <dbl>
# 1 a 11.7
# 2 b 500.
我认为你只是想重新发明 vars()
:
library(magrittr)
library(dplyr,warn.conflicts = FALSE)
#> Warning: package 'dplyr' was built under R version 3.6.1
df <- tibble::tribble(
~a, ~b, ~c,
"A", "a", 10,
"A", "a", 20,
"A", "b", 1000,
"B", "a", 5,
"B", "b", 1
)
my_summarise <- function(data, group_vars) {
data %>%
group_by_at(group_vars) %>%
summarise(x = mean(c))
}
my_summarise(df, c("a","b"))
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
my_summarise(df, vars(a, b))
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
由 reprex package (v0.3.0)
于 2019-07-26 创建
如果你真的想要这个,这里有一个@Artem 解决方案的变体(但为什么?):
my_summarise <- function(df, group_vars) {
quoted_group_vars <- rlang::list2(
!!!as.list(enexpr(group_vars)[-1]))
df %>%
dplyr::group_by(!!!quoted_group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise(df, list(a, b))
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
根据 Programming with dplyr 中关于 捕获多个参数 的部分,我试图指定
多个变量分组
dplyr::group_by
不依赖
...
而是使用显式列表参数group_vars
无需引用 arg 中的列表元素
group_vars
示例数据
df <- tibble::tribble(
~a, ~b, ~c,
"A", "a", 10,
"A", "a", 20,
"A", "b", 1000,
"B", "a", 5,
"B", "b", 1
)
中的 ...
的方法
# Approach 1 -----
my_summarise <- function(df, ...) {
group_vars <- dplyr::enquos(...)
df %>%
dplyr::group_by(!!!group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise(df, a, b)
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
基于带引号元素的列表参数的方法:
# Approach 2 -----
my_summarise_2 <- function(df, group_vars = c("a", "b")) {
group_vars <- dplyr::syms(group_vars)
df %>%
dplyr::group_by(!!!group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise_2(df)
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
my_summarise_2(df, group_vars = "a")
#> # A tibble: 2 x 2
#> a x
#> <chr> <dbl>
#> 1 A 343.
#> 2 B 3
我找不到让我提供不带引号的列名的方法:
# Approach 3 -----
my_summarise_3 <- function(df, group_vars = list(a, b)) {
group_vars <- dplyr::enquos(group_vars)
df %>%
dplyr::group_by(!!!group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise_3(df)
#> Error: Column `list(a, b)` must be length 5 (the number of rows) or one, not 2
我想关键是最终得到一个与
调用 group_vars <- dplyr::enquos(...)
:
<list_of<quosure>>
[[1]]
<quosure>
expr: ^a
env: global
[[2]]
<quosure>
expr: ^b
env: global
我试图用 group_vars %>% purrr::map(dplyr::enquo)
来解决它,但当然 R 会抱怨 a
和 b
,因为它们需要评估。
主要问题是 list(a, b)
不捕获未计算的表达式 a
和 b
,而是计算这些表达式并创建包含结果的 two-element 列表。您基本上有两个选择:
解决方法一:使用rlang::exprs()
捕捉实际的表情。由于表达式已经未计算,您不再需要在函数中使用 enquos
,它只是变成了
my_summarise_3 <- function(df, group_vars = rlang::exprs(a, b)) {
df %>%
dplyr::group_by(!!!group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise_3(df)
# # A tibble: 4 x 3
# # Groups: a [2]
# a b x
# <chr> <chr> <dbl>
# 1 A a 15
# 2 A b 1000
# 3 B a 5
# 4 B b 1
此界面的缺点是用户现在负责引用(即捕获表达式)参数:
# Note that it can be done using quote() from base R
my_summarise_3(df, group_vars=quote(a))
# # A tibble: 2 x 2
# a x
# <chr> <dbl>
# 1 A 343.
# 2 B 3
解决方案二:完整捕获未计算的表达式list(a,b)
并手动解析它。
## Helper function to recursively construct an abstract syntax tree
getAST <- function( ee ) { as.list(ee) %>% map_if(is.call, getAST) }
my_summarise_3 <- function(df, group_vars = list(a,b)) {
## Capture the expression and parse it
ast <- rlang::enexpr(group_vars) %>% getAST()
## Identify symbols present in the data
gvars <- unlist(ast) %>% map_chr(deparse) %>%
intersect(names(df)) %>% rlang::syms()
df %>%
dplyr::group_by(!!!gvars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise_3(df, list(a,b))
# # A tibble: 4 x 3
# # Groups: a [2]
# a b x
# <chr> <chr> <dbl>
# 1 A a 15
# 2 A b 1000
# 3 B a 5
# 4 B b 1
my_summarise_3(df, b)
# # A tibble: 2 x 2
# b x
# <chr> <dbl>
# 1 a 11.7
# 2 b 500.
我认为你只是想重新发明 vars()
:
library(magrittr)
library(dplyr,warn.conflicts = FALSE)
#> Warning: package 'dplyr' was built under R version 3.6.1
df <- tibble::tribble(
~a, ~b, ~c,
"A", "a", 10,
"A", "a", 20,
"A", "b", 1000,
"B", "a", 5,
"B", "b", 1
)
my_summarise <- function(data, group_vars) {
data %>%
group_by_at(group_vars) %>%
summarise(x = mean(c))
}
my_summarise(df, c("a","b"))
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
my_summarise(df, vars(a, b))
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1
由 reprex package (v0.3.0)
于 2019-07-26 创建如果你真的想要这个,这里有一个@Artem 解决方案的变体(但为什么?):
my_summarise <- function(df, group_vars) {
quoted_group_vars <- rlang::list2(
!!!as.list(enexpr(group_vars)[-1]))
df %>%
dplyr::group_by(!!!quoted_group_vars) %>%
dplyr::summarise(x = mean(c))
}
my_summarise(df, list(a, b))
#> # A tibble: 4 x 3
#> # Groups: a [2]
#> a b x
#> <chr> <chr> <dbl>
#> 1 A a 15
#> 2 A b 1000
#> 3 B a 5
#> 4 B b 1