Скользящее соединение на data.table с дублирующимися ключами
Я пытаюсь понять rolling joins
в data.table
, Данные для воспроизведения приведены в конце.
Имеется таблица данных транзакций в аэропорту, в данное время:
> dt
t_id airport thisTime
1: 1 a 5.1
2: 3 a 5.1
3: 2 a 6.2
(нота t_ids
1 и 3 имеют одинаковый аэропорт и время)
и справочная таблица рейсов, вылетающих из аэропортов:
> dt_lookup
f_id airport thisTime
1: 1 a 6
2: 2 a 6
3: 1 b 7
4: 1 c 8
5: 2 d 7
6: 1 d 9
7: 2 e 8
> tables()
NAME NROW NCOL MB COLS KEY
[1,] dt 3 3 1 t_id,airport,thisTime airport,thisTime
[2,] dt_lookup 7 3 1 f_id,airport,thisTime airport,thisTime
Я хотел бы сопоставить все транзакции со всеми возможными рейсами, вылетающими из этого аэропорта, чтобы получить:
t_id airport thisTime f_id
1 a 6 1
1 a 6 2
3 a 6 1
3 a 6 2
Поэтому я подумал, что это будет работать:
> dt[dt_lookup, nomatch=0,roll=Inf]
t_id airport thisTime f_id
1: 3 a 6 1
2: 3 a 6 2
Но это не вернуло транзакции t_id == 1
,
Из документации написано:
Обычно в ключе x не должно быть дубликатов,...
Тем не менее, у меня есть дубликаты в моем "х ключе" (а именно airport
& thisTime
), и не совсем понимаю / понимаю, что это значит t_id = 1
удаляется с выхода.
Может кто-нибудь пролить свет на то, почему t_id = 1
не возвращается, и как я могу получить объединение для работы, когда у меня есть дубликаты?
Данные
library(data.table)
dt <- data.table(t_id = seq(1:3),
airport = c("a","a","a"),
thisTime = c(5.1,6.2, 5.1), key=c( "airport","thisTime"))
dt_lookup <- data.table(f_id = c(rep(1,4),rep(2,3)),
airport = c("a","b","c","d",
"a","d","e"),
thisTime = c(6,7,8,9,
6,7,8), key=c("airport","thisTime"))
1 ответ
Причина того, что t_id = 1
не отображается в выходных данных потому, что скользящее соединение занимает строку, в которой комбинация клавиш встречается последней. Из документации (выделено мое):
Применяется к последнему столбцу соединения, обычно к дате, но может быть любой упорядоченной переменной, нерегулярной и включающей пробелы. Если roll=TRUE и строка i совпадает со всеми, кроме последнего столбца x x join, и его значение в последнем столбце i i join попадает в пробел (в том числе после последнего наблюдения в x для этой группы), то преобладающее значение в x равно накатился вперед. Эта операция особенно быстро выполняется с использованием модифицированного бинарного поиска. Операция также известна как последнее перенесенное наблюдение (LOCF).
Давайте рассмотрим несколько большие наборы данных:
> DT
t_id airport thisTime
1: 1 a 5.1
2: 4 a 5.1
3: 3 a 5.1
4: 2 d 6.2
5: 5 d 6.2
> DT_LU
f_id airport thisTime
1: 1 a 6
2: 2 a 6
3: 2 a 8
4: 1 b 7
5: 1 c 8
6: 2 d 7
7: 1 d 9
Когда вы выполняете скользящее соединение, как в вашем вопросе:
DT[DT_LU, nomatch=0, roll=Inf]
ты получаешь:
t_id airport thisTime f_id
1: 3 a 6 1
2: 3 a 6 2
3: 3 a 8 2
4: 5 d 7 2
5: 5 d 9 1
Как вы можете видеть, как из комбинации клавиш a, 5.1
а также d, 6.2
последняя строка используется для объединенных данных. Потому что вы используете Inf
в качестве значения крена все будущие значения включаются в результирующую таблицу данных. Когда вы используете:
DT[DT_LU, nomatch=0, roll=1]
Вы видите, что включено только первое значение в будущем:
t_id airport thisTime f_id
1: 3 a 6 1
2: 3 a 6 2
3: 5 d 7 2
Если вы хотите f_id
для всех комбинаций airport
& thisTime
где DT$thisTime
ниже чем DT_LU$thisTime
Вы можете достичь этого, создав новую переменную (или заменив существующую thisTime
с помощью ceiling
функция. Пример, где я создаю новую переменную thisTime2
а затем сделать нормальное соединение с DT_LU
:
DT[, thisTime2 := ceiling(thisTime)]
setkey(DT, airport, thisTime2)[DT_LU, nomatch=0]
который дает:
t_id airport thisTime thisTime2 f_id
1: 1 a 5.1 6 1
2: 4 a 5.1 6 1
3: 3 a 5.1 6 1
4: 1 a 5.1 6 2
5: 4 a 5.1 6 2
6: 3 a 5.1 6 2
7: 2 d 6.2 7 2
8: 5 d 6.2 7 2
Применительно к предоставленным вами данным:
> dt[, thisTime2 := ceiling(thisTime)]
> setkey(dt, airport, thisTime2)[dt_lookup, nomatch=0]
t_id airport thisTime thisTime2 f_id
1: 1 a 5.1 6 1
2: 3 a 5.1 6 1
3: 1 a 5.1 6 2
4: 3 a 5.1 6 2
Если вы хотите включить все будущие значения вместо только первого, вам нужен несколько иной подход, для которого вам понадобится i.col
функциональность (которая еще не документирована):
1: сначала установите ключ только на airport
колонки:
setkey(DT, airport)
setkey(DT_LU, airport)
2: Используйте i.col
функциональность (которая пока не задокументирована) в j
чтобы получить то, что вы хотите, следующим образом:
DT1 <- DT_LU[DT, .(tid = i.t_id,
tTime = i.thisTime,
fTime = thisTime[i.thisTime < thisTime],
fid = f_id[i.thisTime < thisTime]),
by=.EACHI]
это дает вам:
> DT1
airport tid tTime fTime fid
1: a 1 5.1 6 1
2: a 1 5.1 6 2
3: a 1 5.1 8 2
4: a 4 5.1 6 1
5: a 4 5.1 6 2
6: a 4 5.1 8 2
7: a 3 5.1 6 1
8: a 3 5.1 6 2
9: a 3 5.1 8 2
10: d 2 6.2 7 2
11: d 2 6.2 9 1
12: d 5 6.2 7 2
13: d 5 6.2 9 1
Некоторое объяснение: если вы объединяете две таблицы данных, в которых используются одинаковые имена столбцов, вы можете ссылаться на столбцы таблицы данных в i
предшествуя именам столбцов i.
, Теперь можно сравнивать thisTime
от DT
с thisTime
от DT_LU
, С by = .EACHI
Вы гарантируете, что все комбинации для с удержанием условия включены в результирующую таблицу данных.
Кроме того, вы можете добиться того же с:
DT2 <- DT_LU[DT, .(airport=i.airport,
tid=i.t_id,
tTime=i.thisTime,
fTime=thisTime[i.thisTime < thisTime],
fid=f_id[i.thisTime < thisTime]),
allow.cartesian=TRUE]
который дает тот же результат:
> identical(DT1, DT2)
[1] TRUE
Если вы хотите включить только будущие значения в определенную границу, вы можете использовать:
DT1 <- DT_LU[DT,
{
idx = i.thisTime < thisTime & thisTime - i.thisTime < 2
.(tid = i.t_id,
tTime = i.thisTime,
fTime = thisTime[idx],
fid = f_id[idx])
},
by=.EACHI]
который дает:
> DT1
airport tid tTime fTime fid
1: a 1 5.1 6 1
2: a 1 5.1 6 2
3: a 4 5.1 6 1
4: a 4 5.1 6 2
5: a 3 5.1 6 1
6: a 3 5.1 6 2
7: d 2 6.2 7 2
8: d 5 6.2 7 2
Когда вы сравните это с предыдущим результатом, вы увидите, что теперь строки 3, 6, 9, 10 и 12 удалены.
Данные:
DT <- data.table(t_id = c(1,4,2,3,5),
airport = c("a","a","d","a","d"),
thisTime = c(5.1, 5.1, 6.2, 5.1, 6.2),
key=c("airport","thisTime"))
DT_LU <- data.table(f_id = c(rep(1,4),rep(2,3)),
airport = c("a","b","c","d","a","d","e"),
thisTime = c(6,7,8,9,6,7,8),
key=c("airport","thisTime"))