将特定时间间隔内两个不同数据集的值合并到单个数据集(R)

将特定时间间隔内两个不同数据集的值合并到单个数据集(R),r,merge,dplyr,lubridate,stringr,R,Merge,Dplyr,Lubridate,Stringr,我有两个独立的数据集:df1和df2。我想创建一个新的数据集df3,如果日期时间彼此相差不超过20秒,它将使df1的endtime列与df2的sent列相匹配 df1 endtime ID 1/7/2020 1:35:08 AM A 1/7/2020 1:39:00 AM B 1/20/2020 1:45:00 AM C df2 sent

我有两个独立的数据集:df1和df2。我想创建一个新的数据集df3,如果日期时间彼此相差不超过20秒,它将使df1的endtime列与df2的sent列相匹配

 df1

 endtime                     ID

 1/7/2020  1:35:08 AM         A
 1/7/2020  1:39:00 AM         B
 1/20/2020 1:45:00 AM         C



 df2

sent                         ID

1/7/2020  1:35:20 AM          E
1/7/2020  1:42:00 AM          F
1/20/2020 1:55:00 AM          G
1/20/2020 2:00:00 AM          E
这是我想要的df3输出。只有一行,因为只有两个值与endtime和sent列的20秒内的条件相匹配

endtime                  sent 

1/7/2020 1:35:08 AM      1/7/2020  1:35:20 AM       
以下是dput:

df1

structure(list(endtime = structure(c(2L, 3L, 1L), .Label = c("1/10/2020 1:45:00 AM", 
"1/7/2020 1:35:08 AM", "1/7/2020 1:39:00 AM"), class = "factor"), 
ID = structure(1:3, .Label = c("A", "B", "C"), class = "factor")), class = "data.frame", row.names =   c(NA, 
 -3L))





 df2

 structure(list(sent = structure(c(3L, 4L, 1L, 2L), .Label = c("1/20/2020 1:55:00 AM", 
 "1/20/2020 2:00:00 AM", "1/7/2020 1:35:20 AM", "1/7/2020 1:42:00 AM"
 ), class = "factor"), ID = structure(c(1L, 2L, 3L, 1L), .Label = c("E", 
"F", "G"), class = "factor")), class = "data.frame", row.names = c(NA, 
-4L))
这就是我尝试过的:

我正在考虑执行左连接并匹配值,或者我可以使用merge(),但棘手的部分是将值与条件语句匹配。如有任何建议,我们将不胜感激

library(dplyr)
left_join(df1, df2)

由于没有要连接的公共列,我们可以使用
交叉
创建所有行的组合,然后
筛选符合条件的行

library(dplyr)

df1 %>%
  rename(ID1 = 'ID') %>%
  tidyr::crossing(df2) %>%
  mutate_at(vars(endtime, sent), lubridate::mdy_hms) %>%
  filter(abs(difftime(sent, endtime, 'secs')) < 20)

#  endtime             ID1   sent                ID   
#  <dttm>              <fct> <dttm>              <fct>
#1 2020-01-07 01:35:08 A     2020-01-07 01:35:20 E    
库(dplyr)
df1%>%
重命名(ID1='ID')%>%
tidyr::交叉(df2)%>%
在(vars(endtime,sent),lubridate::mdy_hms)%>%
过滤器(abs(difftime(sent,endtime,'secs'))<20)
#endtime ID1已发送ID
#                               
#12020-01-0701:35:08A2020-01-0701:35:20E

由于没有公共列可连接,我们可以使用
交叉
创建所有行组合,然后
筛选符合条件的行

library(dplyr)

df1 %>%
  rename(ID1 = 'ID') %>%
  tidyr::crossing(df2) %>%
  mutate_at(vars(endtime, sent), lubridate::mdy_hms) %>%
  filter(abs(difftime(sent, endtime, 'secs')) < 20)

#  endtime             ID1   sent                ID   
#  <dttm>              <fct> <dttm>              <fct>
#1 2020-01-07 01:35:08 A     2020-01-07 01:35:20 E    
库(dplyr)
df1%>%
重命名(ID1='ID')%>%
tidyr::交叉(df2)%>%
在(vars(endtime,sent),lubridate::mdy_hms)%>%
过滤器(abs(difftime(sent,endtime,'secs'))<20)
#endtime ID1已发送ID
#                               
#12020-01-0701:35:08A2020-01-0701:35:20E

