如何批量生成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)语句。
期望输出
| ISO3 | Year | Treat_1 | Flag_Treat_1 |
|---|---|---|---|
| AFG | 2020 | 100 | NA |
| AFG | 2021 | 110 | up |
| AFG | 2022 | 120 | up |
| BEN | 2020 | 300 | NA |
| BEN | 2021 | 330 | up |
| BEN | 2022 | 360 | up |
| GIN | 2020 | 200 | NA |
| GIN | 2021 | 220 | up |
| GIN | 2022 | 100 | down |
| ZWE | 2020 | 300 | NA |
| ZWE | 2021 | NA | NA |
| ZWE | 2022 | 320 | NA |
解决方案
方法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
相关产品推荐
相关产品推荐

