2016-02-05 4 views
6

O per essere più generale, è DT[,.SD[...],by=...] versus merge(aggregate(...)).R: performance data.table vs merge (aggregate())

Senza ulteriori indugi, ecco i dati e l'esempio:

set.seed(5141) 
size = 1e6 
df <- data.table(a = rnorm(size), 
       b = paste0(sample(letters, size, T), 
          sample(letters, size, T), 
          sample(letters, size, T)), 
       c = sample(1:(size/10), size, T), 
       d = sample(seq.Date(as.Date("2015-01-01"), 
            as.Date("2015-05-31"), by="day"), size, T)) 

system.time(df[,.SD[d == max(d)], by = c]) 
# user system elapsed 
# 50.89 0.00 51.00 
system.time(merge(aggregate(d ~ c, data = df, max), df)) 
# user system elapsed 
# 18.24 0.20 18.45 

solito avendo alcun problema con data.table prestazioni, mi sono sorpreso da questo particolare esempio. Ho dovuto sommare (aggregare) un frame di dati abbastanza grande prendendo solo le ultime (possono essere simultanee) occorrenze di alcuni tipi di eventi. E mantieni il resto dei dati rilevanti per quegli eventi particolari. Tuttavia, sembra che .SD non si adatti bene a questa particolare applicazione.

Esiste un "modo tabella dati" migliore per affrontare questo tipo di attività?

+3

È interessante notare che 'df [, is_max: = d == max (d), per = c] [is_max == T,]' gira molto velocemente sulla mia macchina. – Heroka

+0

Questa sembra essere la soluzione più veloce finora. Sarebbe possibile fare la stessa cosa senza creare la colonna in più? –

+0

Non lo so, si può sempre cancellarlo in seguito 'df [, is_max: = d == max (d), per = c] [is_max == T,] [, is_max: = NULL,]'. E la soluzione di join fornita da @Akrun è di gran lunga la più veloce. – Heroka

risposta

8

Possiamo usare .I per ottenere l'indice di riga e impostare il sottoinsieme delle righe in base a quello. Dovrebbe essere più veloce.

system.time(df[df[,.I[d == max(d)], by = c]$V1]) 
# user system elapsed 
# 5.00 0.09 5.30 

@ soluzione Heroka

system.time(df[,is_max:=d==max(d), by = c][is_max==T,]) 
# user system elapsed 
# 5.06 0.00 5.12 

Il metodo aggregate sulla mia macchina dà

system.time(merge(aggregate(d ~ c, data = df, max), df)) 
# user system elapsed 
# 48.62 1.00 50.76 

con l'opzione di .SD

system.time(df[,.SD[d == max(d)], by = c]) 
# user system elapsed 
# 151.13 0.40 156.57 

Utilizzando la data.table unirsi

system.time(df[df[, list(d=max(d)) , c], on=c('c', 'd')]) 
# user system elapsed 
# 0.58 0.01 0.60 

Se guardiamo i confronti tra il merge/aggregate e ==, sono funzioni diverse. Di solito, il metodo aggregate/merge sarà più lento rispetto al join corrispondente con data.table. Ma, invece, stiamo usando == che confronta ogni riga (richiede un po 'di tempo) insieme a .SD per il subsetting (che è anche relativamente meno efficiente se confrontato con lo .I per l'indicizzazione di riga). Lo .SD ha anche il sovraccarico di [.data.table.

+0

Penso che la soluzione di unione manchi qualcosa. E sospettosamente un buon risultato. Nel frattempo, esaminerò la soluzione '.I'. Grazie per aver fornito l'intera gamma da provare. –

+0

@ A.Val. Se osservi la sintassi, sto usando 'on'. È possibile testare la soluzione su un dataset più piccolo – akrun

+0

'> nrow (df [df [, lista (d = max (d)), c], on = 'c']) [1] 1000000' –