r - 将(函数)用户指定的列名传递给 dplyr do()



原始问题

谁能向我解释为什么取消引用在下面不起作用?

我想在调用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]])))
}

最新更新