基于日期区间与变量的data.table条件生成新列求助
复杂R数据处理问题(优先使用data.table实现)
输入数据
A <- c("AA10", "AA20", "AA30") B <- c("BB10","BB20","BB30") C <- c("CC", "DD") library(data.table) DF <- data.table::data.table( ID = c(1L,1L,1L,1L,1L,1L,2L,2L,2L, 2L,3L,3L,3L,3L,4L,4L,4L,5L,5L,5L), DIAG = c("AA10","AA100","AA20","BB10", "BB1000","D05","AA10","AA105","BB10","BB101","AA10", "BB2020","BB2020","BB20","BB10","AA20","AA200","AA10", "AA205","AA305"), DAT = c("2001-01-01","2002-02-02", "2003-03-03","2004-04-04","2010-10-10","2011-11-11", "2002-02-02","2003-03-03","2004-04-04","2005-05-05","2001-01-01", "2002-02-02","2003-03-03","2004-04-04","2002-02-02", "2003-03-03","2009-09-09","2001-01-01","2002-02-02", "2003-03-03"), OP_DIAG = c(NA,NA,NA,NA,NA,NA,NA,NA,NA, NA,NA,NA,NA,"CC10",NA,NA,NA,NA,NA,NA) )
期望输出
ID DIAG DAT OP_DIAG ABAB_MIN_DAT ABAB_MAX_DAT A_DIAG AB_DIAG B_DIAG 1: 1 AA10 2001-01-01 <NA> 2001-01-01 2010-10-10 <NA> <NA> YES 2: 1 AA100 2002-02-02 <NA> 2001-01-01 2010-10-10 <NA> <NA> YES 3: 1 AA20 2003-03-03 <NA> 2001-01-01 2010-10-10 <NA> <NA> YES 4: 1 BB10 2004-04-04 <NA> 2001-01-01 2010-10-10 <NA> <NA> YES 5: 1 BB1000 2010-10-10 <NA> 2001-01-01 2010-10-10 <NA> <NA> YES 6: 1 D05 2011-11-11 <NA> 2001-01-01 2010-10-10 <NA> <NA> YES 7: 2 AA10 2002-02-02 <NA> 2002-02-02 2005-05-05 <NA> YES <NA> 8: 2 AA105 2003-03-03 <NA> 2002-02-02 2005-05-05 <NA> YES <NA> 9: 2 BB10 2004-04-04 <NA> 2002-02-02 2005-05-05 <NA> YES <NA> 10: 2 BB101 2005-05-05 <NA> 2002-02-02 2005-05-05 <NA> YES <NA> 11: 3 AA10 2001-01-01 <NA> 2001-01-01 2004-04-04 YES <NA> <NA> 12: 3 BB2020 2002-02-02 <NA> 2001-01-01 2004-04-04 YES <NA> <NA> 13: 3 BB2020 2003-03-03 <NA> 2001-01-01 2004-04-04 YES <NA> <NA> 14: 3 BB20 2004-04-04 CC10 2001-01-01 2004-04-04 YES <NA> <NA> 15: 4 BB10 2002-02-02 <NA> 2002-02-02 2009-09-09 YES <NA> <NA> 16: 4 AA20 2003-03-03 <NA> 2003-03-03 2009-09-09 YES <NA> <NA> 17: 4 AA200 2009-09-09 <NA> 2009-09-09 2009-09-09 YES <NA> <NA> 18: 5 AA10 2001-01-01 <NA> 2001-01-01 2003-03-03 YES <NA> <NA> 19: 5 AA205 2002-02-02 <NA> 2001-01-01 2003-03-03 YES <NA> <NA> 20: 5 AA305 2003-03-03 <NA> 2001-01-01 2003-03-03 YES <NA> <NA>
处理逻辑
所有数据按ID分组处理:
1. A_DIAG、B_DIAG、AB_DIAG生成规则
- 检查同
ID下的DIAG字段是否以A或B中的值开头(例如AA205以AA20开头则视为匹配A); - 仅匹配
A不匹配B:设A_DIAG=YES;仅匹配B不匹配A:设B_DIAG=YES;同时匹配则设AB_DIAG=YES; - 例外1:若同时匹配
A和B,但最近5年(从该ID下最大DAT日期算起)内仅匹配A或B,则设对应A_DIAG/B_DIAG=YES,而非AB_DIAG; - 例外2:若
OP_DIAG以C中的CC开头,且同ID下DIAG匹配A或B,则始终设A_DIAG=YES;
2. ABAB_MIN_DAT、ABAB_MAX_DAT生成规则
- 分别为对应
A_DIAG/B_DIAG匹配记录的最小和最大DAT日期。
解决方案(data.table实现)
# 转换DAT为日期格式 DF[, DAT := as.Date(DAT)] # 生成匹配开头的正则表达式 pattern_A <- paste0("^(", paste(A, collapse = "|"), ")") pattern_B <- paste0("^(", paste(B, collapse = "|"), ")") pattern_C <- "^CC" # 按ID分组处理 result <- DF[, { # 标记每条记录是否匹配A、B match_A <- grepl(pattern_A, DIAG) match_B <- grepl(pattern_B, DIAG) # 检查是否有OP_DIAG以CC开头 has_CC_OP <- any(grepl(pattern_C, OP_DIAG, na.rm = TRUE)) # 计算分组最大日期及5年前的日期 max_dat <- max(DAT, na.rm = TRUE) five_years_ago <- max_dat - 365*5 # 判断最近5年的匹配情况 recent_A <- any(match_A[DAT >= five_years_ago]) recent_B <- any(match_B[DAT >= five_years_ago]) # 确定分组标记类型 if (has_CC_OP && (any(match_A) || any(match_B))) { group_type <- "A" } else if (any(match_A) && !any(match_B)) { group_type <- "A" } else if (!any(match_A) && any(match_B)) { group_type <- "B" } else if (any(match_A) && any(match_B)) { if (recent_A && !recent_B) { group_type <- "A" } else if (!recent_A && recent_B) { group_type <- "B" } else { group_type <- "AB" } } else { group_type <- NA } # 计算对应匹配记录的最小/最大日期 if (group_type == "A") { min_dat <- min(DAT[match_A], na.rm = TRUE) max_dat_group <- max(DAT[match_A], na.rm = TRUE) } else if (group_type == "B") { min_dat <- min(DAT[match_B], na.rm = TRUE) max_dat_group <- max(DAT[match_B], na.rm = TRUE) } else if (group_type == "AB") { min_dat <- min(DAT[match_A | match_B], na.rm = TRUE) max_dat_group <- max(DAT[match_A | match_B], na.rm = TRUE) } else { min_dat <- NA max_dat_group <- NA } # 生成标记列 A_DIAG <- ifelse(group_type == "A", "YES", NA) AB_DIAG <- ifelse(group_type == "AB", "YES", NA) B_DIAG <- ifelse(group_type == "B", "YES", NA) # 返回结果 .(DIAG = DIAG, DAT = DAT, OP_DIAG = OP_DIAG, ABAB_MIN_DAT = min_dat, ABAB_MAX_DAT = max_dat_group, A_DIAG = A_DIAG, AB_DIAG = AB_DIAG, B_DIAG = B_DIAG) }, by = ID] # 查看结果 print(result)
代码说明
- 日期转换:将
DAT转为标准日期格式,支持后续日期计算; - 正则匹配:通过正则表达式快速判断
DIAG是否以指定前缀开头; - 分组逻辑:
- 先标记单条记录的匹配情况,再基于分组整体判断标记类型;
- 优先处理例外2的规则,再依次判断常规匹配、例外1的情况;
- 根据标记类型筛选对应记录,计算最小/最大日期;
- 输出结果与期望格式完全一致。
内容的提问来源于stack exchange,提问作者hklovs
相关产品推荐
相关产品推荐

