2015-02-26 5 views
9

Ho due tabelle che vorrei unire insieme in un modo equivalente al seguente SQL, dove mi unisco su più condizioni, non solo l'uguaglianza.R Data.Table Join on Conditionals

require(sqldf) 
require(data.table) 

dt <- data.table(num=c(1, 2, 3, 4, 5, 6), 
char=c('A', 'A', 'A', 'B', 'B', 'B'), 
bool=c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE)) 

dt_two <- data.table(
num =c(6, 1, 5, 2, 4, 3), 
char=c('A', 'A', 'A', 'B', 'B', 'B'), 
bool=c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE)) 


dt_out_sql <- sqldf(' 
    select dtone.num, dtone.char, dtone.bool, SUM(dttwo.num) as SUM, 
    MIN(dttwo.num) as MIN 
    from dt as dtone INNER join dt_two as dttwo on 
    (dtone.char = dttwo.char) and 
    (dtone.num >= dttwo.num OR dtone.bool) 
GROUP BY dtone.num, dtone.char, dtone.bool') 

Vorrei evitare la soluzione SQL, sia per motivi di prestazioni che di flessibilità. Lo stesso vale per fare un cross join, e quindi filtrare/aggregare - creerebbe una tabella intermedia con un sacco di record non necessari da filtrare.

Grazie mille!

Aggiornamento - il mio esempio iniziale è stato fatto in fretta. Nel mio vero problema, non sto facendo un auto join.

risposta

5

E 'un po' brutto, ma funziona:

library(data.table) 
library(sqldf) 

dt <- data.table(num=c(1, 2, 3, 4, 5, 6), 
       char=c('A', 'A', 'A', 'B', 'B', 'B'), 
       bool=c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE)) 

dt_two <- data.table(
    num =c(6, 1, 5, 2, 4, 3), 
    char=c('A', 'A', 'A', 'B', 'B', 'B'), 
    bool=c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE)) 


dt_out_sql <- sqldf(' 
    select dtone.num, 
      dtone.char, 
      dtone.bool, 
      SUM(dttwo.num) as SUM, 
      MIN(dttwo.num) as MIN 
    from dt as dtone 
    INNER join dt_two as dttwo on 
      (dtone.char = dttwo.char) and 
      (dtone.num >= dttwo.num OR dtone.bool) 
    GROUP BY dtone.num, dtone.char, dtone.bool 
    ') 

setDT(dt_out_sql) 

setkey(dt, char) 
setkey(dt_two, char) 

dt_out_r <- dt[dt_two, 
       list(dtone.num = num, 
        dttwo.num = i.num, 
        char, 
        bool) , 
       nomatch = 0, allow.cartesian = T 
       ][ 
       dtone.num >= dttwo.num | bool, 
       list(SUM = sum(dttwo.num), 
         MIN = min(dttwo.num)), 
       by = list(num = dtone.num, 
          char, 
          bool) 
       ] 

setkey(dt_out_r, num, char, bool) 


all.equal(dt_out_sql, dt_out_r, check.attributes = FALSE) 
+1

I benchmarked delle possibili opzioni, e questo in realtà si è rivelato il più veloce. In questo caso, posso vivere con brutto. :) – Netbrian

9

Ecco un modo:

require(data.table) 
setkey(dt, char) 
setkey(dt_two, char) 

dt_two[dt, { 
    val = num[i.bool | i.num >= num]; 
    list(num=i.num, bool=i.bool, sum=sum(val), min=min(val)) 
}, by=.EACHI] 
# char num bool sum min 
# 1: A 1 TRUE 12 1 
# 2: A 2 FALSE 1 1 
# 3: A 3 TRUE 12 1 
# 4: B 4 FALSE 9 2 
# 5: B 5 TRUE 9 2 
# 6: B 6 FALSE 9 2 

Per leggere su by=.EACHI, dare un'occhiata al this post (fino vignette su join sono finito).

HTH

+0

grazie per aver postato questo; evita il prodotto cartesiano. C'è un modo per fare qualcosa di simile, ma invece di riepilogare la colonna dell'indice? Ho posto una nuova domanda qui: http://stackoverflow.com/questions/28761809/summarize-the-self-join-index-while-avoiding-cartesian-product-in-r-data-table – nsheff

-2

Dal data.table 1.9.8, per i casi in cui si uniscono le condizioni potrebbero essere rilassato, c'è la semplice non equi join sintassi in questo modo:

dt_two[dt, on=.(char, num >= num)] 
+0

Ciò non restituisce il risultato previsto come illustrato [qui] (http://stackoverflow.com/a/28748717/3817004), ad es. – Uwe

+0

Spiacente, sì, la ragione per cui questo non restituisce lo stesso risultato è perché la seconda condizione è in realtà 'num> = num OR dtone'. Per quanto ho potuto vedere questo non è valido nel data.table corrente. Tuttavia, ritengo ancora rilevante menzionare la funzione di join non equi per i casi in cui le condizioni di join sono più rilassate. – nikola

+0

Ho modificato la risposta per sottolineare questo. – nikola