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

时间:2018-04-19 05:03:19

标签: r dplyr tidyeval

原始问题

有人可以向我解释为什么unquote在以下情况下不起作用吗?

我希望在do版本0.7.4中调用dplyr时传递一个(函数)用户指定的列名。这似乎比使用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函数'或多或少'评估data.frame环境中的表达式,其中parent是调用环境。

有关“数据屏蔽”的说明,另请参阅Advanced R. 22. Evaluation

1 个答案:

答案 0 :(得分:1)

这是因为常规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]])))
}