2016-05-03 42 views
8

Ho due data.tables con molti campi.R join data.table: SQL "select *" simile sintassi nelle tabelle unite?

Desidero unire le due tabelle, aggiungere alcuni campi calcolati e aggiungere tutti gli altri campi dalla prima, seconda o entrambe le tabelle (simile a SQL select a+b AS sum, DT1.*, DT2.* FROM...) senza digitare tutti i nomi dei campi.

Come posso fare questo (per quanto riguarda la sintassi più semplice e le migliori prestazioni)?

semplificato dati esempio:

library(data.table) 
DT1 = data.table(x=c("c", "a", "b", "a", "b"), a=1:5) 
DT2 = data.table(x=c("d", "c", "b"), b=6:8) 

Ora voglio unire le tabelle e aggiungere un campo calcolato:

DT1[DT2, .(sum=a + b, <<< how to say DT1.*, DT2.* here? >>>), on="x"] 

Aggiornamento 4 MAGGIO 2016: Ispirato da utente jangorecki Ho trovato una richiesta di funzionalità per questo:

Should be able to refer to i's .SD during a join

+0

io non sono sicuro che questo è proprio quello che avete bisogno, ma dare un'occhiata al l'argomento .SDcols nella tabella dei dati. Può essere utile – giraffehere

+0

@giraffehere Penso che .SDcols mi richiede di specificare ogni nome di campo che voglio evitare. Sto cercando di trovare una soluzione con .SD ma questo non funziona (errore di sintassi e nessun modo per differenziare tra DT1 e DT2) –

+1

Forse mostrare anche l'output desiderato? La tua domanda mi confonde un po '. –

risposta

7

Questo dovrebbe rispondere esattamente alle vostre necessità.
Utilizza la caratteristica R molto potente denominata elaborazione nella lingua (o meta programmazione) ben descritta nel manuale ufficiale R Language Definition. Questa è una caratteristica eccezionale del linguaggio R e non dovrebbe essere dimenticata IMO.

library(data.table) 
DT1 = data.table(x=c("c", "a", "b", "a", "b"), a=1:5) 
DT2 = data.table(x=c("d", "c", "b"), b=6:8) 

jj = as.call(c(
    list(as.name(".")), 
    list(sum = quote(a+b)), 
    lapply(unique(c(names(DT1), names(DT2))), as.name) 
)) 
print(jj) 
#.(sum = a + b, x, a, b) 
DT1[DT2, eval(jj), on="x"] 
# sum x a b 
#1: NA d NA 6 
#2: 8 c 1 7 
#3: 11 b 3 8 
#4: 13 b 5 8 
+0

, risposta incredibilmente buona! –

+3

Oltre al valore educativo, non un fan delle complicazioni extra qui. @RYoda se stai bene digitando 'nomi (DT *)', allora potrebbe anche fare: 'DT1 [DT2, c (lista (somma = a + b), mget (unione (nomi (DT1), nomi (DT2)))), on = 'x'] '. Al momento non c'è supporto per 'i.SD', ma c'è un FR su di esso. – eddi

+2

vale la pena notare che 'x.col' e' i.col' funzionano entrambi in 1.9.7, quindi 'as.name' può popolare nomi di nomi con prefissi, dando pieno controllo sulle colonne utilizzate. – jangorecki

5

Sono più sicuro della mia risposta alla seconda parte della domanda, quindi risponderò per prima. Se solo si vuole dire DT1 * o DT2 *, ma desidera che la colonna aggiuntiva nuova = a + b, vorrei fare in questo modo:...

DT1[DT2,new:=a+b,on="x"] 

Per la prima parte, in cui è necessario DT1 * . e DT2 *, l'unica risposta che viene in mente è:

DT1[DT2, on="x"][,new := a+b] 

Tuttavia, ci potrebbe essere il codice più efficiente per raggiungere questo obiettivo.

+0

Il tuo primo snippet di codice è perfetto per DT1. *. Penso che DT2. * Non funzioni in questo modo perché ': =' aggiunge la colonna a DT1. Per quanto riguarda il secondo frammento di codice: Funziona perfettamente per DT1. * E DT2. *, Ma non ho menzionato esplicitamente nella mia domanda che voglio evitare di aggiungere una nuova colonna per riferimento (ho mostrato solo una colonna calcolata quindi nella domanda) poiché Non voglio "perfezionare" le tabelle di dati con campi temporanei. Chiarirò la mia domanda –

+0

** Correzione: ** Ho appena capito che ': =' usato nel secondo o in alto '[...] 'chain NON aggiunge il campo alla tabella dati originale, quindi è esattamente quello che volevo! –

+1

Ho deciso di mettere a fuoco la mia domanda sulla parte principale (tutte le colonne di entrambe le tabelle) e ho rimosso le varianti (tutte le colonne da una sola tabella). Il tuo snippet di codice 2 è la soluzione giusta. –

0

È possibile mantenere solo le colonne in DT2 che è necessario:

DT1 = data.table(x=c("c", "a", "b", "a", "b"), a=1:5, d=rnorm(5)) 
DT2 = data.table(x=c("d", "c", "b"), b=6:8, c=letters[3]) 

DT3 <- DT1[DT2[,.(x,b), on="x"][, sum := a+b]