2009-11-02 6 views
207

Diciamo che ho due colonne di dati. Il primo contiene categorie come "Primo", "Secondo", "Terzo", ecc. Il secondo ha numeri che rappresentano il numero di volte in cui ho visto "Primo".Come sommare una variabile per gruppo?

Ad esempio:

Category  Frequency 
First  10 
First  15 
First  5 
Second  2 
Third  14 
Third  20 
Second  3 

voglio ordinare i dati per Categoria e sommare i Frequenze:

Category  Frequency 
First  30 
Second  5 
Third  34 

Come dovrei fare questo in R?

risposta

234

Utilizzando aggregate:

aggregate(x$Frequency, by=list(Category=x$Category), FUN=sum) 
    Category x 
1 First 30 
2 Second 5 
3 Third 34 

(embedding commento @thelatemail), aggregate ha un'interfaccia formula troppo

aggregate(Frequency ~ Category, x, sum) 

Oppure, se si vuole aggregare più colonne, è possibile utilizzare la Notazione . (funziona anche per una colonna)

aggregate(. ~ Category, x, sum) 

o tapply:

tapply(x$Frequency, x$Category, FUN=sum) 
First Second Third 
    30  5  34 

Utilizzando questi dati:

x <- data.frame(Category=factor(c("First", "First", "First", "Second", 
             "Third", "Third", "Second")), 
        Frequency=c(10,15,5,2,14,20,3)) 
+2

@AndrewMcKinlay, R usa la tilde per definire formule simboliche, per statistiche e altre funzioni. Può essere interpretato come * "modello di frequenza per categoria" * o * "frequenza in base alla categoria" *.Non tutte le lingue usano un operatore speciale per definire una funzione simbolica, come fatto in R qui. Forse con quella "interpretazione in linguaggio naturale" dell'operatore di tilde, diventa più significativo (e persino intuitivo). Personalmente trovo questa rappresentazione simbolica delle formule migliore di alcune delle alternative più prolisse. – r2evans

13

Se x è un dataframe con i tuoi dati, allora quanto segue lo fare quello che vuoi:

require(reshape) 
recast(x, Category ~ ., fun.aggregate=sum) 
19
library(plyr) 
ddply(tbl, .(Category), summarise, sum = sum(Frequency)) 
15

solo per aggiungere una terza opzione:

require(doBy) 
summaryBy(Frequency~Category, data=yourdataframe, FUN=sum) 

EDIT: questo è una risposta molto vecchia. Ora consiglierei l'uso di group_by e riepilogare da dplyr, come nella risposta @docendo.

30

questo è un po related to this question.

È anche possibile utilizzare il da() funzione:

x2 <- by(x$Frequency, x$Category, sum) 
do.call(rbind,as.list(x2)) 

Gli altri pacchetti (plyr, rimodellare) hanno il vantaggio di restituire un data.frame, ma vale la pena avere familiarità con da () poiché è una funzione di base.

48

La risposta fornita da rcs funziona ed è semplice. Tuttavia, se si sta gestendo set di dati più grandi e hanno bisogno di un incremento delle prestazioni v'è una alternativa più veloce:

library(data.table) 
data = data.table(Category=c("First","First","First","Second","Third", "Third", "Second"), 
        Frequency=c(10,15,5,2,14,20,3)) 
data[, sum(Frequency), by = Category] 
# Category V1 
# 1: First 30 
# 2: Second 5 
# 3: Third 34 
system.time(data[, sum(Frequency), by = Category]) 
# user system elapsed 
# 0.008  0.001  0.009 

Mettiamo a confronto che, per la stessa cosa utilizzando i dati.telaio e il sopra sopra:

data = data.frame(Category=c("First","First","First","Second","Third", "Third", "Second"), 
        Frequency=c(10,15,5,2,14,20,3)) 
system.time(aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum)) 
# user system elapsed 
# 0.008  0.000  0.015 

E se si desidera mantenere la colonna questa è la sintassi:

data[,list(Frequency=sum(Frequency)),by=Category] 
# Category Frequency 
# 1: First  30 
# 2: Second   5 
# 3: Third  34 

La differenza diventerà più evidente con set di dati più grandi, come il codice qui sotto dimostra:

data = data.table(Category=rep(c("First", "Second", "Third"), 100000), 
        Frequency=rnorm(100000)) 
system.time(data[,sum(Frequency),by=Category]) 
# user system elapsed 
# 0.055  0.004  0.059 
data = data.frame(Category=rep(c("First", "Second", "Third"), 100000), 
        Frequency=rnorm(100000)) 
