R - присвоить значение столбца на основе ближайшего совпадения во втором фрейме данных

У меня есть два фрейма данных, регистратор и df (время числовое):

logger <- data.frame(
time = c(1280248354:1280248413),
temp = runif(60,min=18,max=24.5)
)

df <- data.frame(
obs = c(1:10),
time = runif(10,min=1280248354,max=1280248413),
temp = NA
)

Я хотел бы выполнить поиск в logger $ time для ближайшего совпадения с каждой строкой в ​​df $ time и назначить связанный logger $ temp для df $ temp. До сих пор мне удавалось использовать следующий цикл:

for (i in 1:length(df$time)){
closestto<-which.min(abs((logger$time) - (df$time[i])))
df$temp[i]<-logger$temp[closestto]
}

Однако теперь у меня есть большие кадры данных (регистратор имеет 13 620 строк, а df - 266 138), а время обработки велико. Я читал, что циклы - не самый эффективный способ делать что-то, но я не знаком с альтернативами. Есть ли более быстрый способ сделать это?


person dschorn    schedule 13.11.2013    source источник
comment
+1 за воспроизводимый пример с образцами данных, показывающий, что вы хотели и что вы пробовали. Кстати - в следующий раз, когда вы будете использовать данные со случайной выборкой, сначала запустите команду set.seed(x), где x - любое целое число (большинство людей используют 1). Таким образом, каждый, кто копирует ваш пример, получит один и тот же набор данных.   -  person Simon O'Hanlon    schedule 13.11.2013


Ответы (2)


Я бы использовал для этого data.table. Это позволяет очень легко и очень быстро присоединиться к keys. Есть даже действительно полезный roll = "nearest" аргумент для именно того поведения, которое вы ищете (за исключением данных вашего примера, в этом нет необходимости, потому что все times из df появляются в logger). В следующем примере я переименовал df$time в df$time1, чтобы было понятно, какой столбец какой таблице принадлежит ...

#  Load package
require( data.table )

#  Make data.frames into data.tables with a key column
ldt <- data.table( logger , key = "time" )
dt <- data.table( df , key = "time1" )

#  Join based on the key column of the two tables (time & time1)
#  roll = "nearest" gives the desired behaviour
#  list( obs , time1 , temp ) gives the columns you want to return from dt
ldt[ dt , list( obs , time1 , temp ) , roll = "nearest" ]
#          time obs      time1     temp
# 1: 1280248361   8 1280248361 18.07644
# 2: 1280248366   4 1280248366 21.88957
# 3: 1280248370   3 1280248370 19.09015
# 4: 1280248376   5 1280248376 22.39770
# 5: 1280248381   6 1280248381 24.12758
# 6: 1280248383  10 1280248383 22.70919
# 7: 1280248385   1 1280248385 18.78183
# 8: 1280248389   2 1280248389 18.17874
# 9: 1280248393   9 1280248393 18.03098
#10: 1280248403   7 1280248403 22.74372
person Simon O'Hanlon    schedule 13.11.2013

Вы можете использовать библиотеку data.table. Это также поможет повысить эффективность работы с большими объемами данных -

library(data.table)

logger <- data.frame(
  time = c(1280248354:1280248413),
  temp = runif(60,min=18,max=24.5)
)

df <- data.frame(
  obs = c(1:10),
  time = runif(10,min=1280248354,max=1280248413)
)

logger <- data.table(logger)
df <- data.table(df)

setkey(df,time)
setkey(logger,time)

df2 <- logger[df, roll = "nearest"]

Выход -

> df2
          time     temp obs
 1: 1280248356 22.81437   7
 2: 1280248360 24.08711  10
 3: 1280248366 22.31738   2
 4: 1280248367 18.61222   5
 5: 1280248388 19.46300   4
 6: 1280248393 18.26535   6
 7: 1280248400 20.61901   9
 8: 1280248402 21.92584   1
 9: 1280248410 19.36526   8
10: 1280248410 19.36526   3
person TheComeOnMan    schedule 13.11.2013