Cómo sumar una variable por grupo
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?
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), aggregate
tambié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))
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 .
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 lapply
y .SD
de la siguiente manera
data[, lapply(.SD, sum), by = Category]
# Category Frequency
# 1: First 30
# 2: Second 5
# 3: Third 34
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.