2015-08-14 23 views
14

Sto cercando di capire rolling joins in data.table. I dati per riprodurre questo è dato alla fine.Arrotolamento join su data.table con chiavi duplicate

dato un data.table di transazioni in un aeroporto, in un dato momento:

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

(nota 1 t_ids & 3 hanno lo stesso aeroporto e il tempo)

e una tabella di ricerca di voli in partenza da aeroporti:

> 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 

vorrei abbinare tutte le operazioni a tutti i prossimi possibili voli in partenza da tale aeroporto, per dare:

t_id airport thisTime f_id 
     1  a  6 1 
     1  a  6 2 
     3  a  6 1 
     3  a  6 2 

così ho pensato che questo dovrebbe funzionare:

> dt[dt_lookup, nomatch=0,roll=Inf] 
    t_id airport thisTime f_id 
1: 3  a  6 1 
2: 3  a  6 2 

Ma non è tornato transazioni t_id == 1.

Da the documentation dice:

Di solito, non ci dovrebbero essere duplicati in chiave di x, ...

Tuttavia, io ho i duplicati nel mio 'chiave x' (vale a dire airport & thisTime), e non è possibile vedere/comprendere cosa sta per significare che t_id = 1 viene rimosso dall'output.

Qualcuno può far luce sul motivo per cui t_id = 1 non viene restituito, e come posso far funzionare il join quando ho duplicati?

dati

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")) 

risposta

21

La ragione per cui t_id = 1 non si presenta in uscita è perché un rotolamento join prende la riga in cui la combinazione di tasti posta per ultima. Dalla documentazione (sottolineatura mia):

Vale per l'ultima colonna di join, in genere una data, ma può essere eventuali lacune variabile ordinata, irregolari e compreso. Se roll = TRUE e i numeri di riga corrispondono a tutti tranne l'ultima colonna x join e il suo valore nella colonna ultimo join cade in un intervallo (anche dopo l'ultima osservazione di in x per quel gruppo), quindi il valore prevalente in x è rotolato in avanti. Questa operazione è particolarmente veloce utilizzando una ricerca binaria modificata . L'operazione è anche nota come ultima osservazione trasportata in avanti (LOCF).

Consideriamo serie di dati un po 'più grandi:

> 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 

Quando si esegue un rotolamento join proprio come nella tua domanda:

DT[DT_LU, nomatch=0, roll=Inf] 

si ottiene:

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 

Come puoi vedere, da entrambi i tasti combinazione a, 5.1 e d, 6.2 l'ultima riga viene utilizzata per il datatable unito. Poiché si utilizza Inf come valore di rollover, tutti i valori futuri vengono incorporati nel datatable risultante. Quando si utilizza:

DT[DT_LU, nomatch=0, roll=1] 

si vede che solo il primo valore in futuro è incluso:

t_id airport thisTime f_id 
1: 3  a  6 1 
2: 3  a  6 2 
3: 5  d  7 2 

Se si desidera che il f_id 's per per tutte le combinazioni di airport & thisTime dove DT$thisTime è inferiore a DT_LU$thisTime, è possibile ottenere ciò creando una nuova variabile (o sostituendo lo esistente thisTime) tramite il ceiling funzione. Un esempio in cui creo una variabile nuova thisTime2 e poi fare un normale unirsi con DT_LU:

DT[, thisTime2 := ceiling(thisTime)] 
setkey(DT, airport, thisTime2)[DT_LU, nomatch=0] 

che dà:

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 

applicate ai dati che hai fornito:

> 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 

Quando si desidera includere tutti i valori futuri anziché solo il primo o ne, è necessario un approccio un po 'diverso per il quale è necessario il funzionalità i.col (che non è ancora documentato):

: primo set la chiave per solo le airport colonne:

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

: Utilizzare la funzionalità di i.col (che non è ancora documentato) in j per ottenere ciò che si vuole come segue:

DT1 <- DT_LU[DT, .(tid = i.t_id, 
        tTime = i.thisTime, 
        fTime = thisTime[i.thisTime < thisTime], 
        fid = f_id[i.thisTime < thisTime]), 
      by=.EACHI] 
.210

questo ti dà:

> 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 

Qualche spiegazione: Nel caso in cui si stanno unendo due DataTable dove gli stessi columnnames vengono utilizzati, è possibile fare riferimento alle colonne del DataTable in i facendo precedere il columnnames con i.. Ora è possibile confrontare thisTime da DT con thisTime da DT_LU.Con by = .EACHI si assicura che tutte le combinazioni per le condizioni in attesa siano incluse nel datatable risultante.

In alternativa, è possibile ottenere lo stesso con:

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] 

che dà lo stesso risultato:

> identical(DT1, DT2) 
[1] TRUE 

Se solo si desidera includere i valori futuri entro un certo limite, è possibile utilizzare:

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] 

che dà:

> 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 

Quando si confronta il risultato precedente, si vede che ora le righe 3, 6, 9, 10 e 12 sono state rimosse.


dati:

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

bello vedere questo post. Lo stavo provando da un po 'di tempo .. – akrun

+0

Grande spiegazione - "un join rotolante prende la riga dove si trova la combinazione di tasti per ultima" - è stata la chiave per la mia comprensione grazie. – tospig

+0

E il tuo esempio 'ceiling' funziona bene in questa situazione, ma mi aspetto che non funzioni quando il valore di' dt $ thisTime2' è superiore all'unità di tempo '1' lontano dal valore' dt_lookup $ thisTime' che sta cercando di far coincidere a, quindi potrei dover trovare un'alternativa? – tospig

Problemi correlati