Cómo sumar una variable por grupo

Resuelto boo-urns asked hace 54 años • 18 respuestas

Tengo un marco de datos con dos columnas. La primera columna contiene categorías como "Primera", "Segunda", "Tercera" y la segunda columna tiene números que representan la cantidad de veces que vi los grupos específicos de "Categoría".

Por ejemplo:

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

Quiero ordenar los datos por Categoría y sumar todas las Frecuencias:

Category     Frequency
First        30
Second       5
Third        34

¿Cómo haría esto en R?

boo-urns avatar Jan 01 '70 08:01 boo-urns
Aceptado

Usando aggregate:

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

En el ejemplo anterior, se pueden especificar varias dimensiones en el archivo list. Se pueden incorporar múltiples métricas agregadas del mismo tipo de datos a través de cbind:

aggregate(cbind(x$Frequency, x$Metric2, x$Metric3) ...

(incrustando el comentario de @thelatemail), aggregatetambién tiene una interfaz de fórmula

aggregate(Frequency ~ Category, x, sum)

O si desea agregar varias columnas, puede usar la .notación (también funciona para una columna)

aggregate(. ~ Category, x, sum)

o tapply:

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

Usando estos datos:

x <- data.frame(Category=factor(c("First", "First", "First", "Second",
                                      "Third", "Third", "Second")), 
                    Frequency=c(10,15,5,2,14,20,3))
rcs avatar Nov 02 '2009 12:11 rcs

También puedes usar el paquete dplyr para ese propósito:

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

O, para varias columnas de resumen (también funciona con una columna):

x %>% 
  group_by(Category) %>% 
  summarise(across(everything(), sum))

Aquí hay algunos ejemplos más de cómo resumir datos por grupo usando funciones dplyr usando el conjunto de datos integrado mtcars:

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

# summarise all columns except grouping columns using "sum" 
mtcars %>% 
  group_by(cyl) %>% 
  summarise(across(everything(), sum))

# summarise all columns except grouping columns using "sum" and "mean"
mtcars %>% 
  group_by(cyl) %>% 
  summarise(across(everything(), list(mean = mean, sum = sum)))

# multiple grouping columns
mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise(across(everything(), list(mean = mean, sum = sum)))

# summarise specific variables, not all
mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise(across(c(qsec, mpg, wt), list(mean = mean, sum = sum)))

# summarise specific variables (numeric columns except grouping columns)
mtcars %>% 
  group_by(gear) %>% 
  summarise(across(where(is.numeric), list(mean = mean, sum = sum)))

Para obtener más información, incluido el %>%operador, consulte la introducción a dplyr .

talat avatar Dec 03 '2014 08:12 talat

La respuesta proporcionada por rcs funciona y es simple. Sin embargo, si maneja conjuntos de datos más grandes y necesita mejorar el rendimiento, existe una alternativa más rápida:

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 

Comparemos eso con lo mismo usando data.frame y lo anterior:

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 

Y si quieres mantener la columna esta es la sintaxis:

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

La diferencia será más notable con conjuntos de datos más grandes, como lo demuestra el siguiente código:

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 

Para múltiples agregaciones, puede combinar lapplyy .SDde la siguiente manera

data[, lapply(.SD, sum), by = Category]
#    Category Frequency
# 1:    First        30
# 2:   Second         5
# 3:    Third        34
asieira avatar Sep 08 '2013 17:09 asieira

También puedes usar la función by() :

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

Esos otros paquetes (plyr, reshape) tienen la ventaja de devolver un data.frame, pero vale la pena familiarizarse con by() ya que es una función base.

Shane avatar Nov 02 '2009 17:11 Shane