使用 +/- 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
中最接近 df1
的 Date 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>
嗨,我有两个数据框。
都有 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
中最接近 df1
的 Date 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>