Non esiste una risposta diretta / semplice perché le filosofie di entrambi questi pacchetti differiscono per certi aspetti. Quindi alcuni compromessi sono inevitabili. Ecco alcune delle preoccupazioni che potresti dover affrontare / considerare.
Operazioni che coinvolgono i
(== filter()
e slice()
in dplyr)
Supponiamo DT
con diciamo 10 colonne. Considera queste espressioni data.table:
DT[a > 1, .N]
DT[a > 1, mean(b), by=.(c, d)]
(1) fornisce il numero di righe in DT
cui colonna a > 1
. (2) restituisce mean(b)
raggruppati per c,d
per la stessa espressione in i
(1).
Le dplyr
espressioni comunemente usate sarebbero:
DT %>% filter(a > 1) %>% summarise(n())
DT %>% filter(a > 1) %>% group_by(c, d) %>% summarise(mean(b))
Chiaramente, i codici data.table sono più brevi. Inoltre sono anche più efficienti in termini di memoria 1 . Perché? Perché sia in (3) che in (4), filter()
restituisce prima le righe per tutte le 10 colonne , quando in (3) abbiamo solo bisogno del numero di righe e in (4) abbiamo solo bisogno di colonne b, c, d
per le operazioni successive. Per ovviare a questo, dobbiamo select()
colonne apriori:
DT %>% select(a) %>% filter(a > 1) %>% summarise(n())
DT %>% select(a,b,c,d) %>% filter(a > 1) %>% group_by(c,d) %>% summarise(mean(b))
È essenziale evidenziare un'importante differenza filosofica tra i due pacchetti:
In data.table
, ci piace tenere insieme queste operazioni correlate, e questo consente di guardare j-expression
(dalla stessa chiamata di funzione) e rendersi conto che non c'è bisogno di colonne in (1). L'espressione in i
viene calcolata ed .N
è solo la somma di quel vettore logico che fornisce il numero di righe; l'intero sottoinsieme non viene mai realizzato. In (2), solo la colonna b,c,d
viene materializzata nel sottoinsieme, le altre colonne vengono ignorate.
Ma in dplyr
, la filosofia è quella di avere una funzione di fare esattamente una cosa bene . Non c'è (almeno attualmente) alcun modo per sapere se l'operazione successiva filter()
necessita di tutte quelle colonne che abbiamo filtrato. Dovrai pensare al futuro se desideri eseguire tali attività in modo efficiente. Personalmente lo trovo controintutitivo in questo caso.
Nota che in (5) e (6), abbiamo ancora sottoinsiemi di colonne a
che non richiedono. Ma non sono sicuro di come evitarlo. Se la filter()
funzione avesse un argomento per selezionare le colonne da restituire, potremmo evitare questo problema, ma in tal caso la funzione non eseguirà solo un'attività (che è anche una scelta di progettazione di dplyr).
Sottoassegna per riferimento
dplyr non si aggiornerà mai per riferimento. Questa è un'altra enorme differenza (filosofica) tra i due pacchetti.
Ad esempio, in data.table puoi fare:
DT[a %in% some_vals, a := NA]
che aggiorna la colonna a
per riferimento solo sulle righe che soddisfano la condizione. Al momento dplyr copia in profondità l'intero data.table internamente per aggiungere una nuova colonna. @BrodieG lo ha già menzionato nella sua risposta.
Ma la copia completa può essere sostituita da una copia superficiale quando viene implementato FR # 617 . Rilevante anche: dplyr: FR # 614 . Nota che comunque, la colonna che modifichi verrà sempre copiata (quindi un po 'più lenta / meno efficiente in termini di memoria). Non sarà possibile aggiornare le colonne per riferimento.
Altre funzionalità
In data.table, puoi aggregare durante l'unione, e questo è più semplice da capire ed è efficiente in termini di memoria poiché il risultato del join intermedio non viene mai materializzato. Controlla questo post per un esempio. Non puoi (al momento?) Farlo usando la sintassi data.table / data.frame di dplyr.
La funzione di join rotanti di data.table non è supportata anche nella sintassi di dplyr.
Recentemente abbiamo implementato i join sovrapposti in data.table per unire su intervalli di intervallo ( ecco un esempio ), che è una funzione separata foverlaps()
al momento, e quindi potrebbe essere utilizzato con gli operatori pipe (magrittr / pipeR? - non l'ho mai provato io stesso).
Ma in definitiva, il nostro obiettivo è integrarlo in [.data.table
modo da poter raccogliere le altre funzionalità come il raggruppamento, l'aggregazione durante l'adesione, ecc., Che avranno le stesse limitazioni descritte sopra.
Dalla 1.9.4, data.table implementa l'indicizzazione automatica utilizzando chiavi secondarie per sottoinsiemi basati sulla ricerca binaria veloce sulla sintassi R. Es: DT[x == 1]
e DT[x %in% some_vals]
creerà automaticamente un indice alla prima esecuzione, che verrà quindi utilizzato nei sottoinsiemi successivi dalla stessa colonna al sottoinsieme veloce utilizzando la ricerca binaria. Questa funzionalità continuerà ad evolversi. Controlla questa sintesi per una breve panoramica di questa funzione.
Dal modo in cui filter()
è implementato per data.tables, non sfrutta questa funzionalità.
Una caratteristica di dplyr è che fornisce anche l' interfaccia ai database utilizzando la stessa sintassi, che data.table non al momento.
Quindi, dovrai soppesare questi (e probabilmente altri punti) e decidere in base al fatto che questi compromessi siano accettabili per te.
HTH
(1) Si noti che l'efficienza della memoria influisce direttamente sulla velocità (soprattutto quando i dati diventano più grandi), poiché il collo di bottiglia nella maggior parte dei casi è lo spostamento dei dati dalla memoria principale alla cache (e l'utilizzo dei dati nella cache il più possibile - ridurre i mancati riscontri nella cache - in modo da ridurre gli accessi alla memoria principale). Non entrare nei dettagli qui.
dplyr
metodi per le tabelle di dati, ma anche la tabella di dati ha i suoi metodi comparabili