将(函数)用户指定的列名传递给 dplyr do()
Passing (function) user-specified column name to dplyr do()
原问题
谁能给我解释一下为什么 unquote 在下面不起作用?
我想在 dplyr
的 0.7.4 版本中调用 do
时传递一个(函数)用户指定的列名。与使用 do_
的旧标准评估方法相比,这似乎没有那么尴尬。一个基本的(成功的)例子 忽略了在这里使用 do
是非常不必要的事实 会是这样的:
sum_with_do <- function(D, x, ...) {
x <- rlang::ensym(x)
gr <- quos(...)
D %>%
group_by(!!! gr) %>%
do(data.frame(y=sum(.[[quo_name(x)]])))
}
D <- data.frame(group=c('A','A','B'), response=c(1,2,3))
sum_with_do(D, response, group)
# A tibble: 2 x 2
# Groups: group [2]
group y
<fct> <dbl>
1 A 3.
2 B 3.
从 dplyr 0.7.5 开始,rlang::
是不必要的,现在导出 ensym
。我在这里包含了 lionel 关于使用 ensym
而不是 enquo
的建议,因为前者保证 x
的值是一个符号(而不是表达式)。
取消引用在这里没有用(例如其他dplyr examples),将上面的quo_name(x)
替换为!! x
会产生以下错误:
Error in ~response : object 'response' not found
说明
根据接受的回复,根本原因是 do
没有在其他 dplyr 函数(例如 mutate
)使用的相同环境中计算表达式。
我没有发现文档或源代码中的这一点非常清楚(例如,比较 mutate
and do
for data.frames 的源代码,如果你愿意,可以跟随爱丽丝进入兔子洞),但本质上 - 这对大多数人来说可能不是什么新鲜事;
do
在父环境为调用环境的环境中计算表达式,并将 data.frame 的当前组(切片)附加到符号 .
,并且;
- 其他 dplyr 函数 'more or less' 评估 data.frame 环境中的表达式,父级是调用环境。
另请参阅 Advanced R. 22. Evaluation 以了解 'data masking' 的描述。
这是因为常规 do()
语义,其中除了 .
:
之外没有数据屏蔽
do(df, data.frame(y = sum(.$response)))
#> y
#> 1 6
do(df, data.frame(y = sum(.[[response]])))
#> Error: object 'response' not found
因此您只需要将裸列名称捕获为字符串即可,无需取消引号,因为没有数据屏蔽:
sum_with_do <- function(df, x, ...) {
# ensym() guarantees that `x` is a simple column name and not a
# complex expression:
x <- as.character(ensym(x))
df %>%
group_by(...) %>%
do(data.frame(y = sum(.[[x]])))
}
原问题
谁能给我解释一下为什么 unquote 在下面不起作用?
我想在 dplyr
的 0.7.4 版本中调用 do
时传递一个(函数)用户指定的列名。与使用 do_
的旧标准评估方法相比,这似乎没有那么尴尬。一个基本的(成功的)例子 忽略了在这里使用 do
是非常不必要的事实 会是这样的:
sum_with_do <- function(D, x, ...) {
x <- rlang::ensym(x)
gr <- quos(...)
D %>%
group_by(!!! gr) %>%
do(data.frame(y=sum(.[[quo_name(x)]])))
}
D <- data.frame(group=c('A','A','B'), response=c(1,2,3))
sum_with_do(D, response, group)
# A tibble: 2 x 2
# Groups: group [2]
group y
<fct> <dbl>
1 A 3.
2 B 3.
从 dplyr 0.7.5 开始,rlang::
是不必要的,现在导出 ensym
。我在这里包含了 lionel 关于使用 ensym
而不是 enquo
的建议,因为前者保证 x
的值是一个符号(而不是表达式)。
取消引用在这里没有用(例如其他dplyr examples),将上面的quo_name(x)
替换为!! x
会产生以下错误:
Error in ~response : object 'response' not found
说明
根据接受的回复,根本原因是 do
没有在其他 dplyr 函数(例如 mutate
)使用的相同环境中计算表达式。
我没有发现文档或源代码中的这一点非常清楚(例如,比较 mutate
and do
for data.frames 的源代码,如果你愿意,可以跟随爱丽丝进入兔子洞),但本质上 - 这对大多数人来说可能不是什么新鲜事;
do
在父环境为调用环境的环境中计算表达式,并将 data.frame 的当前组(切片)附加到符号.
,并且;- 其他 dplyr 函数 'more or less' 评估 data.frame 环境中的表达式,父级是调用环境。
另请参阅 Advanced R. 22. Evaluation 以了解 'data masking' 的描述。
这是因为常规 do()
语义,其中除了 .
:
do(df, data.frame(y = sum(.$response)))
#> y
#> 1 6
do(df, data.frame(y = sum(.[[response]])))
#> Error: object 'response' not found
因此您只需要将裸列名称捕获为字符串即可,无需取消引号,因为没有数据屏蔽:
sum_with_do <- function(df, x, ...) {
# ensym() guarantees that `x` is a simple column name and not a
# complex expression:
x <- as.character(ensym(x))
df %>%
group_by(...) %>%
do(data.frame(y = sum(.[[x]])))
}