如果数据集太大,无法生成笛卡尔乘积,也可以这样做:

df1%>%
拆分(1:NROW(%)%>%
映射(~merge(.x,
df2[abs(差分时间(df2$sent,.x$endtime,units='s'))<20,],
by=NULL))%>%
绑定_行()
编辑 TLDR

使用,它具有最佳的整体性能

dt1=as.data.table(df1)
dt2=as.data.table(df2)
dt1[,`:=`(endtime\u min=endtime-20,endtime\u max=endtime+20)]
dt1[dt2,
(ID,ID1,endtime,sent),
on=(endtime\u minsent),nomatch=0L,allow.cartesian=T]
较长版本

在数据帧太大的情况下,我给出的答案会更好,因为首先执行交叉连接生成的数据帧的行数等于两个数据帧的行数的乘积。通过先过滤后加入,避免了不必要的内存分配。但是,对于
df1
的每一行,它的开销为,检查
df2
中是否有要匹配的行

这个答案更好的另一个用例是其中一个数据帧比另一个小得多,即使它们不是那么大。我运行了一些基准测试来检查这一点

但是,在遇到OP提出的问题并在data.table中创建解决方案版本后,没有一个答案可以与此实现的性能相比

我运行的测试使用OP提供的数据集,为了模拟更大的数据集,我只需将这些数据集复制一定的次数。我做了两个测试:

  • 复制两个数据集的次数相同
  • 修复了
    df1
    和复制的
    df2
  • 对于每个测试,我测量了接受答案(
    merge\u filter
    )、我的原始答案(
    filter\u merge
    )和data.table解决方案(
    datatable
    )的平均执行时间

    在运行测试之前,我准备了
    df1
    df2
    以获得正确的数据类型,并将列
    ID
    df1
    重命名为
    ID1
    。对于data.table解决方案,我将两个数据帧转换为它们的
    data.tables
    对应项,
    dt1
    dt2

    对于每个方法,我都必须做一些更改,主要是使用
    merge(…,by=NULL)
    而不是
    crossing(…)
    ,因为最后一个方法不支持与重复行交叉连接,从结果数据集中删除所有重复行

    以下是我用来运行测试的代码:

    库(tidyverse)
    库(数据表)
    运行测试=功能(n,n1=n,n2=n){
    df1=绑定行(rep(列表(df1\u op),n1))
    df2=绑定行(rep(列表(df1\u op),n2))
    dt1=as.data.table(df1)
    dt2=as.data.table(df2)
    微基准::微基准(
    合并过滤器=df1%>%
    合并(df2,by=NULL)%>%
    过滤器(abs(difftime(sent,endtime,'secs'))<20),
    过滤器合并=df1%>%
    拆分(1:NROW(%)%>%
    映射(~merge(.x,
    df2[abs(差分时间(df2$sent,.x$endtime,units='s'))<20,],
    by=NULL))%>%
    绑定行(),
    数据表={
    dt1[,`:=`(endtime\u min=endtime-20,endtime\u max=endtime+20)]
    dt1[dt2,
    (ID,ID1,endtime,sent),
    on=(endtime\u minsent),nomatch=0L,allow.cartesian=T]
    }
    )
    }
    test_1_list=list()
    for(n在c(1,2,5,10,20,50100200500)中){
    测试列表[[toString(n)]]%
    映射(~merge(mutate(.x,k=1),
    df2%>%
    过滤器(abs(difftime(df2$sent,.x$endtime,units='s'))<20)%
    突变(k=1),
    by=“k”,
    所有.x=T)%>%
    选择(-k))%>%
    绑定_行()%>%
    选择(ID1、结束时间、ID、已发送)
    #已发送ID1结束时间ID
    #1A 2020-01-07 01:35:08 E 2020-01-07 01:35:20
    #2B2020-01-0701:39:00
    #3 C 2020-01-10 01:45:00
    <