Присоедините 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. Я просто хотел отметить этот момент. Я оставлю это вам.