Cómo sumar una variable por grupo
Pregunta
Vamos a decir que tengo dos columnas de datos. El primero contiene categorías tales como "primero", "segundo", "tercero", etc. La segunda tiene números que representan el número de veces que la vi "primero".
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 las frecuencias:
Category Frequency
First 30
Second 5
Third 34
¿Cómo voy a hacer esto en R?
Solución
El uso de 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, dimensiones múltiples se pueden especificar en el list
. Múltiples métricas agregadas del mismo tipo de datos pueden ser incorporados a través de cbind
:
aggregate(cbind(x$Frequency, x$Metric2, x$Metric3) ...
(incrustación comentario @thelatemail), aggregate
tiene una interfaz de fórmula demasiado
aggregate(Frequency ~ Category, x, sum)
O si desea agregar múltiples columnas, se puede usar la notación .
(que funciona para una columna también)
aggregate(. ~ Category, x, sum)
o tapply
:
tapply(x$Frequency, x$Category, FUN=sum)
First Second Third
30 5 34
El uso de estos datos:
x <- data.frame(Category=factor(c("First", "First", "First", "Second",
"Third", "Third", "Second")),
Frequency=c(10,15,5,2,14,20,3))
Otros consejos
Más recientemente, también se puede utilizar el dplyr paquete 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 las columnas Resumen múltiple (trabaja con una columna también):
x %>%
group_by(Category) %>%
summarise_each(funs(sum))
Actualización para dplyr> = 0,5:. summarise_each
ha sido reemplazado por summarise_all
, summarise_at
y familiares summarise_if
de funciones en dplyr
O, si usted tiene varias columnas para agrupar por, puede especificar todos ellos en el group_by
separados por comas:
mtcars %>%
group_by(cyl, gear) %>% # multiple group columns
summarise(max_hp = max(hp), mean_mpg = mean(mpg)) # multiple summary columns
Para obtener más información, incluyendo el operador %>%
, ver la introducción rel="noreferrer"> href="https://cran.r-project.org/web/packages/dplyr/vignettes/dplyr.html" .
La respuesta proporcionada por RCS funciona y es simple. Sin embargo, si usted está manejando grandes conjuntos de datos y la necesidad de un aumento de rendimiento no es 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
Vamos a comparar eso a lo mismo usando hoja.de.datos y por encima de todo 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 desea 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 grandes conjuntos de datos, como el código siguiente muestra:
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, se pueden 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
Esto es algo relacionado con esta pregunta.
Puede también usar simplemente () función:
x2 <- by(x$Frequency, x$Category, sum)
do.call(rbind,as.list(x2))
Los otros paquetes (plyr, remodelar) tienen la ventaja de devolver un hoja.de.datos, pero vale la pena estar familiarizado con por (), ya que es una función de base.
library(plyr)
ddply(tbl, .(Category), summarise, sum = sum(Frequency))
Varios años más tarde, sólo para añadir otra solución base de R sencilla que no está presente aquí por algún xtabs
razonable
xtabs(Frequency ~ Category, df)
# Category
# First Second Third
# 30 5 34
O si quieres un data.frame
volver
as.data.frame(xtabs(Frequency ~ Category, df))
# Category Freq
# 1 First 30
# 2 Second 5
# 3 Third 34
Si x
es una trama de datos con sus datos, entonces lo siguiente hace lo que quiere:
require(reshape)
recast(x, Category ~ ., fun.aggregate=sum)
Mientras que recientemente me he convertido en un convertido al dplyr
para la mayoría de estos tipos de operaciones, el paquete sqldf
sigue siendo muy agradable (y en mi humilde opinión más legible) para algunas cosas.
A continuación se muestra un ejemplo de cómo esta pregunta puede responderse 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
Sólo para añadir una tercera opción:
require(doBy)
summaryBy(Frequency~Category, data=yourdataframe, FUN=sum)
EDIT: se trata de una respuesta muy antigua. Ahora me gustaría recomendar el uso de group_by
y summarise
de dplyr
, como en @docendo respuesta.
El dplyr::tally()
recientemente añadido ahora hace que esto sea más fácil que nunca:
tally(x, Category)
Category n
First 30
Second 5
Third 34
ave
muy útil (y eficiente) cuando es necesario aplicar diferentes funciones de agregación en diferentes columnas (y debe / quiere meter en la base R):
por ejemplo.
Dada esta entrada:
DF <-
data.frame(Categ1=factor(c('A','A','B','B','A','B','A')),
Categ2=factor(c('X','Y','X','X','X','Y','Y')),
Samples=c(1,2,4,3,5,6,7),
Freq=c(10,30,45,55,80,65,50))
> DF
Categ1 Categ2 Samples Freq
1 A X 1 10
2 A Y 2 30
3 B X 4 45
4 B X 3 55
5 A X 5 80
6 B Y 6 65
7 A Y 7 50
queremos agrupar por Categ1
y Categ2
y calcular la suma de Samples
y media de Freq
.
Aquí hay una solución posible usando ave
:
# create a copy of DF (only the grouping columns)
DF2 <- DF[,c('Categ1','Categ2')]
# add sum of Samples by Categ1,Categ2 to DF2
# (ave repeats the sum of the group for each row in the same group)
DF2$GroupTotSamples <- ave(DF$Samples,DF2,FUN=sum)
# add mean of Freq by Categ1,Categ2 to DF2
# (ave repeats the mean of the group for each row in the same group)
DF2$GroupAvgFreq <- ave(DF$Freq,DF2,FUN=mean)
# remove the duplicates (keep only one row for each group)
DF2 <- DF2[!duplicated(DF2),]
Resultados:
> DF2
Categ1 Categ2 GroupTotSamples GroupAvgFreq
1 A X 6 45
2 A Y 9 40
3 B X 7 50
6 B Y 6 65
Se puede utilizar la función de group.sum
paquete Rfast .
Category <- Rfast::as_integer(Category,result.sort=FALSE) # convert character to numeric. R's as.numeric produce NAs.
result <- Rfast::group.sum(Frequency,Category)
names(result) <- Rfast::Sort(unique(Category)
# 30 5 34
Rfast tiene muchas funciones de grupo y group.sum
es uno de ellos.
usando cast
en lugar de recast
(nota 'Frequency'
es ahora '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)
para obtener:
Category (all)
First 30
Second 5
Third 34