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

按ID合并数据框:匹配区间内或最近前置处方记录

解决R数据框按ID+日期匹配处方的问题

我懂你现在的痛点:要把测试记录和处方信息关联起来,既要匹配日期落在处方区间内的情况,还要处理测试日期之前最近的处方,而且得完整保留原始测试数据的每一行——之前用data.table的滚动连接没搞定第三行,还改了testdate,确实不对头。

先把你的原始数据再贴一遍,方便参考:

df1 <- data.frame(
  ID = c("1", "1", "1", "2", "2", "2"),
  testdate = as.POSIXct(c("2010-3-20", "2018-04-12","2018-04-25","2011-04-17","2011-09-05","2019-04-16")),
  testvalue = c(17, 35, 44, 65, 21, 22)
)

df2 <- data.frame(
  ID = c("1", "1", "2", "2", "2"),
  begindate = as.POSIXct(c("2018-04-10","2018-04-30","2011-04-12","2011-07-15","2018-01-21")),
  enddate = as.POSIXct(c("2018-04-22","2018-05-12","2011-04-30","2011-07-30","2018-01-29")),
  Dose = c("2x per day", "1x per day", "1x morning", "2x morning", "3x per day")
)

需求拆解

咱们的核心需求可以拆成两点:

  • 对每个testdate,优先匹配同ID下testdate落在begindate和enddate之间的处方
  • 如果没有符合区间的,就找同ID下begindate早于等于testdate的所有处方里,begindate最近的那一个
  • 必须完整保留df1的所有原始行,包括testdate的原始值

解决方案1:用dplyr + fuzzyjoin(直观易读)

这个组合能先处理区间匹配,再处理最近前置处方的情况,逻辑清晰:

library(dplyr)
library(fuzzyjoin)

# 第一步:先匹配所有testdate落在处方区间内的记录
interval_matches <- df1 %>%
  fuzzy_inner_join(
    df2,
    by = c("ID" = "ID", "testdate" = "begindate", "testdate" = "enddate"),
    match_fun = list(`==`, `>=`, `<=`)
  ) %>%
  select(ID = ID.x, testdate, testvalue, begindate, enddate, Dose)

# 第二步:找出df1中没有区间匹配的行,再找最近的前置处方
non_interval_rows <- df1 %>%
  anti_join(interval_matches, by = c("ID", "testdate")) %>%
  group_by(ID, testdate, testvalue) %>%
  mutate(
    # 筛选同ID下begindate <= testdate的处方,取begindate最大的那个
    matched_prescription = list(
      df2 %>%
        filter(ID == cur_data()$ID, begindate <= cur_data()$testdate) %>%
        slice_max(begindate, n = 1, with_ties = FALSE)
    )
  ) %>%
  unnest(matched_prescription, keep_empty = TRUE)

# 第三步:合并两种情况的结果,得到最终数据集
final_result <- bind_rows(interval_matches, non_interval_rows) %>%
  arrange(ID, testdate)

运行后得到的final_result完全符合需求:

  • 第1行(ID=1,testdate=2010-03-20):没有任何前置处方,处方列显示NA
  • 第2行(ID=1,testdate=2018-04-12):落在第一个处方区间内,匹配到2x per day
  • 第3行(ID=1,testdate=2018-04-25):无区间内处方,匹配到最近的前置处方(2018-04-10的记录)
  • 所有行都保留了df1的原始testdate值

解决方案2:改进data.table方法(高效处理大数据)

如果你习惯用data.table,可以修正之前的逻辑,先处理区间匹配,再用滚动连接补全剩余行:

library(data.table)

setDT(df1)
setDT(df2)

# 先做区间匹配,用i.testdate保留原始测试日期
interval_matches <- df2[df1, 
                        on = .(ID = ID, begindate <= testdate, enddate >= testdate),
                        .(ID, testdate = i.testdate, testvalue, begindate, enddate, Dose)]

# 找出没有匹配的行,用滚动连接找最近的前置处方
non_interval_rows <- df1[!interval_matches, on = .(ID, testdate)]
non_interval_matches <- df2[non_interval_rows, 
                           on = .(ID = ID, begindate <= testdate),
                           roll = Inf,
                           .(ID, testdate = i.testdate, testvalue, begindate, enddate, Dose)]

# 合并结果并排序
final_result_dt <- rbind(interval_matches, non_interval_matches) %>%
  setorder(ID, testdate)

这个方法和dplyr的结果完全一致,而且适合处理大规模数据集,关键是通过i.testdate避免了原始testdate被替换的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 23:07:46