2012-07-18 16 views
59

Penso che sto usando plyr in modo errato. Qualcuno potrebbe dirmi se questo è un codice plyr 'efficiente'?Perché il film è così lento?

require(plyr) 
plyr <- function(dd) ddply(dd, .(price), summarise, ss=sum(volume)) 

Un po 'di contesto: ho un paio di problemi di grande aggregazione e ho notato che erano ognuno prendendo po' di tempo. Nel tentativo di risolvere i problemi, mi sono interessato allo svolgimento delle varie procedure di aggregazione in R.

Ho provato alcuni metodi di aggregazione e mi sono trovato ad aspettare tutto il giorno.

Quando finalmente ho ottenuto risultati, ho scoperto un enorme divario tra il metodo plyr e gli altri - il che mi fa pensare che ho fatto qualcosa di sbagliato.

Ho eseguito il seguente codice (ho pensato di controllare il nuovo pacchetto di dataframe mentre ero a lui):

require(plyr) 
require(data.table) 
require(dataframe) 
require(rbenchmark) 
require(xts) 

plyr <- function(dd) ddply(dd, .(price), summarise, ss=sum(volume)) 
t.apply <- function(dd) unlist(tapply(dd$volume, dd$price, sum)) 
t.apply.x <- function(dd) unlist(tapply(dd[,2], dd[,1], sum)) 
l.apply <- function(dd) unlist(lapply(split(dd$volume, dd$price), sum)) 
l.apply.x <- function(dd) unlist(lapply(split(dd[,2], dd[,1]), sum)) 
b.y <- function(dd) unlist(by(dd$volume, dd$price, sum)) 
b.y.x <- function(dd) unlist(by(dd[,2], dd[,1], sum)) 
agg <- function(dd) aggregate(dd$volume, list(dd$price), sum) 
agg.x <- function(dd) aggregate(dd[,2], list(dd[,1]), sum) 
dtd <- function(dd) dd[, sum(volume), by=(price)] 

obs <- c(5e1, 5e2, 5e3, 5e4, 5e5, 5e6, 5e6, 5e7, 5e8) 
timS <- timeBasedSeq('20110101 083000/20120101 083000') 

bmkRL <- list(NULL) 

for (i in 1:5){ 
    tt <- timS[1:obs[i]] 

    for (j in 1:8){ 
    pxl <- seq(0.9, 1.1, by= (1.1 - 0.9)/floor(obs[i]/(11-j))) 
    px <- sample(pxl, length(tt), replace=TRUE) 
    vol <- rnorm(length(tt), 1000, 100) 

    d.df <- base::data.frame(time=tt, price=px, volume=vol) 
    d.dfp <- dataframe::data.frame(time=tt, price=px, volume=vol) 
    d.matrix <- as.matrix(d.df[,-1]) 
    d.dt <- data.table(d.df) 

    listLabel <- paste('i=',i, 'j=',j) 

    bmkRL[[listLabel]] <- benchmark(plyr(d.df), plyr(d.dfp), t.apply(d.df),  
         t.apply(d.dfp), t.apply.x(d.matrix), 
         l.apply(d.df), l.apply(d.dfp), l.apply.x(d.matrix), 
         b.y(d.df), b.y(d.dfp), b.y.x(d.matrix), agg(d.df), 
         agg(d.dfp), agg.x(d.matrix), dtd(d.dt), 
      columns =c('test', 'elapsed', 'relative'), 
      replications = 10, 
      order = 'elapsed') 
    } 
} 

Il test è stato supposto per controllare fino a 5E8, ma ci sono voluti troppo a lungo - principalmente a causa di plyr. Il 5e5 la tabella finale mostra il problema:

$`i= 5 j= 8` 
        test elapsed relative 
15   dtd(d.dt) 4.156 1.000000 
6  l.apply(d.df) 15.687 3.774543 
7  l.apply(d.dfp) 16.066 3.865736 
8 l.apply.x(d.matrix) 16.659 4.008422 
4  t.apply(d.dfp) 21.387 5.146054 
3  t.apply(d.df) 21.488 5.170356 
5 t.apply.x(d.matrix) 22.014 5.296920 
13   agg(d.dfp) 32.254 7.760828 
14  agg.x(d.matrix) 32.435 7.804379 
12   agg(d.df) 32.593 7.842397 
10   b.y(d.dfp) 98.006 23.581809 
11  b.y.x(d.matrix) 98.134 23.612608 
9   b.y(d.df) 98.337 23.661453 
1   plyr(d.df) 9384.135 2257.972810 
2   plyr(d.dfp) 9384.448 2258.048123 

