原始问题
谁能向我解释为什么取消引用在下面不起作用?
我想在调用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
.我在这里包含了莱昂内尔关于使用ensym
而不是enquo
的建议,因为前者保证了x
的值是一个符号(而不是表达式)。
取消引用在这里没有用(例如其他 dplyr 示例),将上述quo_name(x)
替换为!! x
会产生以下错误:
Error in ~response : object 'response' not found
解释
根据接受的响应,根本原因是do
不会在与其他 dplyr 函数相同的环境中评估表达式(例如mutate
)使用。
我发现这从文档或源代码中都不是很清楚(例如,比较mutate
的源代码和 data.frame 的do
,如果您愿意,可以跟随 Alice 进入兔子洞),但本质上 - 这对大多数人来说可能并不是什么新鲜事;
do
计算父环境是调用环境中的表达式,并将 data.frame 的当前组(片)附加到符号.
和;- 其他DPLYR函数"或多或少"计算data.frame环境中的表达式,父级是调用环境。
另见高级R.22。根据"数据屏蔽"评估描述。
这是因为常规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]])))
}