You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于多规则结合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)

代码说明

  1. rowwise():将数据框转换为按行分组的模式,确保后续计算都是基于单行进行。
  2. c_across(starts_with("Val_")):提取当前行所有以Val_开头的列,组合成一个向量,再过滤掉NA值得到val_list。
  3. 计数逻辑:分别统计Yes的数量和其他响应的总数量,简化后续条件判断。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 00:03:21