如何将tidyselect::starts_with等选择器传入dplyr函数
报错原因
你当前写法的核心问题是管道符传递逻辑错误:select(starts_with("var")) %>% ifelse(...)会把选出来的数据集作为第一个参数传入ifelse(),但ifelse()仅接收3个参数(判断条件、条件为真的返回值、条件为假的返回值),你后续传入的rowSums逻辑就变成了多余的未识别参数,直接触发报错。
另外你在mutate内部嵌套调用完整的test数据集,会脱离dplyr按行计算的上下文,即使修复参数问题也容易出现计算结果错位、长度不匹配的问题,完全不需要额外嵌套select做子集截取。
正确实现方案
if_all()和across()本身原生支持tidyselect风格的列选择规则,直接在函数内传入列选择规则即可,不需要提前截取数据集,正确代码如下:
library(tibble) library(dplyr) # 构造测试数据 tibble(var1 = c(NA,1,2), var2 = c(NA,NA,3), var3 = c(NA,0,0), do_not_touch = c(1:3) ) -> test # 计算新列 test %>% mutate( new_col = ifelse( # 判断所有var开头的列当前行是否全为NA if_all(starts_with("var"), is.na), # 全NA则返回数值型NA,和后续求和结果类型保持一致 NA_real_, # 非全NA则对var开头的列按行求和,自动忽略NA rowSums(across(starts_with("var")), na.rm = TRUE) ) )
运行上述代码将完全匹配你预期的输出结果:
# A tibble: 3 × 5 var1 var2 var3 do_not_touch new_col <dbl> <dbl> <dbl> <int> <dbl> 1 NA NA NA 1 NA 2 1 NA 0 2 1 3 2 3 0 3 5
实现要点
- 所有dplyr支持的列选择函数(包括
if_all/across/if_any等)都可以直接传入starts_with()、contains()、matches()等tidyselect选择规则,不需要提前用select()截取子集 - 数值类型的缺失值建议用
NA_real_而非通用NA,避免和rowSums返回的数值类型冲突,触发列类型不一致的警告 - mutate内部计算直接引用列名或者列选择规则即可,不需要重复传入原始数据集名,否则会破坏按行计算的上下文逻辑
内容的提问来源于stack exchange,提问作者Dr. Fabian Habersack
相关产品推荐
相关产品推荐

