使用 +/- 3 天按日期连接两个数据框

Join two data frames by date using +/- 3 days

嗨,我有两个数据框。

都有 ID 变量和日期变量。我想按 ID 和日期加入,但日期可以通过正负 3 天来加入。第一个数据帧的顺序需要保持不变。这两个数据框的长度不同,并非所有日期或 ID 都对齐。 ID 也可以有 2 个或更多条目作为不同日期的不同集合。

希望这是有道理的。

第一个数据框 -

structure(list(ID = c(1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 5, 6, 6, 
6, 6, 7), `Date Collected` = structure(c(18903, 18936, 18976, 
18539, 18508, 18581, 18655, 17688, 17975, 18037, 18297, 18081, 
18242, 18338, 18721, 18128), class = "Date")), row.names = c(NA, 
-16L), class = c("tbl_df", "tbl", "data.frame"))

第二个数据框-

structure(list(ID = c(1, 1, 1, 1, 3, 3, 3, 4, 4, 4, 5), `Date Relapse` = structure(c(18900, 
18938, 18973, 19004, 18511, 18578, 18657, 17322, 17685, 18129, 
18300), class = "Date")), row.names = c(NA, -11L), class = c("tbl_df", 
"tbl", "data.frame"))

这是一个 tidyverse 解决方案。

group_modify 采用分组的小标题并将函数应用于每个组。该函数应该有两个参数。第一个提供当前组中的行(不包括定义组的列,第二个是单列 tibble,每一列定义组的一列。该 tibble 的一行中的值包含 定义当前组的值

由于您尚未定义如何解决关系,我只是选择了 df2 中最接近 df1Date Collected 的行。如果 df2 中的两行与 Date Collected 等距,则采用较早的行。

# Modify to allow validation
df2 <- df2 %>% 
         mutate(Row2=row_number())

df1 %>% 
  group_by(ID, `Date Collected`) %>% 
  group_modify(
    function(.x, .y) {
      t <- df2 %>% 
             filter(ID == .y$ID) %>% 
             add_column(Delta=abs(as.numeric(.y$`Date Collected` - .$`Date Relapse`))) %>% 
             arrange(Delta) %>% 
             filter(Delta <= 3) %>% 
             head(1) %>% 
             select(-ID)
      if (t %>% nrow() > 0) {
        .x %>% bind_cols(t)
      } else {
        .x
      }
    }
  )
# A tibble: 16 × 5
# Groups:   ID, Date Collected [16]
      ID `Date Collected` `Date Relapse`  Row2 Delta
   <dbl> <date>           <date>         <int> <dbl>
 1     1 2021-10-03       2021-09-30         1     3
 2     1 2021-11-05       2021-11-07         2     2
 3     1 2021-12-15       2021-12-12         3     3
 4     2 2020-10-04       NA                NA    NA
 5     3 2020-09-03       2020-09-06         5     3
 6     3 2020-11-15       2020-11-12         6     3
 7     3 2021-01-28       2021-01-30         7     2
 8     4 2018-06-06       2018-06-03         9     3
 9     5 2019-03-20       NA                NA    NA
10     5 2019-05-21       NA                NA    NA
11     5 2020-02-05       2020-02-08        11     3
12     6 2019-07-04       NA                NA    NA
13     6 2019-12-12       NA                NA    NA
14     6 2020-03-17       NA                NA    NA
15     6 2021-04-04       NA                NA    NA
16     7 2019-08-20       NA                NA    NA

有一个更简单的方法——使用non-equi joins

library(data.table)
setDT(df2)[,`:=`(d_lower = `Date Relapse`-3, d_upper = `Date Relapse`+3)]

df2[
  i=setDT(df1),
  on = .(ID, d_lower<=`Date Collected`, d_upper>=`Date Collected`),
  j=.(ID, "Date Collected"=d_lower, `Date Relapse`)
]

如果您喜欢dplyr,只需加入ID,然后根据需要进行过滤

left_join(df1,inner_join(df1,df2, by="ID") %>%
  filter(abs(`Date Collected`-`Date Relapse`)<=3),
  by=c("ID", "Date Collected")
)

无论哪种方式,输出:

    ID Date Collected Date Relapse
 1:  1     2021-10-03   2021-09-30
 2:  1     2021-11-05   2021-11-07
 3:  1     2021-12-15   2021-12-12
 4:  2     2020-10-04         <NA>
 5:  3     2020-09-03   2020-09-06
 6:  3     2020-11-15   2020-11-12
 7:  3     2021-01-28   2021-01-30
 8:  4     2018-06-06   2018-06-03
 9:  5     2019-03-20         <NA>
10:  5     2019-05-21         <NA>
11:  5     2020-02-05   2020-02-08
12:  6     2019-07-04         <NA>
13:  6     2019-12-12         <NA>
14:  6     2020-03-17         <NA>
15:  6     2021-04-04         <NA>
16:  7     2019-08-20         <NA>