基于多规则结合across与starts_with为R数据框生成新变量及排错
问题描述
我有如下代表10个个体响应的dataframe,包含Yes、No、Inc、Vag四种响应类型:
data_test = data.frame(Val_Av=c("Yes", "No", "Inc", "Yes", "No", "Yes", "No", "Inc", "Vag", "Yes"), Val_Am=c("No", "No", "No", "Inc", "No", "Yes", "Yes", "Inc", "Vag", NA), Val_ZM=c(NA, NA, NA, "Yes", "No", NA, "No", "Inc", "Vag", "Yes"), Val_FC=c("No", "No", "No", NA, "No", "Yes", "Yes", "Yes", "Inc", "No"), Val_CL=c("Yes", "No", "Inc", "Yes", "No", "Yes", "No", NA, NA, "Yes"))
数据预览:
Val_Av Val_Am Val_ZM Val_FC Val_CL 1 Yes No <NA> No Yes 2 No No <NA> No No 3 Inc No <NA> No Inc 4 Yes Inc Yes <NA> Yes 5 No No No No No 6 Yes Yes <NA> Yes Yes 7 No Yes No Yes No 8 Inc Inc Inc Yes <NA> 9 Vag Vag Vag Inc <NA> 10 Yes <NA> Yes No Yes
我希望遵循以下规则生成一个汇总响应的新变量:
- 若所有非NA变量的响应一致,则写入该响应(例如:第2行→No,第5行→No,第6行→Yes)
- 若响应中无Yes,则拼接所有唯一非NA值(例如:第3行→Inc;No,第9行→Vag;Inc)
- 若响应中存在Yes,且:
- Yes的数量严格多于其他响应,则写入Yes(例如:第10行→Yes,第4行→Yes) - Yes的数量与其他响应相等,则写入"Dif"(例如:第1行→Dif) - Yes的数量严格少于其他响应,则拼接所有唯一非NA值(例如:第8行→Inc;Yes,第7行→No;Yes)
我的实际数据中存在其他无需考虑的变量,因此希望使用across和starts_with("Val_")函数实现。但自己尝试的代码无法正常运行,恳请提供思路。
我的尝试代码
data_test %>% mutate(Val= across(starts_with('Val_'), function(x) case_when( length(unique(x[!is.na(x)])) == 1 ~ unique(x[!is.na(x)]), all(x != "Yes", na.rm = TRUE) ~ paste(unique(x[!is.na(x)]), collapse = ";"), any(x == "Yes", na.rm = TRUE) & sum(x == "Yes", na.rm = TRUE) == sum(x != "Yes", na.rm = TRUE) ~ "Dif", any(x == "Yes", na.rm = TRUE) & sum(x == "Yes", na.rm = TRUE) > sum(x != "Yes", na.rm = TRUE) ~ "Yes", any(x == "Yes", na.rm = TRUE) & sum(x == "Yes", na.rm=TRUE) < sum(x !="Yes", na.rm=TRUE) ~ paste(unique(x[!is.na(x)]), collapse = ";") ) ) )
解决方案
你的代码核心问题是误用了across的作用场景:across是用来对多列分别执行相同操作,而你需要的是对每一行的Val_开头列做跨行的汇总计算,应该用rowwise()配合c_across()来实现,而不是直接在mutate里嵌套across。
下面是符合要求的代码:
library(dplyr) data_test %>% rowwise() %>% mutate( # 提取当前行所有非NA的Val_开头列的值 val_list = list(c_across(starts_with("Val_"))[!is.na(c_across(starts_with("Val_")))]), # 计算Yes的数量和其他响应的数量 yes_count = sum(val_list == "Yes"), other_count = length(val_list) - yes_count, # 根据规则生成汇总值 Val_Summary = case_when( # 所有非NA值一致 length(unique(val_list)) == 1 ~ unique(val_list), # 无Yes的情况 yes_count == 0 ~ paste(sort(unique(val_list)), collapse = ";"), # 有Yes的分支 yes_count > other_count ~ "Yes", yes_count == other_count ~ "Dif", yes_count < other_count ~ paste(sort(unique(val_list)), collapse = ";") ) ) %>% # 移除中间变量(可选) select(-val_list, -yes_count, -other_count)
代码说明
rowwise():将数据框转换为按行分组的模式,确保后续计算都是基于单行进行。c_across(starts_with("Val_")):提取当前行所有以Val_开头的列,组合成一个向量,再过滤掉NA值得到val_list。- 计数逻辑:分别统计
Yes的数量和其他响应的总数量,简化后续条件判断。 case_when顺序:按照规则优先级排序,先判断所有值一致的情况,再判断无Yes的情况,最后处理有Yes的分支,确保逻辑覆盖完整。
运行后得到的Val_Summary列结果如下:
Val_Av Val_Am Val_ZM Val_FC Val_CL Val_Summary 1 Yes No <NA> No Yes Dif 2 No No <NA> No No No 3 Inc No <NA> No Inc Inc;No 4 Yes Inc Yes <NA> Yes Yes 5 No No No No No No 6 Yes Yes <NA> Yes Yes Yes 7 No Yes No Yes No No;Yes 8 Inc Inc Inc Yes <NA> Inc;Yes 9 Vag Vag Vag Inc <NA> Inc;Vag 10 Yes <NA> Yes No Yes Yes
内容的提问来源于stack exchange,提问作者Av65
相关产品推荐
相关产品推荐

