使用filter_at时starts_with报错:需在选择函数内使用的问题排查
解决
filter_at里用starts_with()报错的问题 这个坑我之前也踩过,确实是你用的dplyr(0.8.4)和tidyselect(1.0.0)版本组合导致的兼容问题。tidyselect 1.0.0开始对选择器的使用场景做了严格限制,starts_with()这类选择器被限定只能在选择型函数(比如select())里用,而旧版的filter_at的vars()参数并不属于这个场景,所以就触发了报错。
给你几个靠谱的解决办法,按需选择就行:
办法1:用matches()替换starts_with()
matches()支持正则表达式匹配列名,完全能实现starts_with("all")的效果,而且在filter_at里可以正常工作:
filter_at(df, vars(matches("^all")), any_vars(. == "Y"))
这里^all是正则表达式的写法,意思就是匹配以"all"开头的列名,和starts_with("all")的逻辑完全一致。
办法2:先获取目标列名再传入vars()
先通过select()把符合条件的列名提取出来,再用one_of()将列名传给filter_at:
# 先获取以all开头的列名 target_cols <- names(select(df, starts_with("all"))) # 执行过滤逻辑 filter_at(df, vars(one_of(target_cols)), any_vars(. == "Y"))
one_of()可以接受列名字符向量,在vars()中是合法用法,完美绕开选择器的场景限制。
办法3:升级dplyr到新版本(推荐)
如果允许升级包的话,建议把dplyr升级到1.0.0及以上版本。新版dplyr中,虽然filter_at这类“范围动词”被标记为软弃用,但引入了更灵活的across()和if_any()函数,写法更直观:
# 使用across的写法(dplyr 1.0.0+可用) df %>% filter(across(starts_with("all"), ~ .x == "Y", .fns = any)) # 更简洁的if_any写法(dplyr 1.0.5+可用) df %>% filter(if_any(starts_with("all"), ~ .x == "Y"))
if_any()专门用来实现“任意一列满足条件则保留该行”的逻辑,代码可读性比旧版filter_at高很多。
用你的测试数据验证效果
拿你提供的数据集运行办法1的代码,能得到正确结果:
id <- c("john","paul","george","ringo","mick","dozy","beaky","tich") allow_a <- c("Y","N","Y","Y","","","Y","") allow_b <- c("N","","N","","N","N","N","Y") generic_a <- c("N","","","Y","Y","Y","N","") generic_b <- c("N","","","N","Y","N","","Y") df <- data.frame(id, allow_a, allow_b, generic_a, generic_b) library(dplyr) filter_at(df, vars(matches("^all")), any_vars(. == "Y"))
输出结果:
id allow_a allow_b generic_a generic_b 1 john Y N N N 2 george Y N N 3 ringo Y Y N 4 beaky Y N N
内容的提问来源于stack exchange,提问作者TimL
相关产品推荐
相关产品推荐

