Присоединить кадры данных по идентификатору и диапазону дат с перекрытием

У меня есть два кадра данных x и 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 <- 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)

Я хотел бы объединить их в новый фрейм данных z, сопоставив id и влажную дату.y происходит в пределах date.x + 3 дня, например, у отдельного "1" было событие "y" в date.y = "2015-01-03 msgstr "в течение 3 дней с момента события x на date.x = "2015-01-01".

3 ответа

Решение

Вы можете создать оператор 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>

Я заимствовал функцию safe.ifelse из этого поста, потому что базовый оператор ifelse приводит к числовому вектору, а не к вектору даты.

Используя версию разработки data.table, v1.9.7там, где недавно были реализованы неравные (или условные) объединения, мы можем сделать это простым (и эффективным) способом. См. инструкции по установке здесь.

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>

Решения, которые объединяются в фиктивном столбце и затем фильтруются на основе условий, как правило, не масштабируются (так как число строк быстро разлагается), а решения, которые циклически прорабатывают строки и запускают условие фильтрации для каждой строки, являются медленными, так как они выполняют операция по строкам.

Это решение не выполняет ни одного, т. Е. Непосредственно выполняет условное соединение, и поэтому должно быть быстродействующим с точки зрения времени выполнения и памяти.

Используя внутреннее объединение таблиц данных 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
Другие вопросы по тегам