Comment additionner une variable par groupe?

disons que j'ai deux colonnes de données. La première contient les catégories telles que "Première", "Deuxième", "Troisième", etc. Le second a des nombres qui représentent le nombre de fois où j'ai vu "premier".

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 faire la somme des fréquences:

Category     Frequency
First        30
Second       5
Third        34

comment je ferais ça en R?

247
demandé sur David Arenburg 2009-11-02 12:01:28

10 réponses

utilisant aggregate :

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

(embedding @thelatemail comment), aggregate a une interface de formule trop

aggregate(Frequency ~ Category, x, sum)

ou si vous voulez agréger plusieurs colonnes, vous pouvez utiliser la notation . (fonctionne pour une colonne aussi)

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))
274
répondu rcs 2017-02-03 23:06:55

Plus récemment, vous pouvez également utiliser la fonction dplyr package à cet effet:

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 les plusieurs colonnes de synthèse (fonctionne avec une colonne trop):

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 en dplyr.

Ou, si vous avez plusieurs colonnes pour grouper par, vous pouvez tous les spécifier dans le group_by séparé 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'information, y compris l'opérateur %>% , voir la introduction à dplyr .

141
répondu docendo discimus 2017-03-02 15:32:13

La réponse fournie par rcs fonctionne et est simple. Cependant, si vous manipulez des ensembles de données plus grands et avez besoin d'une augmentation de la performance, Il ya 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 des données.image 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 sera de plus en plus perceptible avec les ensembles de données plus le code ci-dessous dé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 les agrégations multiples, 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
56
répondu asieira 2017-08-09 12:24:08

c'est quelque peu lié à cette question .

vous pouvez également utiliser le par () fonction:

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

ces autres paquets (plyr, reshape) ont l'avantage de renvoyer une donnée.frame, mais cela vaut la peine d'être familier avec by() car c'est une fonction de base.

33
répondu Shane 2017-05-23 12:10:44
library(plyr)
ddply(tbl, .(Category), summarise, sum = sum(Frequency))
21
répondu learnr 2009-11-02 09:44:34

plusieurs années plus tard, juste pour ajouter une autre solution de base simple R qui n'est pas présent ici pour une raison quelconque - xtabs

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

ou si vous voulez un data.frame arrière

as.data.frame(xtabs(Frequency ~ Category, df))
#   Category Freq
# 1    First   30
# 2   Second    5
# 3    Third   34
18
répondu David Arenburg 2015-09-10 13:36:01

juste pour ajouter une troisième option:

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

EDIT: c'est une réponse très ancienne. Maintenant, je recommande l'utilisation de group_by et de résumer de dplyr, comme dans la réponse de @docendo.

15
répondu dalloliogm 2015-08-24 15:24:20

alors que je suis récemment devenu un converti en dplyr pour la plupart de ces types d'opérations, le paquet sqldf est encore vraiment agréable (et IMHO plus lisible) pour certaines choses.

voici un exemple de réponse à cette question 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
14
répondu joemienko 2016-05-17 12:12:56

si x est une dataframe avec vos données, alors ce qui suit fera ce que vous voulez:

require(reshape)
recast(x, Category ~ ., fun.aggregate=sum)
13
répondu Rob Hyndman 2009-11-02 09:44:03

utilisant 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
1
répondu Grant Shannon 2018-02-25 15:43:56