【问题标题】:Merge nearest preceding date and between dates合并最近的前一个日期和日期之间
【发布时间】:2020-06-30 17:08:55
【问题描述】:

我有 2 个如下所示的 dfs:

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")))

Df1 有某天受试者的测试值。 Df2 包含受试者在特定时期之间的处方。

我想通过ID 合并两个dfs,如果testdate 介于df2.begindatedf2.enddate 之间,或者如果testdate 在df2 中有一个前面的“处方”,那么我想要最近的“处方”(见新 df 的第 3 行)。

最终的df应该是这样的,我也想把所有的数据都保存在df1中。

   ID   testdate   testvalue  begindate   enddate       dose
1   1   2010-03-20  17        NA          NA            NA
2   1   2018-04-12  35        2018-04-10  2018-04-22    2x per day
3   1   2018-04-25  44        2018-04-10  2018-04-22    2x per day
4   2   2011-04-17  65        2011-04-12  2011-04-30    1x morning
5   2   2011-09-05  21        2011-07-15  2011-07-30    2x morning
6   2   2019-04-16  22        2018-01-21  2018-01-29    3x per day

我试过这个:Find nearest preceding and following dates between data frames,但没有成功。我不断得到多行,不仅包含最近的“处方”,而且都来自某个测试日期之前,这不是我想要的。

编辑: 我试过这个:

setDT(df1)
setDT(df2)

setkey(df1, ID, testdate)
setkey(df2, ID, begindate)[, PrecedingDate:=begindate]

result <- df2[df1, roll=Inf]

但这不适用于新 df 中的第 3 行并调整我希望在 df 中包含的 testvalue 日期。

【问题讨论】:

  • df1 中的一个日期是“2018-04-09-25”,这显然是错误的。同样在第 3 行的输出中,begindate 不在 testdata 之前。
  • @jay.sf 我已经粘贴了代码
  • @G.Grothendieck,错误调整,确实是不是前面,我已经调整了句子

标签: r date merge


【解决方案1】:

1) 对于每一行,它会找到包含具有最大 begindate 和相同 ID 的 testvalue 的区间,如果没有,它会找到最大 begindate 不大于具有相同 ID 的 testvalue 的区间。

首先将行号seq 添加到 df1 创建临时表 df1s,然后将 df1s 中的每一行左连接到 df2 中包含其测试值且具有相同 ID 和最大开始日期的行。它还创建临时表 df1b,该表在 testdate 之前找到最大的 begindate 并且具有相同的 ID。最后,它在 seq 上加入 df1a 和 df1b,如果存在则从 df1a 获取 begindate、enddate 和 Dose,如果不存在则从 df2 获取。

library(sqldf)

sqldf("with df1s as (
  select rowid as seq, * from df1
),
df1a as (  -- nearest preceding containing interval having same ID
  select max(b.begindate) as begindate, a.*, b.begindate, b.enddate, b.Dose
  from df1s a
  left join df2 b on a.ID = b.ID and a.testdate between b.begindate and b.enddate
  group by a.seq),
df1b as (  -- nearest preceding begindate having same ID
  select max(b.begindate), a.*, b.begindate, b.enddate, b.Dose
    from df1s a 
    left join df2 b on a.ID = b.ID and b.begindate <= a.testdate
    group by a.seq)
-- pick out interval in df1a or if none in df1b
select a.ID, a.testdate, a.testvalue, 
    coalesce(a.begindate, b.begindate) as begindate,
    coalesce(a.enddate, b.enddate) as enddate,
    coalesce(a.Dose, b.Dose) as Dose
  from df1a a 
  left join df1b b on a.seq = b.seq")

使用问题中的修订数据给出以下内容:

  ID   testdate testvalue  begindate    enddate       Dose
1  1 2010-03-20        17       <NA>       <NA>       <NA>
2  1 2018-04-12        35 2018-04-10 2018-04-22 2x per day
3  1 2018-04-25        44 2018-04-10 2018-04-22 2x per day
4  2 2011-04-17        65 2011-04-12 2011-04-30 1x morning
5  2 2011-09-05        21 2011-07-15 2011-07-30 2x morning
6  2 2019-04-16        22 2018-01-21 2018-01-29 3x per day

2) 如果我们将 (1) 简化为 df1b,我们会得到一个更短的解决方案,尽管显然不等价。它只取 df2 中最大的 begindate,它不大于 df1 中的 testdate 并且具有相同的 ID。有可能它会更喜欢不跨越 testvalue 的匹配,即使存在一个间隔,如果存在一个包含 testvalue 的间隔但另一个间隔包含在该间隔中并在 testvalue 之前结束;但是,除此之外应该没问题。如果不合适,请使用 (1)。

library(sqldf)

sqldf("select a.*, max(b.begindate) as begindate, b.enddate, b.Dose
  from df1 a
  left join df2 b on a.ID = b.ID and b.begindate <= a.testdate
  group by a.rowid")

使用问题中的修订数据给出以下内容:

  ID   testdate testvalue  begindate    enddate       Dose
1  1 2010-03-20        17       <NA>       <NA>       <NA>
2  1 2018-04-12        35 2018-04-10 2018-04-22 2x per day
3  1 2018-04-25        44 2018-04-10 2018-04-22 2x per day
4  2 2011-04-17        65 2011-04-12 2011-04-30 1x morning
5  2 2011-09-05        21 2011-07-15 2011-07-30 2x morning
6  2 2019-04-16        22 2018-01-21 2018-01-29 3x per day

【讨论】:

  • 第 3 行的日期更改为 2018-4-25,这是我最初的打算。但是您的代码在更改后仍然有效,所以谢谢您!
【解决方案2】:

由于您的问题中有一些data.table 代码并链接到data.table 问题,因此这里有一个使用data.table 的选项:

#if testdate falls between df2.begindate and df2.enddate,
df1[, (cols) := 
    df2[.SD, on=.(ID, begindate<=testdate, enddate>=testdate), mget(xcols)]
]

#if testdate has a preceding "prescription" in df2, then I would like the nearest "prescription" (seen in row 3 of new df).
df1[is.na(begindate), (cols) := 
    df2[.SD, on=.(ID, enddate=testdate), roll=Inf, mget(xcols)]]

输出:

   ID   testdate testvalue  begindate    enddate       Dose
1:  1 2010-03-20        17       <NA>       <NA>       <NA>
2:  1 2018-04-12        35 2018-04-10 2018-04-22 2x per day
3:  1 2018-04-25        44 2018-04-10 2018-04-22 2x per day
4:  2 2011-04-17        65 2011-04-12 2011-04-30 1x morning
5:  2 2011-09-05        21 2011-07-15 2011-07-30 2x morning
6:  2 2019-04-16        22 2018-01-21 2018-01-29 3x per day

数据:

library(data.table)
setDT(df1)
setDT(df2)

cols <- setdiff(names(df2), "ID")
xcols <- paste0("x.", cols)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-28
    • 1970-01-01
    • 1970-01-01
    • 2018-08-11
    • 1970-01-01
    • 2017-06-29
    • 1970-01-01
    • 2014-12-12
    相关资源
    最近更新 更多