我有两个这样的数据框:
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
(with 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
因为对于 (X, Y) = (2, 1),time
17 与 11 的距离大于 1 。df$time
此外,有两行to.merge
满足匹配df
's (2, 1, 11) 行的条件,但是选择了 'c' 行而不是 'b' 行,因为它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
for(2, 1, 11.5, c)
是 12,但是 12 in 的时间df
对应于 (x, y) = (2, 5) 而不是 (2, 1) 因此合并失败。