用dplyr的case_when/if_any跨多变量处理月度数据并标记首次超标月份
问题需求
现有数据集的变量按月度命名(示例:var1_M1、var1_M2、var2_M1等),需完成以下操作:
- 对所有月度变量执行条件处理:值大于90则保留原值,否则设为0;
- 按月份对处理后的变量值进行汇总;
- 识别每个ID首次出现值大于90的月度,并标记对应的月份编号;
- 最终生成如预期输出所示的数据集。
现有代码
library(dplyr) ID <- c("Dave", "Joe", "Steve") var1_M1 <- c(10, 10, 90) var1_M2 <- c(30, 90, 95) var1_M3 <- c(90, 100, 95) var2_M1 <- c(10, 90, 20) var2_M2 <- c(33, 10, 100) var2_M3 <- c(90, 10, 50) data <- tibble(ID, var1_M1, var1_M2, var1_M3, var2_M1, var2_M2, var2_M3) # 原数据集预览 # A tibble: 3 x 7 # ID var1_M1 var1_M2 var1_M3 var2_M1 var2_M2 var2_M3 # <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #1 Dave 10 30 90 10 33 90 #2 Joe 10 90 100 90 10 10 #3 Steve 90 95 95 20 100 50 data %>% mutate_at(vars(matches(c("M1","M2","M3"))), list(~ ifelse(. > 90, .,0))) #%>% #mutate() using map_dfc and if_any ??
预期输出
# A tibble: 3 x 5 # ID M1 M2 M3 output # <chr> <dbl> <dbl> <dbl> <dbl> #1 Dave 0 0 0 0 #2 Joe 0 0 100 3 #3 Steve 0 195 95 2
解决方案
使用dplyr结合tidyr的宽转长/长转宽操作,逻辑更清晰直观:
library(dplyr) library(tidyr) data %>% # 宽表转长表,拆分变量名与月份标识 pivot_longer(cols = -ID, names_to = c("var", "month"), names_pattern = "(.*)_M(\\d+)") %>% # 执行条件处理:值>90保留,否则设为0 mutate(processed_val = ifelse(value > 90, value, 0)) %>% # 按ID+月份汇总处理后的值 group_by(ID, month) %>% summarize(total = sum(processed_val), .groups = "drop") %>% # 转回宽表,生成M1/M2/M3列 pivot_wider(names_from = month, values_from = total, names_prefix = "M") %>% # 计算首次出现有效数值的月份编号 rowwise() %>% mutate(output = { vals = c(M1, M2, M3) first_valid = which(vals > 0)[1] ifelse(is.na(first_valid), 0, as.integer(first_valid)) }) %>% ungroup()
运行上述代码即可得到与预期一致的数据集。
内容的提问来源于stack exchange,提问作者user20168262
相关产品推荐
相关产品推荐

