У меня есть два такта данных:
set.seed(1)
df <- cbind(expand.grid(x=1:3, y=1:5), time=round(runif(15)*30))
to.merge <- data.frame(x=c(2, 2, 2, 3, 2),
y=c(1, 1, 1, 5, 4),
time=c(17, 12, 11.6, 22.5, 2),
val=letters[1:5],
stringsAsFactors=F)
Я хочу объединить to.merge
в df
(с помощью all.x=T
), чтобы:
-
df$x == to.merge$x
И -
df$y == to.merge$y
И -
abs(df$time - to.merge$time) <= 1
; в случае множестваto.merge
, которые удовлетворяют, мы выбираем ту, которая минимизирует это расстояние.
Как я могу это сделать?
Итак, мой желаемый результат (это просто df
с соответствующим столбцом value
to.merge
, добавленным для сопоставления строк):
x y time val
1 1 1 8 NA
2 2 1 11 c
3 3 1 17 NA
4 1 2 27 NA
5 2 2 6 NA
6 3 2 27 NA
7 1 3 28 NA
8 2 3 20 NA
9 3 3 19 NA
10 1 4 2 NA
11 2 4 6 NA
12 3 4 5 NA
13 1 5 21 NA
14 2 5 12 NA
15 3 5 23 d
где to.merge
:
x y time val
1 2 1 17.0 a
2 2 1 12.0 b
3 2 1 11.6 c
4 3 5 22.5 d
5 2 4 2.0 e
Примечание - (2, 1, 17, a) не совпало с df
, потому что time
17 было более 1 от df$time
11 для (X, Y) = (2, 1).
Кроме того, в to.merge
были две строки, удовлетворяющие условию для сопоставления с строкой df
(2, 1, 11), но вместо строки 'b' была выбрана строка 'c', потому что ее time
был самым близким к 11.
Наконец, в to.merge
могут быть строки, которые не соответствуют чему-либо в df
.
Один из способов работы - это цикл for, но для моих данных он слишком длинный (df
имеет ~ 12k строк и to.merge
имеет строки ~ 250k)
df$value <- NA
for (i in 1:nrow(df)) {
row <- df[i, ]
idx <- which(row$x == to.merge$x &
row$y == to.merge$y &
abs(row$time - to.merge$time) <= 1)
if (length(idx)) {
j <- idx[which.min(row$time - to.merge$time[idx])]
df$val[i] <- to.merge$val[j]
}
}
Я чувствую, что могу как-то слить, например:
to.merge$closest_time_in_df <- sapply(to.merge$time,
function (tm) {
dts <- abs(tm - df$time)
# difference must be at most 1
if (min(dts) <= 1) {
df$time[which.min(dts)]
} else {
NA
}
})
merge(df, to.merge,
by.x=c('x', 'y', 'time'),
by.y=c('x', 'y', 'closest_time_in_df'),
all.x=T)
Но это не сливает строку (2, 1, 11)
, потому что to.merge$closest_time_in_df
для (2, 1, 11.5, c)
равно 12, но время 12 в df
соответствует (x, y) = (2, 5) не (2, 1), следовательно, слияние терпит неудачу.