2009-11-02 2 views
207

Disons que j'ai deux colonnes de données. Le premier contient des catégories telles que "Premier", "Deuxième", "Troisième", etc. Le second a des nombres qui représentent le nombre de fois que j'ai vu "Premier".Comment faire la somme d'une variable par groupe?

Par exemple:

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

Je veux trier les données par catégorie et additionner les fréquences:

Category  Frequency 
First  30 
Second  5 
Third  34 

Comment pourrais-je faire cela en R?

Répondre

234

En utilisant aggregate:

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

(intégration commentaire @thelatemail), aggregate a une interface de formule trop

aggregate(Frequency ~ Category, x, sum) 

Ou si vous voulez regrouper plusieurs colonnes, vous pouvez utiliser la Notation . (fonctionne aussi pour une colonne)

aggregate(. ~ Category, x, sum) 

ou tapply:

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

En utilisant ces données:

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 utilise le tilde pour définir des formules symboliques, pour les statistiques et d'autres fonctions. Il peut être interprété comme * "Fréquence du modèle par catégorie" * ou * "Fréquence selon la catégorie" *.Toutes les langues n'utilisent pas un opérateur spécial pour définir une fonction symbolique, comme dans R ici. Peut-être avec cette «interprétation en langage naturel» de l'opérateur tilde, cela devient plus significatif (et même intuitif). Personnellement, je trouve cette représentation symbolique meilleure que certaines alternatives plus verbeuses. – r2evans

13

Si x est une trame de données avec vos données, les commandes suivantes vont faire ce que vous voulez:

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

Juste pour ajouter une troisième option:

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

EDIT: ceci est une très vieille réponse. Maintenant, je recommanderais l'utilisation de group_by et de summary de dplyr, comme dans @docendo answer.

30

C'est un peu related to this question.

Vous pouvez aussi utiliser la par() fonction:

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

Les autres paquets (plyr, remodeler) ont l'avantage de retourner un data.frame, mais il vaut la peine d'être familier avec par () puisque c'est une fonction de base.

48

La réponse fournie par rcs fonctionne et est simple. Toutefois, si vous gérez des jeux de données plus grandes et besoin d'un coup de pouce de la performance, il est une alternative plus rapide:

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 

Comparons cela à la même chose en utilisant les données.cadre et ce qui précède ci-dessus:

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 

Et si vous voulez garder la colonne c'est la syntaxe:

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

La différence deviendra plus visible avec des ensembles de données plus importants, comme le code ci-dessous montre:

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 

Pour plusieurs agrégations, vous pouvez combiner lapply et .SD comme suit

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

+1 Mais 0,296 contre 0,059 n'est pas particulièrement impressionnant. La taille des données doit être beaucoup plus grande que 300k lignes, et avec plus de 3 groupes, pour que data.table brille. Nous allons essayer de supporter plus de 2 milliards de lignes bientôt par exemple, puisque certains utilisateurs de data.table ont 250Go de RAM et que GNU R supporte maintenant la longueur> 2^31. –

+1

True. Il se trouve que je n'ai pas toute cette RAM, et essayait simplement de fournir des preuves de la performance supérieure de data.table. Je suis sûr que la différence serait encore plus grande avec plus de données. – asieira

+0

J'ai eu 7 mil observations dplyr a pris 0,3 secondes et aggregate() a pris 22 secondes pour terminer l'opération. J'allais l'afficher sur ce sujet et tu m'as battu dessus! – zazu

114

Plus récemment, vous pouvez également utiliser le package de dplyr à cette fin:

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 

Ou, pour plusieurs colonnes de synthèse (fonctionne avec une colonne aussi):

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

Mise à jour pour dplyr> = 0,5:summarise_each a été remplacé par summarise_all, summarise_at et summarise_if famille de fonctions dans dplyr.

Ou, si vous avez plusieurs colonnes à groupe par, vous pouvez spécifier tous dans le group_by séparés par des virgules:

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

Pour plus d'informations, y compris l'opérateur %>%, voir le introduction to dplyr.

+0

À quelle vitesse est-il comparé aux solutions data.table et agrégées présentées dans d'autres réponses? – asieira

+2

@asieira, Qui est le plus rapide et quelle est la différence (ou si la différence est perceptible) dépendra toujours de la taille de vos données. Généralement, pour les grands ensembles de données, par exemple certains Go, data.table sera probablement le plus rapide. Avec une taille de données plus petite, data.table et dplyr sont souvent proches, également en fonction du nombre de groupes. Les deux données, table et dplyr seront beaucoup plus rapides que les fonctions de base, cependant (peut être 100-1000 fois plus rapide pour certaines opérations). Voir aussi [ici] (http://stackoverflow.com/questions/21435339/data-table-vs-dplyr-can-one-do-something-well-the-other-cant-or-does-poorly) –

15

Plusieurs années plus tard, juste pour ajouter une autre solution de base de R simple qui est présente pas ici pour une raison- xtabs

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

Ou si vous voulez un data.frame retour

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

Alors que je récemment devenu un converti en dplyr pour la plupart de ces types d'opérations, le paquet sqldf est toujours très agréable (et à mon humble avis plus lisible) pour certaines choses.

Voici un exemple de la façon dont cette question peut répondre avec 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

à l'aide cast au lieu de recast (note 'Frequency' est maintenant '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) 

pour obtenir:

Category (all) 
First  30 
Second 5 
Third  34