Присоедините R data.tables, где значения ключа не совсем равны - объедините строки с ближайшими моментами

Есть ли пятно способ присоединиться к таблицам данных в R, где ключевые значения времени близки, но не совсем то же самое? Например, предположим, что у меня есть таблица данных результатов, которые даны для разных периодов времени:

DT1 = data.table(x=rep(c("a","b","c"),each=3), time=c(10,30,60), v=1:9)

Здесь мы имеем несколько значений (v) для разных категорий (x), взятых в разное время (время). Теперь предположим, что у меня есть данные из другого источника, который предоставляет некоторые значения времени для разных категорий:

DT2=data.table(x=rep(c("a","b","c"),each=1),time=c(10,10,60))

Мне может быть интересно попытаться совместить время в DT2 так близко, как я могу, до DT1, чтобы предсказать значение, v, для моих категорий DT2. Я хотел бы сделать что-то вроде

setkeyv(DT2,c("x","time"))
merge(DT1,DT2,by=c("time","v")

Что возвращает:

   time x v
1:   10 a 1
2:   10 b 4
3:   60 c 9

Но что, если бы мои времена не имели такой же точности? Например:

DT2=data.table(x=rep(c("a","b","c"),each=1),time=c(17,54,3))

Есть ли способ выполнить аналогичное слияние, но выбрать время DT2, которое близко к DT1? То есть 17 будет близко к 30, 54 близко к 60 и 3 близко к 10?

Если этот простой пример не ясен, я кратко объясню большую проблему, которую я испытываю. У меня есть таблица данных со столбцами: category, time, output1, output2... Есть сотни категорий со связанным временем. Возможно, мне захочется вывести результат 1 для всех категорий в определенное время. Поскольку времена были отбракованы без видимой логики, иногда время округляется до ближайшей четной секунды; в других случаях время округляется до ближайшей минуты или даже 10 минут.

Я мог бы написать script, чтобы переписать время в более общем формате, но мне любопытно, есть ли пятно data.table решение, которое я не видел. Я исследовал слияние без успеха.

Ответы

Ответ 1

Другой вариант может быть roll='nearest' (новый в версии 1.8.8 на CRAN).

> setkey(DT1,x,time)
> DT1
   x time v
1: a   10 1
2: a   30 2
3: a   60 3
4: b   10 4
5: b   30 5
6: b   60 6
7: c   10 7
8: c   30 8
9: c   60 9
> DT2
   x time
1: a   17
2: b   54
3: c    3
> DT1[DT2,roll="nearest"]
   x time v
1: a   17 1
2: b   54 6
3: c    3 7

Обратите внимание, что 17 кажется ближе к 10, чем 30, следовательно, результат в первой строке.

Если вам нужно перейти к следующему наблюдению (следующее наблюдение перенесено назад):

> DT1[DT2,roll=-Inf]
   x time v
1: a   17 2
2: b   54 6
3: c    3 7

Ответ 2

Вы можете использовать findInterval для выполнения этого:

setkey(DT2, time)
DT1[, id := findInterval(DT1$time, DT2$time)]
DT2[, id := 1:3]

setkey(DT1, "x", "id")
setkey(DT2, "x", "id")
print(DT1[DT2][, id := NULL])
#    x time v time.1
# 1: a   30 2     17
# 2: b   60 6     54
# 3: c   10 7      3

Идея: сначала сортируйте data.table по времени, потому что второй аргумент findInterval требует увеличения порядка значений. Теперь используйте findInterval, чтобы найти, в каком интервале 3, 17, 54 выпадают значения в DT1$time и сохраняются в id. В этом конкретном случае это может варьироваться от 1 до 3. Таким образом, установите эти значения как столбец id для DT2. Как только вы найдете интервалы и получите id, тогда это просто. Вместо установки x и time установите x и id в качестве ключей и выполните слияние.

Примечание: Предположим, что у вашего DT1$time было значение 0, тогда интервал для этого был бы 0. Таким образом, вы получили бы 4 уникальных значения (0: 3). В этом случае может быть лучше иметь DT2 со значением времени = 0. Я просто хотел отметить этот момент. Я оставлю это вам.