Присоединить кадры данных по идентификатору и диапазону дат с перекрытием
У меня есть два кадра данных 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