按ID和重叠日期范围加入数据框

时间:2016-07-23 14:23:18

标签: r date dataframe

我有两个数据框x和y,其中包含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和wether date将它们加入到新的数据帧z中。在date.x + 3天内发生,例如个人“1”在date.y =“2015-01-03”上发生事件“y”,该事件发生在日期x =“2015-01-01”的事件x的3天内。

3 个答案:

答案 0 :(得分:2)

最近实现了data.table的开发版本v1.9.7,其中非equi (或条件)连接,我们可以用简单(高效)的方式做到这一点..请参阅安装说明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>

连接虚拟列然后根据条件进行过滤的解决方案通常不可扩展(因为行数快速爆炸),循环遍历并为每行运行过滤条件的解决方案很慢,嗯,因为他们按行执行操作。

这个解决方案既不会,也不会直接执行条件连接,因此在运行时和内存方面都应该具有高性能。

答案 1 :(得分:1)

如果date.y&lt; = date.x + 3和date.y&gt; = date.x并且等于date.y,则可以创建一个ifelse语句,该语句创建一个等于date.x的向量。然后根据这个向量合并两个:

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>

我从这个post借用了 safe.ifelse 函数,因为基本ifelse语句导致数字向量而不是日期向量。

答案 2 :(得分:1)

使用y和x数据表的内连接,方法是将键设置为两个数据表的id,然后检查日期条件,最后提取真实数据。

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