2015-08-14 4 views
14

Я пытаюсь понять rolling joins в data.table. Данные для воспроизведения этого приведены в конце.Rolling join на data.table с дублирующимися ключами

Учитывая data.table сделок в аэропорту, в данное время:

> dt 
    t_id airport thisTime 
1: 1  a  5.1 
2: 3  a  5.1 
3: 2  a  6.2 

(обратите внимание 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.

От the documentation он говорит:

Обычно, не должно быть никаких дубликатов в ключе иксы ...

Однако, у меня есть дубликаты в моей 'х ключа' (а именно 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")) 

ответ

21

Причина, по которой t_id = 1 не появляется в выход, потому что прокатный присоединиться принимает строку, в которой ключевой комбинации происходит в последнюю очередь. Из документации (курсив мой):

Относится к последнему присоединиться к колонке, как правило, даты, но могут быть любые упорядоченный переменной, нерегулярные и включая пробелы. Если строка roll = TRUE и i соответствует всем, кроме последнего столбца соединения x, а его значение в последнем столбце соединения падает в промежутке (в том числе после последнего наблюдения в x для этой группы), тогда преобладающее значение in x is свернуто вперед. Эта операция особенно быстро выполняется с использованием модифицированного двоичного поиска . Операция также известна как последнее проведенное наблюдение вперед (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 «S для всех комбинаций 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 функциональность (которая еще не задокументированных):

: Сначала установите ключ только airport столбцов:

setkey(DT, airport) 
setkey(DT_LU, airport) 

: Используйте 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 

Некоторые пояснения: В случае, когда вы присоединяетесь два DataTables, где одни и те же columnnames используются, вы можете обратиться к столбцам DataTable в i по предшествовавших columnnames с 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")) 
+2

Рад видеть этот пост. Я пытался это сделать в течение некоторого времени. – akrun

+0

Отличное объяснение - «скользящее соединение занимает строку, где сочетается комбинация клавиш» - был ключ к моему пониманию. – tospig

+0

И ваш пример 'потолка' хорошо работает в этой ситуации, но я ожидаю, что это не сработает, когда значение' dt $ thisTime2' будет больше, чем единица времени 1 вдали от значения 'dt_lookup $ thisTime', которое оно пытается совместить к, поэтому мне, возможно, придется придумать альтернативу? – tospig

Смежные вопросы