dplyr 0.7.4中do()传递用户指定列名时unquote失效原因咨询
do()里unquote(!!/!!!)不起作用? 我来帮你拆解这个问题——你在dplyr 0.7.4的do()调用里用!!/!!!(unquote/unquote-splice)传递用户指定列名时失效,核心原因是这个版本的do()对tidy eval的支持逻辑和group_by()这类函数完全不一样。
先理清楚差异:为什么group_by(!!!gr)能生效?
在dplyr 0.7+版本里,group_by()已经完全适配了rlang的tidy eval机制,它会主动识别并解析quosure里的!!!符号,把你传入的分组变量列表正确展开。但do()在0.7.4版本里的NSE(非标准求值)逻辑还没跟上,它本质上是把传入的表达式当作延迟执行的代码块,不会自动去解析里面的unquote操作——也就是说,!!/!!!会被当作普通的符号,而不是“解包”指令。
解决方案:主动触发tidy eval解析
针对你的场景,有两种可靠的解决方式:
1. 用rlang::eval_tidy()在分组环境里求值
如果你只是要在do()里引用用户传入的单个列(比如你的x参数),可以用eval_tidy()主动在每个分组的数据框环境里解析sym:
sum_with_do <- function(D, x, ...) { x <- rlang::ensym(x) gr <- rlang::quos(...) D %>% group_by(!!!gr) %>% do(sum_result = sum(rlang::eval_tidy(x, data = .))) }
这里的.代表do()当前处理的分组数据框,eval_tidy()会在这个环境里找到x对应的列,完成求和计算。
2. 动态生成表达式后再传入do()
如果需要动态生成多个计算逻辑(不止一个列),可以先用rlang::expr()构建完整的表达式,再用!!!解包传入do():
sum_with_do <- function(D, x, ...) { x <- rlang::ensym(x) gr <- rlang::quos(...) # 先构建要在do里执行的表达式 do_expr <- rlang::expr(sum_result = sum(!!x)) D %>% group_by(!!!gr) %>% do(!!!do_expr) }
这种方式相当于提前完成unquote操作,再把完整的表达式传递给do(),避免它忽略unquote符号。
额外提醒
dplyr 0.7.4是比较老旧的版本了,后续的1.0+版本已经用group_map()、group_modify()等函数替代了do(),这些新函数对tidy eval的支持更完善,写法也更直观。如果你的项目允许升级dplyr,建议考虑迁移到新函数。
内容的提问来源于stack exchange,提问作者stephematician

