2012-08-02 11 views
8

Ho un dataframe molto grande in R e vorrei sommare due colonne per ogni valore distinto in altre colonne, ad esempio diciamo che avevamo dati di un dataframe di transazioni in vari negozi in un giorno come segueSomma per valore colonna distinto in R

shop <- data.frame('shop_id' = c(1, 1, 1, 2, 3, 3), 
    'shop_name' = c('Shop A', 'Shop A', 'Shop A', 'Shop B', 'Shop C', 'Shop C'), 
    'city' = c('London', 'London', 'London', 'Cardiff', 'Dublin', 'Dublin'), 
    'sale' = c(12, 5, 9, 15, 10, 18), 
    'profit' = c(3, 1, 3, 6, 5, 9)) 

che è:

shop_id shop_name city  sale profit 
    1  Shop A  London 12 3 
    1  Shop A  London 5 1 
    1  Shop A  London 9 3 
    2  Shop B  Cardiff 15 6 
    3  Shop C  Dublin 10 5 
    3  Shop C  Dublin 18 9 

e vorrei riassumere la vendita e profitto per ogni negozio di dare:

shop_id shop_name city  sale profit 
    1  Shop A  London 26 7 
    2  Shop B  Cardiff 15 6 
    3  Shop C  Dublin 28 14 

Attualmente sto usando il seguente codice per fare questo:

shop_day <-ddply(shop, "shop_id", transform, sale=sum(sale), profit=sum(profit)) 
shop_day <- subset(shop_day, !duplicated(shop_id)) 

che funziona assolutamente bene, ma come ho detto il mio dataframe è di grandi dimensioni (140.000 righe, 37 colonne e circa 100.000 righe uniche che voglio riassumere) e il mio codice impiega anni per essere eseguito e poi alla fine dice che ha esaurito la memoria.

Qualcuno sa del modo più efficiente per farlo.

Grazie in anticipo!

+2

... Sento una risposta 'data.table' in arrivo ... –

risposta

13

** Tabella obbligatorio dei dati risposta **

> library(data.table) 
data.table 1.8.0 For help type: help("data.table") 
> shop.dt <- data.table(shop) 
> shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id'] 
    shop_id sale profit 
[1,]  1 26  7 
[2,]  2 15  6 
[3,]  3 28  14 
> 

che suona bello e buono fino a quando le cose si fanno più grandi ...

shop <- data.frame(shop_id = letters[1:10], profit=rnorm(1e7), sale=rnorm(1e7)) 
shop.dt <- data.table(shop) 

> system.time(ddply(shop, .(shop_id), summarise, sale=sum(sale), profit=sum(profit))) 
    user system elapsed 
    4.156 1.324 5.514 
> system.time(shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id']) 
    user system elapsed 
    0.728 0.108 0.840 
> 

Si ottiene un aumento di velocità aggiuntivi se si crea la data.table con una chiave:

shop.dt <- data.table(shop, key='shop_id') 

> system.time(shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id']) 
    user system elapsed 
    0.252 0.084 0.336 
> 
+0

Nota che Justin sta usando' sumarise' invece 'transform' nella sua chiamata' ddply'; questo cambiamento è probabilmente sufficiente per far funzionare il codice senza errori di memoria, anche se altre soluzioni sono sicuramente più veloci. – Aaron

+0

@Aaron Grazie! Ho lasciato questa spiegazione poiché c'era una risposta precedente che la spiegava. Comunque è stato cancellato! – Justin

+0

Grazie Justin, molto più veloce. Un'altra domanda veloce, c'è un modo per mantenere le altre colonne (ad esempio shop_name, città) nella tabella dei dati finali? Posso tornare al dataframe iniziale per ottenere questo, ma sarebbe più ordinato se ci fosse un modo per farlo all'interno della query iniziale. – user1165199

3

Ecco come utilizzare di base R per velocizzare le operazioni in questo modo:

idx <- split(1:nrow(shop), shop$shop_id) 
a2 <- data.frame(shop_id=sapply(idx, function(i) shop$shop_id[i[1]]), 
       sale=sapply(idx, function(i) sum(shop$sale[i])), 
       profit=sapply(idx, function(i) sum(shop$profit[i]))) 

Il tempo si riduce a 0,75 sec contro 5,70 sec per la versione riepilogativa ddply sul sistema.

+0

Se ho molte colonne come la vendita e il profitto nell'esempio sopra che voglio sommare, è possibile chiamare una singola funzione per combinare la terza e la quarta riga nel codice sopra in una singola riga. – discipulus

+1

Non proprio usando questo metodo esatto, ma ci sono modi per farlo. Inizia una nuova domanda con un esempio minimamente riproducibile e riceverai molti suggerimenti. – Aaron