Scal ramki danych przy dopasowywaniu A, B i * najbliższej * C?

Mam dwie ramki danych:

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)

Chcę się połączyćto.merge wdf (zall.x=T) takie, że:

df$x == to.merge$x Idf$y == to.merge$y Iabs(df$time - to.merge$time) <= 1; w przypadku wielokrotnościto.merge które spełniają, wybieramy ten, który minimalizuje te odległości.

Jak mogę to zrobić?

Więc moim pożądanym rezultatem jest (to jest po prostudf z odpowiednimvalue kolumnato.merge dodane dla pasujących wierszy):

   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

gdzieto.merge było:

  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

Uwaga - (2, 1, 17, a) nie pasuje dodf ponieważtime 17 było więcej niż 1 oddf$time 11 dla (X, Y) = (2, 1).

Ponadto były dwa rzędyto.merge to spełniało warunek dopasowania dodfwiersz (2, 1, 11), ale wiersz „c” został wybrany zamiast wiersza „b”, ponieważ jesttime był najbliższy 11.

Wreszcie mogą być wierszeto.merge które nie pasują do niczegodf.

Jednym ze sposobów działania jest pętla for-loop, ale moje dane zajmują zbyt dużo czasu (df ma ~ 12k rzędów ito.merge ma ~ 250 tys. wierszy)

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]
    }
}

Czuję, że mogę jakoś połączyć się, jak:

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)

Ale to nie łączy(2, 1, 11) wiersz, ponieważto.merge$closest_time_in_df dla(2, 1, 11.5, c) wynosi 12, ale czas 12 calidf odpowiada (x, y) = (2, 5) nie (2, 1) stąd połączenie nie powiedzie się.

questionAnswers(3)

yourAnswerToTheQuestion