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

如何批量生成Flag列:基于case_when跨列处理治疗数据年度变化

问题

我有一个大型数据集df,包含按国家(ISO3)、年份(Year)统计的各类治疗人数列(列名含下划线,实际名称如HIV_treatment)。需要为每个治疗列自动生成Flag_Treat_X列,标记年度变化:较上年减少为"down",增加为"up",缺失数据为NA。已掌握逐列处理方法,但因数据量大、列名易变,需要自动化方案。

简化示例数据

ISO3 <- c("AFG", "AFG","AFG", "BEN", "BEN","BEN", "GIN", "GIN", "GIN", "ZWE", "ZWE", "ZWE")
Year <- c(2020, 2021, 2022, 2020, 2021, 2022, 2020, 2021, 2022, 2020, 2021, 2022)
Treat_1 <- c(100, 110, 120, 300, 330, 360, 200, 220, 100, 300, NA, 320)

df = data.frame(ISO3, Year, Treat_1)

逐列处理示例(可行但不自动化)

df_poutcome %>%
   group_by(ISO3) %>%
   mutate(Target_art_n_pf = case_when(Treat_1 < lag(Treat_1) ~ "down",
                                      Treat_1 > lag(Treat_1) ~ "up", 
                                      TRUE ~ as.character(NA)))

尝试的循环方法(存在问题)

for (i in grep("_", names(temp), value=TRUE)){
 
  varname = ((gsub(" ", "", paste("Flag_",i))))
  temp = temp %>%
       group_by(ISO3) %>%
       mutate(!!varname:= case_when(i < lag(i) ~ "down",
                                   i > lag(i) ~ "up"))

}

问题点:新生成的Flag_[...]列全为NA值,且无法加入TRUE ~ as.character(NA)语句。

期望输出

ISO3YearTreat_1Flag_Treat_1
AFG2020100NA
AFG2021110up
AFG2022120up
BEN2020300NA
BEN2021330up
BEN2022360up
GIN2020200NA
GIN2021220up
GIN2022100down
ZWE2020300NA
ZWE2021NANA
ZWE2022320NA

解决方案

方法1:使用dplyr::across(推荐,高效简洁)

利用across批量处理符合条件的列,无需循环,代码简洁且适配大数据量:

library(dplyr)

# 筛选所有含下划线的治疗列(可根据实际需求调整筛选规则)
treat_cols <- grep("_", names(df), value = TRUE)

df_result <- df %>%
  group_by(ISO3) %>%
  mutate(
    across(
      all_of(treat_cols),
      ~ case_when(
        .x > lag(.x) ~ "up",
        .x < lag(.x) ~ "down",
        TRUE ~ NA_character_  # 处理相等、缺失等所有其他情况
      ),
      .names = "Flag_{.col}"  # 自动生成新列名,格式为Flag_原列名
    )
  ) %>%
  ungroup()  # 可选,根据后续需求决定是否取消分组

说明:

  • all_of(treat_cols)指定要处理的目标列
  • .x代表当前循环的列,lag(.x)获取该列的上一行值
  • .names = "Flag_{.col}"自动生成规范的新列名
  • NA_character_是字符型NA,比as.character(NA)更规范

方法2:修正循环方法

之前循环出错的核心原因是直接用字符变量i做比较,而非引用数据框中对应的列。改用.data[[i]]引用列即可解决:

library(dplyr)

temp <- df  # 复制原数据
treat_cols <- grep("_", names(temp), value = TRUE)

for (i in treat_cols) {
  varname <- paste0("Flag_", i)
  temp <- temp %>%
    group_by(ISO3) %>%
    mutate(
      !!varname := case_when(
        .data[[i]] > lag(.data[[i]]) ~ "up",
        .data[[i]] < lag(.data[[i]]) ~ "down",
        TRUE ~ NA_character_
      )
    ) %>%
    ungroup()
}

说明:

  • .data[[i]]用来引用数据框中名为i的列,避免把字符串i当成比较值
  • !!varname通过准引用实现动态赋值新列名
  • TRUE ~ NA_character_覆盖所有不符合up/down的场景(包括缺失值、相等值)

内容的提问来源于stack exchange,提问作者mikimouse

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 13:27:21