如何使用dplyr::filter()按动态列名列表筛选数据
用dplyr筛选所有指定后缀列值全为0的行
问题描述
需要遍历列名存在差异的数据框,筛选出所有以met结尾的列值均为0的行,尝试使用dplyr::filter()结合动态列名时出现错误。
示例数据框列名包括:pfall_met、cfall_met、fall_met、spring_met、csprin_met、pspring_met等任意以met结尾的列。
之前尝试的错误代码:
mets <- c(names(data)[grep("met", names(data))]) data_filt <- data %>% filter(paste0(mets) == 0) # 尝试用select风格的选择器失败 data_filt <- data %>% filter(ends_with("met") == 0) # 用sym处理向量报错 filter((!!sym(mets)) == 0)
错误信息:
Error in `sym()`: ! Can't convert a character vector to a symbol. Run `rlang::last_error()` to see where the error occurred.
解决方案
方法1:直接用across() + ends_with()选择器
filter()本身不支持ends_with()这类列选择器,需要结合across()来遍历目标列,再判断所有列是否满足值为0的条件:
library(dplyr) # 构造示例数据 data <- tibble( pfall_met = c(0, 0, 1, 0), cfall_met = c(0, 1, 0, 0), fall_met = c(0, 0, 0, 0), spring_met = c(0, 0, 0, 1), temp = c(20, 25, 18, 22) # 非目标列 ) # 筛选所有以'met'结尾的列值均为0的行 data_filt <- data %>% filter(across(ends_with("met"), ~ .x == 0))
across(ends_with("met"))会选中所有后缀为met的列,~ .x == 0对每列的每个值做等于0的判断,filter()会自动要求所有列的判断结果都为TRUE(逻辑与),最终保留所有目标列值全为0的行。
方法2:结合动态列名向量
如果已经提前获取了目标列名列表,可使用all_of()将字符向量传入across():
# 准确获取所有以'met'结尾的列名(用$确保匹配后缀而非包含) mets <- names(data)[grepl("met$", names(data))] data_filt <- data %>% filter(across(all_of(mets), ~ .x == 0))
all_of()用于明确指定传入的字符向量是列名,避免dplyr的语法歧义。
错误原因说明
paste0(mets) == 0:将列名向量拼接成字符串后与0比较,完全没有对数据框的列值进行判断,逻辑错误。sym(mets):sym()仅能处理单个字符(转成符号),无法处理字符向量,需用syms()配合!!!展开,但这种写法远不如across()简洁直观。
内容的提问来源于stack exchange,提问作者ac_wildlife
相关产品推荐
相关产品推荐

