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

R语言如何按3个月内最近日期全连接两个dataframe且避免行重复

两个DataFrame按时间窗口匹配全连接解决方案

核心需求

  • 仅当两表同ID对应的日期差值不超过3个月时,按最近日期完成匹配关联
  • 执行全连接操作,必须完整保留df1和df2的所有行,无匹配的字段留空
  • 若3个月时间窗口内存在多个可选匹配行,禁止重复复制另一张表的行,保证每行仅匹配一次

预期输出示例

ID  Date.df1     Date.df2   V1_df1  V2_df1  V1_df2   V2_df2
100    NA       07/11/2015    NA      NA      93.3    93.3
100 01/11/2015  03/11/2015    9.3    10.6     93.3    95.5
100 23/12/2016  27/12/2016     8     10.3     97.78   97.78
100 04/11/2017  13/11/2017    9.3     11      98.9    98.89
100 09/11/2018      NA       10.3    9.6       NA     NA
101 07/11/2015  07/11/2015     7     6.6      97.78   97.78
101 21/01/2017  19/12/2016     6     7.3      95.7    95.5
101 18/11/2017      NA        7.6    6.6       NA     NA
101 22/01/2019      NA        6.5     7        NA     NA
102 27/09/2017  26/08/2017     5      7       94.8    94.8
102 01/10/2018      NA        8.6    7.3       NA      NA
102 15/09/2019      NA         9      8        NA      NA
103     NA      14/11/2015     NA     NA      97.7     97

原有方案问题分析

dplyr版本问题

原有代码先全连接后直接过滤符合3个月时间差的行,直接把日期差超过3个月、本该留空匹配的行删掉了;且仅按df1的日期分组去重,没有处理df2的未匹配行,最终导致两边都有行丢失。
原有代码:

df1 %>%
  full_join(df2, by = "ID") %>%
  mutate(diff = abs(Date.df2 - Date.df1)) %>%
  filter(
    (!is.na(Date.df2) &
      (is.na(Date.df1) | (diff < months(3)))) |
    (!is.na(Date.df1) &
      (is.na(Date.df2) | (diff < months(3))))  
  ) %>%
  arrange(ID, Date.df1, Date.df2) %>%
  group_by(ID, Date.df1) %>%
  filter(row_number() == 1) %>%
  ungroup()

data.table版本问题

原有滚动连接仅做了右连接,没有保留df1中无匹配的行,且未设置滚动匹配的最大时间范围,导致超出3个月的日期也被强制匹配,同时没有去重逻辑,出现重复匹配的问题。
原有代码:

library(data.table)

# 转为data.table并添加连接用日期列,保留原始日期字段
setDT(df1)[, join_date := Date.df1]
setDT(df2)[, join_date := Date.df2]

# 按ID和日期滚动近邻匹配
df1[df2, on = .(ID, join_date), roll = "nearest"] [order(ID, join_date)]

修复后的实现代码

dplyr修复版

library(dplyr)
library(lubridate)

# 1. 给df1和df2添加唯一行号,避免重复匹配
df1 <- df1 %>% mutate(row_id1 = row_number())
df2 <- df2 %>% mutate(row_id2 = row_number())

# 2. 先匹配df1的所有行,每个df1行仅匹配最近的符合3个月窗口的df2行
match_df1 <- df1 %>%
  left_join(df2, by = "ID") %>%
  mutate(diff = abs(Date.df2 - Date.df1)) %>%
  filter(is.na(diff) | diff < months(3)) %>%
  arrange(ID, row_id1, diff) %>%
  group_by(ID, row_id1) %>%
  slice_head(n = 1) %>%
  ungroup()

# 3. 提取已经被匹配的df2行号,剩下的df2行单独补充进来
used_df2_rows <- unique(match_df1$row_id2)
unmatch_df2 <- df2 %>%
  filter(!row_id2 %in% used_df2_rows) %>%
  mutate(
    row_id1 = NA_integer_,
    Date.df1 = as.Date(NA),
    V1_df1 = NA_real_,
    V2_df1 = NA_real_
  )

# 4. 合并两部分结果,排序输出
final_res <- bind_rows(match_df1, unmatch_df2) %>%
  arrange(ID, Date.df1, Date.df2) %>%
  select(ID, Date.df1, Date.df2, V1_df1, V2_df1, V1_df2, V2_df2)

data.table修复版

library(data.table)
library(lubridate)

setDT(df1)
setDT(df2)

# 1. 添加行号
df1[, row_id1 := .I]
df2[, row_id2 := .I]

# 2. 滚动匹配,限制3个月窗口
res1 <- df2[df1, on = .(ID, Date.df2 = Date.df1), roll = "nearest", nomatch = NA]
res1[, diff := abs(Date.df2 - i.Date.df1)]
res1[diff >= months(3), c("Date.df2", "V1_df2", "V2_df2", "row_id2") := .(as.Date(NA), NA_real_, NA_real_, NA_integer_)]
setnames(res1, "i.Date.df1", "Date.df1")

# 3. 补充未匹配的df2行
used_row2 <- unique(res1$row_id2)
res2 <- df2[!row_id2 %in% used_row2]
res2[, c("Date.df1", "V1_df1", "V2_df1", "row_id1") := .(as.Date(NA), NA_real_, NA_real_, NA_integer_)]

# 4. 合并输出
final_res <- rbind(res1, res2, fill = TRUE)
setorder(final_res, ID, Date.df1, Date.df2)
final_res <- final_res[, .(ID, Date.df1, Date.df2, V1_df1, V2_df1, V1_df2, V2_df2)]

示例数据集

df1 <- structure(list(ID = c(100L, 100L, 100L, 100L, 101L, 101L, 101L, 
101L, 102L, 102L, 102L), Date.df1 = structure(c(16740, 17158, 
17474, 17844, 16746, 17187, 17488, 17918, 17436, 17805, 18154
), class = "Date"), V1_df1 = c(9.3, 8, 9.3, 10.3, 7, 6, 7.6, 
6.5, 5, 8.6, 9), V2_df1 = c(10.6, 10.3, 11, 9.6, 6.6, 7.3, 6.6, 
7, 7, 7.3, 8)), row.names = c(NA, -11L), class = "data.frame")

df2 <- structure(list(ID = c(100L, 100L, 100L, 100L, 101L, 101L, 102L, 
103L), Date.df2 = structure(c(16742, 16746, 17162, 17483, 16746, 
17154, 17404, 16753), class = "Date"), V1_df2 = c(93.3, 93.3, 
97.78, 98.9, 97.78, 95.7, 94.8, 97.7), V2_df2 = c(95.5, 93.3, 
97.78, 98.89, 97.78, 95.5, 94.8, 97)), row.names = c(NA, -8L), class = "data.frame")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 08:18:01