system.time(aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum)) 
# user system elapsed 
# 0.287  0.010  0.296 

Per più aggregazioni, è possibile combinare lapply e 0.123.880,366 milacome segue

data[, lapply(.SD, sum), by = Category] 
# Category Frequency 
# 1: First  30 
# 2: Second   5 
# 3: Third  34 
+7

+1 Ma 0,296 contro 0,059 non è particolarmente impressionante. La dimensione dei dati deve essere molto più grande di 300k righe e con più di 3 gruppi, affinché data.table risplenda. Ad esempio, proveremo a supportare più di 2 miliardi di righe, dato che alcuni utenti data.table hanno 250 GB di RAM e GNU R ora supporta la lunghezza> 2^31. –

+1

Vero. Si scopre che non ho tutta quella RAM, e stavo semplicemente cercando di fornire alcune prove delle prestazioni superiori di data.table. Sono sicuro che la differenza sarebbe ancora più grande con più dati. – asieira

+0

Ho avuto 7 mil osservazioni dplyr ha preso 3 secondi e aggregato() ha impiegato 22 secondi per completare l'operazione. Stavo per postarlo su questo argomento e tu mi hai battuto! – zazu

114

Più di recente, è anche possibile utilizzare il pacchetto dplyr a tal fine:

library(dplyr) 
x %>% 
    group_by(Category) %>% 
    summarise(Frequency = sum(Frequency)) 

#Source: local data frame [3 x 2] 
# 
# Category Frequency 
#1 First  30 
#2 Second   5 
#3 Third  34 

Oppure, per più colonne di riepilogo (funziona con una colonna troppo):

x %>% 
    group_by(Category) %>% 
    summarise_each(funs(sum)) 

Aggiornamento per dplyr> = 0,5:summarise_each è stato sostituito da summarise_all, summarise_at e summarise_if famiglia di funzioni in dplyr.

Oppure, se si dispone di più colonne Group by è possibile specificare tutti loro nel group_by separati da virgole:

mtcars %>% 
    group_by(cyl, gear) %>%       # multiple group columns 
    summarise(max_hp = max(hp), mean_mpg = mean(mpg)) # multiple summary columns 

Per ulteriori informazioni, tra cui l'operatore %>%, vedere la introduction to dplyr.

+0

Quanto è veloce rispetto alle alternative data.table e aggregate presentate in altre risposte? – asieira

+2

@asieira, che è il più veloce e quanto grande è la differenza (o se la differenza è evidente) dipenderà sempre dalla dimensione dei dati. In genere, per i set di dati di grandi dimensioni, ad esempio alcuni GB, data.table sarà probabilmente il più veloce. In caso di dimensioni ridotte dei dati, data.table e dplyr sono spesso chiusi, anche in base al numero di gruppi. Sia data, table che dplyr saranno molto più veloci delle funzioni base, tuttavia (può essere 100-1000 volte più veloce per alcune operazioni). Vedi anche [qui] (http://stackoverflow.com/questions/21435339/data-table-vs-dplyr-can-one-do-something-well-the-other-cant-or-does-poorly) –

15

Diversi anni dopo, solo per aggiungere un'altra soluzione semplice base di R che non è presente qui per qualche ragionevole xtabs

xtabs(Frequency ~ Category, df) 
# Category 
# First Second Third 
# 30  5  34 

O se vuole un data.frame indietro

as.data.frame(xtabs(Frequency ~ Category, df)) 
# Category Freq 
# 1 First 30 
# 2 Second 5 
# 3 Third 34 
14

Mentre ho recentemente convertito in dplyr per la maggior parte di questi tipi di operazioni, il pacchetto sqldf è ancora molto bello (e IMHO più leggibile) per alcune cose.

Ecco un esempio di come a questa domanda si può rispondere con sqldf

x <- data.frame(Category=factor(c("First", "First", "First", "Second", 
            "Third", "Third", "Second")), 
       Frequency=c(10,15,5,2,14,20,3)) 

sqldf("select 
      Category 
      ,sum(Frequency) as Frequency 
     from x 
     group by 
      Category") 

## Category Frequency 
## 1 First  30 
## 2 Second   5 
## 3 Third  34 
0

utilizzando cast invece di recast (nota 'Frequency' è ora 'value')

df <- data.frame(Category = c("First","First","First","Second","Third","Third","Second") 
        , value = c(10,15,5,2,14,20,3)) 

install.packages("reshape") 

result<-cast(df, Category ~ . ,fun.aggregate=sum) 

per ottenere:

Category (all) 
First  30 
Second 5 
Third  34