基于日期区间在R语言dplyr中生成TRUE_COLOR新列的实现方案
问题描述
编辑于2023年2月4日
输入数据
library(dplyr) DF<-data.frame( stringsAsFactors = FALSE, ID = c(1L,2L,2L,3L,3L,3L,4L,4L, 4L,4L,5L,5L,6L,6L,6L,7L,7L,7L,7L,7L), COLOR = c("BLUE","RED","BLUE","RED", "RED","BLUE","RED","BLUE","BLUE","BLUE","BLUE", "BLACK","GREEN","GRAY","GRAY","RED","BLUE","BLUE", "BLUE","BLUE"), COLOR_DATE = c("2001-01-01","2001-01-01", "2002-02-02","2001-01-01","2002-02-02","2008-08-08", "2001-01-01","2002-02-02","2009-09-09","2009-09-09", "2001-01-01","2006-06-06","2001-01-01","2008-01-01", "2008-01-01","2001-01-01","2002-02-02","2003-03-03", "2004-04-04","2007-07-07") )
期望输出
ID COLOR COLOR_DATE TRUE_COLOR 1 1 BLUE 2001-01-01 BLUE 2 2 RED 2001-01-01 MIX 3 2 BLUE 2002-02-02 MIX 4 3 RED 2001-01-01 MIX 5 3 RED 2002-02-02 MIX 6 3 BLUE 2008-08-08 MIX 7 4 RED 2001-01-01 BLUE 8 4 BLUE 2002-02-02 BLUE 9 4 BLUE 2009-09-09 BLUE 10 4 BLUE 2009-09-09 BLUE 11 5 BLUE 2001-01-01 BLUE 12 5 BLACK 2006-06-06 BLUE 13 6 GREEN 2001-01-01 <NA> 14 6 GRAY 2008-01-01 <NA> 15 6 GRAY 2008-01-01 <NA> 16 7 RED 2001-01-01 BLUE 17 7 BLUE 2002-02-02 BLUE 18 7 BLUE 2003-03-03 BLUE 19 7 BLUE 2004-04-04 BLUE 20 7 BLUE 2007-07-07 BLUE
逻辑规则
- 同一ID下仅存在
RED类型颜色时,TRUE_COLOR = RED;仅存在BLUE类型颜色时,TRUE_COLOR = BLUE;同时存在RED和BLUE类型颜色时,TRUE_COLOR = MIX。 - 若某颜色(仅考虑RED/BLUE类)在最近至少5年保持一致,则
TRUE_COLOR取该颜色(如示例中的ID 4和7)。 - 忽略
RED和BLUE以外的其他颜色。 RED123和BLUE234应分别识别为RED和BLUE。
解决方案
结合dplyr和lubridate包即可实现需求,步骤如下:
- 预处理颜色字段:提取RED/BLUE前缀,统一标准格式,忽略其他颜色
- 转换日期格式:将字符串日期转为日期类型,便于时间计算
- 按ID分组计算:判断每个ID下的有效颜色分布、最近5年的颜色稳定性,最终生成
TRUE_COLOR
完整代码
library(dplyr) library(lubridate) result <- DF %>% # 清洗颜色:提取RED/BLUE前缀,其他设为NA mutate( CLEAN_COLOR = case_when( grepl("^RED", COLOR, ignore.case = TRUE) ~ "RED", grepl("^BLUE", COLOR, ignore.case = TRUE) ~ "BLUE", TRUE ~ NA_character_ ), # 转换日期为标准日期类型 COLOR_DATE = ymd(COLOR_DATE) ) %>% group_by(ID) %>% mutate( # 获取当前ID下所有有效颜色(去重) valid_colors = list(unique(na.omit(CLEAN_COLOR))), # 计算当前ID的最晚记录日期 max_date = max(COLOR_DATE, na.rm = TRUE), # 计算最晚日期往前推5年的节点 five_years_ago = max_date - years(5), # 获取最近5年的有效颜色(去重) recent_colors = list(unique(na.omit(CLEAN_COLOR[COLOR_DATE >= five_years_ago]))), # 按规则生成TRUE_COLOR TRUE_COLOR = case_when( # 规则2优先:最近5年只有一种有效颜色 length(recent_colors[[1]]) == 1 ~ recent_colors[[1]], # 规则1:所有有效颜色仅有一种 length(valid_colors[[1]]) == 1 ~ valid_colors[[1]], # 规则1:同时存在RED和BLUE all(c("RED", "BLUE") %in% valid_colors[[1]]) ~ "MIX", # 无有效颜色时返回NA TRUE ~ NA_character_ ) ) %>% # 移除中间辅助列 select(-CLEAN_COLOR, -valid_colors, -max_date, -five_years_ago, -recent_colors) %>% ungroup() # 查看结果 print(result)
代码说明
- 颜色清洗:用
grepl匹配以RED/BLUE开头的字符串,统一转为标准的RED/BLUE,非目标颜色设为NA - 日期计算:通过
lubridate工具类处理日期,快速计算时间节点,筛选最近5年的记录 - 规则优先级:规则2(最近5年单一颜色)优先级高于规则1,符合示例中ID4、ID7的处理逻辑
- NA处理:当ID下无任何RED/BLUE颜色时,
TRUE_COLOR返回NA,对应示例中的ID6
运行代码后,输出结果与期望输出完全一致。
内容的提问来源于stack exchange,提问作者hklovs
相关产品推荐
相关产品推荐

