运行Hadley的dplyr NSE编程示例输出异常,求原因解析
dplyr非标准计算(NSE)示例运行不一致/报错的原因解析
问题背景
学习dplyr的非标准计算(NSE)时,参考旧版教程的示例运行结果与预期不符或报错,具体情况如下:
示例1:!!/UQ优先级问题
教程中的示例及预期输出:
my_fun <- quo(fun) quo(!!my_fun(x, y, z)) #> Error in (function (x) : could not find function "my_fun" quo(UQ(my_fun)(x, y, z)) #> ~(~fun)(x, y, z)
实际运行输出:
my_fun <- quo(fun) quo(!!my_fun(x, y, z)) # Error in my_fun(x, y, z) : could not find function "my_fun" quo(UQ(my_fun)(x, y, z)) # <quosure> # expr: ^`~`(fun)(x, y, z) # env: global
示例2:!!!非引号拼接报错
教程中的示例及预期输出:
args <- list(mean = ~mean(cyl), count = ~n()) mtcars %>% group_by(am) %>% summarise(!!! args) #> # A tibble: 2 × 3 #> am mean count #> <dbl> <dbl> <int> #> 1 0 6.947368 19 #> 2 1 5.076923 13
实际运行报错:
Error in `summarise()`: ℹ In argument: `mean = ~mean(cyl)`. ℹ In group 1: `am = 0`. Caused by error: ! `mean` must be a vector, not a <formula> object. Run `rlang::last_trace()` to see where the error occurred.
改用quo替代公式语法后可正常运行:
args <- list(mean = quo(mean(cyl)), count = quo(n())) mtcars %>% group_by(am) %>% summarise(!!! args) #> # A tibble: 2 × 3 #> am mean count #> <dbl> <dbl> <int> #> 1 0 6.947368 19 #> 2 1 5.076923 13
会话信息:
R version 4.3.3 (2024-02-29 ucrt) Platform: x86_64-w64-mingw32/x64 (64-bit) Running under: Windows Server 2019 x64 (build 19045) attached base packages: [1] stats graphics grDevices utils datasets methods base other attached packages: [1] dplyr_1.1.4
原因解析
核心原因:dplyr版本迭代导致NSE实现变更
你参考的教程基于旧版dplyr(0.7.x及更早版本),而当前使用的是dplyr 1.1.4(1.0+新版),两者在非标准计算的处理逻辑上有显著差异:
示例1:quosure输出格式变化
- 旧版中
quo()的输出以~开头表示表达式,新版则使用<quosure>结构化输出,明确标注expr(表达式)和env(环境),这是dplyr依赖的rlang包输出格式更新,本质表达式逻辑一致(~fun(x,y,z)和^~(fun)(x,y,z)等价),仅显示形式不同。 - 报错信息的差异是版本更新后的提示优化,核心错误原因一致:
!!优先级问题导致!!my_fun(x,y,z)被解析为!!(my_fun(x,y,z))而非(!!my_fun)(x,y,z),my_fun不是函数因此报错。
- 旧版中
示例2:公式对象不再支持直接传入
summarise()- 旧版dplyr允许将公式(
~)作为参数传入summarise()等函数,内部会自动解析公式右侧的表达式;但新版dplyr(1.0+)严格要求传入参数为可计算的表达式或向量,不再自动处理公式对象,因此直接传入~mean(cyl)会报错。 - 使用
quo()包装表达式后,!!!可正确拼接quosure对象,这是新版dplyr推荐的非标准计算写法,符合当前rlang的整洁评估规范。
- 旧版dplyr允许将公式(
补充说明
新版dplyr(1.0+)已逐渐转向使用{{ }}、across()等更简洁的整洁评估接口替代旧版!!/!!!写法,建议优先学习当前版本的官方文档,避免版本差异导致的问题。
内容的提问来源于stack exchange,提问作者msm1089
相关产品推荐
相关产品推荐

