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

基于日期区间与变量的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)

代码说明

  1. 日期转换:将DAT转为标准日期格式,支持后续日期计算;
  2. 正则匹配:通过正则表达式快速判断DIAG是否以指定前缀开头;
  3. 分组逻辑:
    • 先标记单条记录的匹配情况,再基于分组整体判断标记类型;
    • 优先处理例外2的规则,再依次判断常规匹配、例外1的情况;
    • 根据标记类型筛选对应记录,计算最小/最大日期;
  4. 输出结果与期望格式完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 06:39:54