È giusto? Perché plyr 2250x è più lento di data.table? E perché non usare il nuovo pacchetto di frame dati fa la differenza?

Le informazioni di sessione è:

> sessionInfo() 
R version 2.15.1 (2012-06-22) 
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit) 

locale: 
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8 

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base  

other attached packages: 
[1] xts_0.8-6  zoo_1.7-7  rbenchmark_0.3 dataframe_2.5 data.table_1.8.1  plyr_1.7.1  

loaded via a namespace (and not attached): 
[1] grid_2.15.1 lattice_0.20-6 tools_2.15.1 
+3

Per relativamente semplici problemi di manipolazione dei dati/aggregazione, ho trovato tabella di dati di essere estremamente veloce. Se può farlo, non sono affatto sorpreso che sia il chiaro vincitore. Non ho familiarità con 'plyr' per commentarlo. – Joshua

+1

Hai esaminato la documentazione di 'plyr' e' data.table'? Se ricordo male, 'plyr' funziona con base-' R' 'data.frame's. 'data.table' usa una rappresentazione completamente diversa, usando colonne con chiave e un ordinamento digitale efficiente. In questo modo è molto più simile a un database. –

+0

ho dato un'occhiata - ma non riuscivo a capirlo. plyr è più di un po 'più lento ... la famiglia applica, agg, e sono molto veloci - e sono di base. è per questo che ho pensato che dovevo fare qualche errore da principiante con plyr. – ricardo

risposta

51

Perché è così lento? Un po 'di ricerca trova un post gruppo di posta da un agosto 2011 in cui @hadley, il pacchetto autore, states

Questo è un inconveniente del modo in cui ddply lavora sempre con dati cornici. Sarà un po 'più veloce se si utilizza il riepilogo invece di data.frame (perché data.frame è molto lento), ma sto ancora pensando a su come superare questa limitazione fondamentale dell'approccio ddply .


Come per essere efficiente codice plyr Non lo sapevo neanche. Dopo una serie di test param e benchmark sembra che possiamo fare di meglio.

Il summarize() nel comando è una funzione di supporto semplice, pura e semplice. Possiamo sostituirlo con la nostra funzione di somma poiché non sta aiutando con qualcosa che non è già semplice e gli argomenti .data e .(price) possono essere resi più espliciti. Il risultato è

ddply(dd[, 2:3], ~price, function(x) sum(x$volume)) 

Il summarize può sembrare bello, ma semplicemente non è più veloce di una semplice chiamata di funzione. Ha senso; basta guardare la nostra piccola funzione rispetto allo code per summarize. Eseguendo i benchmark con la formula riveduta si ottiene un guadagno notevole. Non prendere ciò per dire che hai usato plyr in modo errato, non è così, non è efficiente; niente che tu possa fare con esso lo renderà veloce come altre opzioni.

A mio parere la funzione ottimizzata puzza ancora in quanto non è chiara e deve essere analizzata mentalmente con ancora ridicolmente lento rispetto a data.table (anche con un guadagno del 60%).


Nella stessa thread cui sopra, anche la lentezza di plyr, un progetto plyr2 è menzionato. Dal momento della risposta originale alla domanda l'autore plyr ha rilasciato dplyr come successore di plyr. Sebbene sia plyr che dplyr siano etichettati come strumenti di manipolazione dei dati e il vostro interesse principale dichiarato è l'aggregazione, potreste comunque essere interessati ai vostri risultati di benchmark del nuovo pacchetto per il confronto poiché ha un backend rielaborato per migliorare le prestazioni.

plyr_Original <- function(dd) ddply(dd, .(price), summarise, ss=sum(volume)) 
plyr_Optimized <- function(dd) ddply(dd[, 2:3], ~price, function(x) sum(x$volume)) 

dplyr <- function(dd) dd %.% group_by(price) %.% summarize(sum(volume))  

