2010-10-19 3 views
8

Ho una data.frame (link to file) con 18 colonne e 11520 righe che trasformo simili:come accelerare questo codice R

library(plyr) 
df.median<-ddply(data, .(groupname,starttime,fPhase,fCycle), 
       numcolwise(median), na.rm=TRUE) 

secondo system.time(), ci vogliono circa questo lungo per eseguire:

user system elapsed 
    5.16 0.00 5.17 

Questa chiamata è parte di una webapp, in modo da eseguire il tempo è abbastanza importante. C'è un modo per velocizzare questa chiamata?

+2

È possibile memorizzare nella cache i risultati? – Shane

+0

'ddply()' è prima di tutto * conveniente *. Se hai bisogno di qualcosa di veloce, potrebbe essere necessario reimplementare la logica. –

+0

@Shane: ci sono attualmente 3 * 400 set di dati possibili (e in aumento ogni giorno) che un utente potrebbe richiedere. È improbabile che un utente prenda lo stesso set di dati di un altro. Quindi il caching sarebbe utile solo all'interno di una sessione. Poiché l'output della webapp è essenzialmente un report in scatola, non penso che l'utente lo richiederebbe più di una volta. Implementeresti il ​​caching per la situazione che ho descritto? Non l'ho mai fatto prima, quindi sono un po 'in perdita. – dnagirl

risposta

9

Usando solo aggregate è un po 'più veloce ...

> groupVars <- c("groupname","starttime","fPhase","fCycle") 
> dataVars <- colnames(data)[ !(colnames(data) %in% c("location",groupVars)) ] 
> 
> system.time(ag.median <- aggregate(data[,dataVars], data[,groupVars], median)) 
    user system elapsed 
    1.89 0.00 1.89 
> system.time(df.median <- ddply(data, .(groupname,starttime,fPhase,fCycle), numcolwise(median), na.rm=TRUE)) 
    user system elapsed 
    5.06 0.00 5.06 
> 
> ag.median <- ag.median[ do.call(order, ag.median[,groupVars]), colnames(df.median)] 
> rownames(ag.median) <- 1:NROW(ag.median) 
> 
> identical(ag.median, df.median) 
[1] TRUE 
+0

'aggregate' risolve questo problema facilmente. – dnagirl

7

Giusto per riassumere alcuni dei punti dai commenti:

  1. Prima di iniziare a ottimizzare, si dovrebbe avere un senso per le prestazioni "accettabile". A seconda delle prestazioni richieste, puoi quindi essere più specifico su come migliorare il codice. Ad esempio, a una certa soglia, è necessario interrompere l'uso di R e spostarsi su un linguaggio compilato.
  2. Una volta ottenuto un tempo di esecuzione previsto, è possibile profilare il codice esistente per individuare potenziali colli di bottiglia. R ha diversi meccanismi per questo, incluso Rprof (ci sono esempi su StackOverflow se si utilizza search for [r] + rprof).
  3. plyr è progettato principalmente per la facilità d'uso, non per le prestazioni (anche se la versione recente ha avuto alcuni miglioramenti delle prestazioni). Alcune delle funzioni di base sono più veloci perché hanno un sovraccarico minore. @JDLong ha indicato a nice thread che copre alcuni di questi problemi, tra cui alcune tecniche specializzate di Hadley.
+0

Grazie per il sommario. E grazie a tutti coloro che hanno contribuito a tali informazioni utili. Ho un sacco di letture da fare! – dnagirl

2

Beh ho appena fatto un paio di semplici trasformazioni su un grande telaio di dati (i dati di baseball fissati nel pacchetto plyr) utilizzando lo standard funzioni di libreria (ad esempio, 'table', 'tapply', 'aggregate', ecc.) e la funzione plyr analoga - in ogni caso, ho trovato che plyr era significativamente più lento. Ad esempio,

> system.time(table(BB$year)) 
    user system elapsed 
    0.007 0.002 0.009 

> system.time(ddply(BB, .(year), 'nrow')) 
    user system elapsed 
    0.183 0.005 0.189 

In secondo luogo, e l'ho fatto non indagare se questo potrebbe migliorare le prestazioni nel tuo caso, ma per i frame di dati di dimensioni che si sta lavorando con la società e più grande, io uso la libreria data.table, disponibile su CRAN. E 'semplice per creare oggetti data.table così come per convertire data.frames esistenti a data.tables - basta chiamare data.table sul data.frame si desidera convertire:

dt1 = data.table(my_dataframe) 
3

Per aggiungere a Giosuè di soluzione. Se si decide di utilizzare significare invece di mediana, è possibile accelerare il calcolo altre 4 volte:

> system.time(ag.median <- aggregate(data[,dataVars], data[,groupVars], median)) 
    user system elapsed 
    3.472 0.020 3.615 
> system.time(ag.mean <- aggregate(data[,dataVars], data[,groupVars], mean)) 
    user system elapsed 
    0.936 0.008 1.006 
+1

molto interessante! Lo terro 'a mente. Sfortunatamente, questi dati devono confrontare le mediane. – dnagirl

4

L'ordine dei dati importa quando si sta calcolando mediane: se i dati sono in ordine dal più piccolo al più grande, allora il calcolo è un po 'più veloce.

x <- 1:1e6 
y <- sample(x) 
system.time(for(i in 1:1e2) median(x)) 
    user system elapsed 
    3.47 0.33 3.80 

system.time(for(i in 1:1e2) median(y)) 
    user system elapsed 
    5.03 0.26 5.29 

Per i nuovi dataset, ordinare i dati in base a una colonna appropriata quando si importa. Per i set di dati esistenti è possibile ordinarli come lavori batch (all'esterno dell'app Web).

2

Lavorare con questi dati è notevolmente più veloce con dplyr:

library(dplyr) 

system.time({ 
    data %>% 
    group_by(groupname, starttime, fPhase, fCycle) %>% 
    summarise_each(funs(median(., na.rm = TRUE)), inadist:larct) 
}) 
#> user system elapsed 
#> 0.391 0.004 0.395 

(avrete bisogno dplyr 0.2 per ottenere %>% e summarise_each)

Ciò a fronte favorevole alla plyr:

library(plyr) 
system.time({ 
    df.median <- ddply(data, .(groupname, starttime, fPhase, fCycle), 
    numcolwise(median), na.rm = TRUE) 
}) 
#> user system elapsed 
#> 0.991 0.004 0.996 

E a aggregate() (codice da @ joshua-ulrich)

groupVars <- c("groupname", "starttime", "fPhase", "fCycle") 
dataVars <- colnames(data)[ !(colnames(data) %in% c("location", groupVars))] 
system.time({ 
    ag.median <- aggregate(data[,dataVars], data[,groupVars], median) 
}) 
#> user system elapsed 
#> 0.532 0.005 0.537