2016-07-23 51 views
1

我有两个数据框x和y,它们包含ids和日期的列。按ID和重叠日期范围加入数据框

id.x <- c(1, 2, 4, 5, 7, 8, 10) 
date.x <- as.Date(c("2015-01-01", "2015-01-02", "2015-01-21", "2015-01-13", "2015-01-29", "2015-01-01", "2015-01-03"),format = "%Y-%m-%d") 
x <- data.frame(id.x, date.x) 
id.y <- c(1, 2, 3, 6, 7, 8, 9) 
date.y <- as.Date(c("2015-01-03", "2015-01-29", "2015-01-22", "2015-01-13", "2015-01-29", "2014-12-31", "2015-01-03"), format = "%Y-%m-%d") 
y <- data.frame(id.y, date.y) 

我想通过匹配ID和阉date.y将它们加入到一个新的数据帧Z的内侧date.x + 3天,例如发生在date.y =“2015-01-03”上发生事件“y”,事件x在date.x =“2015-01-01”的3天内发生了事件“y”。

+0

请停止使用'cbind'来创建data.frames ..有一个data.frame函数可用于此。 – Arun

+0

@阿伦注意。如果其他人对cbind.data.frame()和data.frame()之间的区别感到好奇,可以很好地总结[here](https://docs.tibco.com/pub/enterprise-runtime- for-R/1.5.0_may_2013/TERR_1.5.0_LanguageRef/base/cbind.data.frame.html) – user6571411

+0

在调用data.frame()时使用'check.names = FALSE'(以获得' cbind.data.frame'),这似乎是唯一的默认差异。 – Arun

回答

1

您可以创建一个ifelse语句,创建一个等于date.x的向量,如果date.y < = date.x + 3和date.y> = date.x,否则等于date.y。然后合并在此基础上向量两种:

id.x <- c(1, 2, 4, 5, 7, 8, 10) 
date.x <- as.Date(c("2015-01-01", "2015-01-02", "2015-01-21", "2015-01-13", "2015-01-29", "2015-01-01", "2015-01-03"),format = "%Y-%m-%d") 
x <- cbind.data.frame(id.x, date.x) 
id.y <- c(1, 2, 3, 6, 7, 8, 9) 
date.y <- as.Date(c("2015-01-03", "2015-01-29", "2015-01-22", "2015-01-13", "2015-01-29", "2014-12-31", "2015-01-03"), format = "%Y-%m-%d") 
y <- cbind.data.frame(id.y, date.y) 

safe.ifelse <- function(cond, yes, no) structure(ifelse(cond, yes, no), class = class(yes)) 

match <- safe.ifelse(date.y <= date.x+3 & date.y >= date.x, 
      match <- date.x, 
      match <- date.y) 

y$date.x <- match 
names(y)[1] <- "id.x" 

dplyr::left_join(x, y, by=c("id.x","date.x")) 

    id.x  date.x  date.y 
1 1 2015-01-01 2015-01-03 
2 2 2015-01-02  <NA> 
3 4 2015-01-21  <NA> 
4 5 2015-01-13  <NA> 
5 7 2015-01-29 2015-01-29 
6 8 2015-01-01  <NA> 
7 10 2015-01-03  <NA> 

我从这个postsafe.ifelse功能,因为该基地ifelse语句会导致一个数字矢量,而不是最新矢量。

+0

这是不安全的,通常会导致错误的解决方案。你们这一代的'match'专栏根本不考虑'id'专栏。 – Arun

+0

我遇到了Arun提到的问题。我的工作如下 'temp < - merge(x,y,by.x =“id.x”,by.y =“id.y”,all = TRUE)' 'temp.subset < - safe.ifelse(date.y <= date.x + 3&date.y> = date.x,TRUE,FALSE)' 'joined.df < - temp [which(temp.subset == TRUE),] ' – user6571411

1

通过将键设置为两个数据表的ID,然后检查日期条件,最后提取真正的数据表,使用y和x数据表的内部连接。

library("data.table") 

x <- as.data.table(x) 

y <- as.data.table(y) 

setkey(x, id.x) 

setkey(y, id.y) 

z <- y[x, nomatch = 0][, j = .(is_true = ((date.y <= date.x + 3) & (date.y > date.x)), id.y, date.x, date.y)][i = is_true == TRUE] 

> z 
    is_true id.y  date.x  date.y 
1: TRUE 1 2015-01-01 2015-01-03 
1

使用data.table,v1.9.7,其中非球菌(或条件)联接最近实施的,我们可以在一个简单的(和效率)的方式做到这一点的开发版本。请参见安装说明here

require(data.table) # v1.9.7+ 
setDT(x) 
setDT(y) ## convert both data.frames to data.tables by reference 

x[, date.x.plus3 := date.x + 3L] 
y[x, .(id.x, date.x, date.y=x.date.y), 
    on=.(id.y == id.x, date.y >= date.x, date.y <= date.x.plus3)] 
# id.x  date.x  date.y 
# 1: 1 2015-01-01 2015-01-03 
# 2: 2 2015-01-02  <NA> 
# 3: 4 2015-01-21  <NA> 
# 4: 5 2015-01-13  <NA> 
# 5: 7 2015-01-29 2015-01-29 
# 6: 8 2015-01-01  <NA> 
# 7: 10 2015-01-03  <NA> 

解决方案,加入上一虚设列,然后筛选基于所述条件通常不可扩展(如行数迅速爆炸),并通过行的解决方案,循环和运行的每一行被过滤条件很慢,因为他们按行进行操作。

该解决方案既不执行,也就是直接执行条件连接,因此应该在运行时和内存两方面都是高性能的。