data_table <- function(dd) dd[, sum(volume), keyby=price] 

Il pacchetto dataframe è stato rimosso dal CRAN e successivamente dai test, insieme con le versioni funzione di matrice.

Ecco i risultati dei benchmark i=5, j=8:

$`obs= 500,000 unique prices= 158,286 reps= 5` 
        test elapsed relative 
9  data_table(d.dt) 0.074 1.000 
4   dplyr(d.dt) 0.133 1.797 
3   dplyr(d.df) 1.832 24.757 
6  l.apply(d.df) 5.049 68.230 
5  t.apply(d.df) 8.078 109.162 
8   agg(d.df) 11.822 159.757 
7   b.y(d.df) 48.569 656.338 
2 plyr_Optimized(d.df) 148.030 2000.405 
1 plyr_Original(d.df) 401.890 5430.946 

Non c'è dubbio che l'ottimizzazione ha aiutato un po '. Dai un'occhiata alle funzioni d.df; semplicemente non possono competere.

Per una piccola prospettiva sulla lentezza della struttura data.frame qui ci sono micro-benchmark dei tempi di aggregazione di data_table e dplyr utilizzando un set di dati di test più grande (i=8,j=8).

$`obs= 50,000,000 unique prices= 15,836,476 reps= 5` 
Unit: seconds 
      expr min  lq median  uq max neval 
data_table(d.dt) 1.190 1.193 1.198 1.460 1.574 10 
     dplyr(d.dt) 2.346 2.434 2.542 2.942 9.856 10 
     dplyr(d.df) 66.238 66.688 67.436 69.226 86.641 10 

Il data.frame è ancora lasciato nella polvere. Non solo, ma ecco lo system.time trascorso per popolare le strutture di dati con i dati del test:

`d.df` (data.frame) 3.181 seconds. 
`d.dt` (data.table) 0.418 seconds. 

Sia la creazione e l'aggregazione del data.frame è più lento di quello del data.table.

Lavorare con il data.frame in R è più lento di alcune alternative, ma come i benchmark mostrano il costruito nelle funzioni di R plyr saltare fuori dall'acqua. Anche la gestione di data.frame come fa dplyr, che migliora con i built-in, non fornisce una velocità ottimale; dove data.table è più veloce sia nella creazione e aggregazione e data.table fa quello che fa mentre lavora con/su data.frames.

Alla fine ...

Plyr è lento a causa del modo esso funziona con e gestisce la manipolazione data.frame.

[punt: vedere i commenti alla domanda iniziale].


## R version 3.0.2 (2013-09-25) 
## Platform: x86_64-pc-linux-gnu (64-bit) 
## 
## attached base packages: 
## [1] stats  graphics grDevices utils  datasets methods base  
## 
## other attached packages: 
## [1] microbenchmark_1.3-0 rbenchmark_1.0.0  xts_0.9-7   
## [4] zoo_1.7-11   data.table_1.9.2  dplyr_0.1.2   
## [7] plyr_1.8.1   knitr_1.5.22   
## 
## loaded via a namespace (and not attached): 
## [1] assertthat_0.1 evaluate_0.5.2 formatR_0.10.4 grid_3.0.2  
## [5] lattice_0.20-27 Rcpp_0.11.0  reshape2_1.2.2 stringr_0.6.2 
## [9] tools_3.0.2 

Data-Generating gist .rmd

+0

+1. buoni suggerimenti grazie compagno. Sto rieseguendo i test con il tuo codice 'plyr' e' dc' suggerito oggi. posterò una risposta quando avranno finito. Ho deciso di eliminare il bit della matrice, per accelerare un po 'le cose (poiché spostare il df in una matrice non sembrava aggiungere molto). – ricardo

+0

Ho accettato questa risposta, poiché sembra che sia il punto che otterremo, a meno che Hadley non voglia controllare e spiegare il funzionamento interno di 'plyr'. – ricardo

+3

@Thell Dato che hai menzionato la facilità d'uso, ho aggiunto ciò che 'dtd()' in effetti è affiancato, iiuc. Come qualcuno può dire che non è facile mi batte. Ma dplyr utilizzando il back-end data.table è più lento di usare direttamente data.table, quindi? Come mai? –

Problemi correlati