R语言匹配多列ZSD字符串并比较关联.test列取值的实现问题
需求说明
需要实现跨多列的字符串检测与关联列的值比较逻辑:若列中检测到字符串ZSD,则需对比其对应的关联列取值。
输入数据
输入数据框结构示例如下:
a.zsd a.test b.zsd b.test c.zsd c.test d.zsd d.test 'ZSD' 0.0 'ZAD' 1.0 NA 0.5 'ZAD' 1.0 'ZAD' 1.0 NA 0.0 NA 0.5 'ZSD' 0.0 NA 0.5 NA 0.5 'ZAD' 0.5 NA 0.5 'Not Achieved ZSD' 0.0 NA 0.5 'ZAD' 0.5 NA 0.5 'ZSD' 1.0 'ZSD' 0.5 NA 0.5 'ZSD' 0.0 NA 0.0 NA 0.0 NA 0.5 NA 0.0 NA 1.0 'ZSD' 0.0 'ZSD' 0.5 'ZSD' 1.0
数据框有上百列,仅需处理列名以.zsd结尾的字段:这类字段取值仅可为NA、ZAD、ZSD、Not Achieved ZSD,且每个.zsd列都对应一个同名前缀的.test列。
输出要求
需要在原数据框中新增两列smallest.test和zsd.level,规则如下:
- 遍历所有列名以
.zsd结尾的字段 - 在上述列中检测字符串
ZSD - 若仅1个
.zsd列检测到ZSD,zsd.level返回该列前缀名(如a.zsd返回a),smallest.test返回其对应的.test列取值 - 若无任何
.zsd列检测到ZSD,zsd.level返回NA,smallest.test返回0.0 - 若多个
.zsd列检测到ZSD,选取对应.test列取值最小的列,返回对应前缀和test值 - 若多个符合条件的列
.test取值相等,选取排序最靠后的列返回对应结果
示例输出
a.zsd a.test b.zsd b.test c.zsd c.test d.zsd d.test smallest.test zsd.level 'ZSD' 0.0 'ZAD' 1.0 NA 0.5 'ZAD' 1.0 0.0 a 'ZAD' 1.0 NA 0.0 NA 0.5 'ZSD' 0.0 0.0 d NA 0.5 NA 0.5 'ZAD' 0.5 NA 0.5 0.0 NA 'Not Achieved ZSD' 0.0 NA 0.5 'ZAD' 0.5 NA 0.5 0.0 a 'ZSD' 1.0 'ZSD' 0.5 NA 0.5 'ZSD' 0.0 0.0 d NA 0.0 NA 0.0 NA 0.5 NA 0.0 0.0 NA NA 1.0 'ZSD' 0.0 'ZSD' 0.5 'ZSD' 1.0 0.0 b
示例数据dput
dput(df) structure(list(a.zsd = c("ZSD", "ZAD", NA, "Not Achieved ZSD", "ZSD", NA, NA), a.test = c(0, 1, 0.5, 0, 1, 0, 1), b.zsd = c("ZAD", NA, NA, NA, "ZSD", NA, "ZSD"), b.test = c(1, 0, 0.5, 0.5, 0.5, 0, 0), c.zsd = c(NA, NA, "ZAD", "ZAD", NA, NA, "ZSD"), c.test = c(0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5), d.zsd = c("ZAD", "ZSD", NA, NA, "ZSD", NA, "ZSD"), d.test = c(1, 0, 0.5, 0.5, 0, 0, 1)), class = "data.frame", row.names = c(NA, -7L))
现有实现问题
已有的代码仅能遍历选择.zsd列并返回列名,无法实现关联.test字段的取值比较逻辑,完整实现方案如下:
完整实现代码
library(dplyr) library(stringr) # 提取所有.zsd列的前缀 prefixes <- str_remove(names(df)[endsWith(names(df), ".zsd")], ".zsd") df_result <- df %>% rowwise() %>% mutate( # 收集当前行所有含ZSD的zsd列对应的前缀、test值、列排序 match_list = list( lapply(prefixes, function(p) { zsd_val <- get(paste0(p, ".zsd")) if (!is.na(zsd_val) && str_detect(zsd_val, "ZSD")) { data.frame( prefix = p, test_val = get(paste0(p, ".test")), col_order = which(prefixes == p) ) } else { NULL } }) %>% do.call(rbind, .) ), # 计算smallest.test smallest.test = ifelse(is.null(match_list), 0.0, min(match_list$test_val)), # 计算zsd.level zsd.level = if(is.null(match_list)) { NA_character_ } else { # 先按test值升序,再按列排序降序取第一个,符合test相同取最后列的规则 match_list %>% arrange(test_val, desc(col_order)) %>% slice(1) %>% pull(prefix) } ) %>% ungroup() %>% select(-match_list)
内容的提问来源于stack exchange,提问作者Sandy
相关产品推荐
相关产品推荐

