Contar el número de filas dentro de cada grupo

121

Tengo un marco de datos y me gustaría contar el número de filas dentro de cada grupo. Normalmente utilizo la aggregatefunción para sumar datos de la siguiente manera:

df2 <- aggregate(x ~ Year + Month, data = df1, sum)

Ahora, me gustaría contar las observaciones, pero parece que no puedo encontrar el argumento adecuado para FUN. Intuitivamente, pensé que sería lo siguiente:

df2 <- aggregate(x ~ Year + Month, data = df1, count)

Pero no tuve tanta suerte.

¿Algunas ideas?


Algunos datos de juguetes:

set.seed(2)
df1 <- data.frame(x = 1:20,
                  Year = sample(2012:2014, 20, replace = TRUE),
                  Month = sample(month.abb[1:3], 20, replace = TRUE))
MikeTP
fuente
17
nrow, NROW, length...
Joshua Ulrich
15
Sigo leyendo esta pregunta como una forma divertida de contar las cosas (a diferencia de las muchas formas poco divertidas, supongo).
Hong Ooi
6
@JoshuaUlrich: nrowno funcionó para mí, pero NROWy lengthbien trabajado. +1
Prolix

Respuestas:

69

La mejor práctica actual (tidyverse) es:

require(dplyr)
df1 %>% count(Year, Month)
geoteoria
fuente
¿Hay alguna manera de agregar una variable y contar también (como 2 funciones en agregación: media + recuento)? Necesito obtener la media de una columna y el número de filas para el mismo valor en otra columna
sop
1
Tengo cbindlos resultados de aggregate(Sepal.Length ~ Species, iris, mean)yaggregate(Sepal.Length ~ Species, iris, length)
geoteoría
Lo he hecho, pero parece que obtengo 2 veces cada columna excepto la que está agregada; así que hice una fusión en ellos y parece estar bien
sop
6
No lo sé, pero esto también podría ser útil ...df %>% group_by(group, variable) %>% mutate(count = n())
Manoj Kumar
1
Sí, dplyr es la mejor práctica ahora.
geoteoría
67

Siguiendo la sugerencia de @ Joshua, aquí hay una forma en que puede contar el número de observaciones en su dfmarco de datos donde Year= 2007 y Month= Nov (asumiendo que son columnas):

nrow(df[,df$YEAR == 2007 & df$Month == "Nov"])

y con aggregate, siguiendo a @GregSnow:

aggregate(x ~ Year + Month, data = df, FUN = length)
Ben
fuente
47

dplyrpaquete hace esto con count/ tallycomandos, o la n()función :

Primero, algunos datos:

df <- data.frame(x = rep(1:6, rep(c(1, 2, 3), 2)), year = 1993:2004, month = c(1, 1:11))

Ahora el recuento:

library(dplyr)
count(df, year, month)
#piping
df %>% count(year, month)

También podemos utilizar una versión un poco más larga con ribete y la n()función:

df %>% 
  group_by(year, month) %>%
  summarise(number = n())

o la tallyfunción:

df %>% 
  group_by(year, month) %>%
  tally()
jeremycg
fuente
37

Una vieja pregunta sin data.tablesolución. Así que aquí va ...

Utilizando .N

library(data.table)
DT <- data.table(df)
DT[, .N, by = list(year, month)]
mnel
fuente
1
estándar hoy en día para usar en .()lugar de list()y setDT()convertir un data.frame en data.table. Así que en un solo paso setDT(df)[, .N, by = .(year, month)].
sindri_baldur
23

La opción más simple para usar aggregatees la lengthfunción que le dará la longitud del vector en el subconjunto. A veces se utiliza un poco más robusto function(x) sum( !is.na(x) ).

Greg Snow
fuente
18

Cree una nueva variable Countcon un valor de 1 para cada fila:

df1["Count"] <-1

Luego agregue el marco de datos, sumando por la Countcolumna:

df2 <- aggregate(df1[c("Count")], by=list(Year=df1$Year, Month=df1$Month), FUN=sum, na.rm=TRUE)
Leroy Tyrone
fuente
Solo tenga en cuenta que si está utilizando el método predeterminado que no es de fórmula para aggregate, no es necesario cambiar el nombre de cada variable en by=like, list(year=df1$year)etc. A data.framees un listya, así aggregate(df1[c("Count")], by=df1[c("Year", "Month")], FUN=sum, na.rm=TRUE)que funcionará.
thelatemail
17

Una alternativa a la aggregate()función en este caso sería table()con as.data.frame(), que también indicaría qué combinaciones de Año y Mes están asociadas con cero ocurrencias.

df<-data.frame(x=rep(1:6,rep(c(1,2,3),2)),year=1993:2004,month=c(1,1:11))

myAns<-as.data.frame(table(df[,c("year","month")]))

Y sin las combinaciones de ocurrencia cero

myAns[which(myAns$Freq>0),]
BenBarnes
fuente
7

Si desea incluir 0 recuentos para meses-años que faltan en los datos, puede usar un poco de tablemagia.

data.frame(with(df1, table(Year, Month)))

Por ejemplo, el toy data.frame de la pregunta, df1, no contiene observaciones de enero de 2014.

df1
    x Year Month
1   1 2012   Feb
2   2 2014   Feb
3   3 2013   Mar
4   4 2012   Jan
5   5 2014   Feb
6   6 2014   Feb
7   7 2012   Jan
8   8 2014   Feb
9   9 2013   Mar
10 10 2013   Jan
11 11 2013   Jan
12 12 2012   Jan
13 13 2014   Mar
14 14 2012   Mar
15 15 2013   Feb
16 16 2014   Feb
17 17 2014   Mar
18 18 2012   Jan
19 19 2013   Mar
20 20 2012   Jan

La aggregatefunción base R no devuelve una observación para enero de 2014.

aggregate(x ~ Year + Month, data = df1, FUN = length)
  Year Month x
1 2012   Feb 1
2 2013   Feb 1
3 2014   Feb 5
4 2012   Jan 5
5 2013   Jan 2
6 2012   Mar 1
7 2013   Mar 3
8 2014   Mar 2

Si desea una observación de este mes-año con 0 como recuento, entonces el código anterior devolverá un data.frame con recuentos para todas las combinaciones mes-año:

data.frame(with(df1, table(Year, Month)))
  Year Month Freq
1 2012   Feb    1
2 2013   Feb    1
3 2014   Feb    5
4 2012   Jan    5
5 2013   Jan    2
6 2014   Jan    0
7 2012   Mar    1
8 2013   Mar    3
9 2014   Mar    2
lmo
fuente
5

Para mis agregaciones, generalmente termino queriendo ver la media y "qué tan grande es este grupo" (también conocido como longitud). Así que este es mi fragmento útil para esas ocasiones;

agg.mean <- aggregate(columnToMean ~ columnToAggregateOn1*columnToAggregateOn2, yourDataFrame, FUN="mean")
agg.count <- aggregate(columnToMean ~ columnToAggregateOn1*columnToAggregateOn2, yourDataFrame, FUN="length")
aggcount <- agg.count$columnToMean
agg <- cbind(aggcount, agg.mean)
laberinto
fuente
4

UNA solución usando el sqldfpaquete:

library(sqldf)
sqldf("SELECT Year, Month, COUNT(*) as Freq
       FROM df1
       GROUP BY Year, Month")
METRO--
fuente
1

Teniendo en cuenta la respuesta de @Ben, R arrojaría un error si df1no contiene una xcolumna. Pero se puede resolver elegantemente con paste:

aggregate(paste(Year, Month) ~ Year + Month, data = df1, FUN = NROW)

Del mismo modo, se puede generalizar si se utilizan más de dos variables en la agrupación:

aggregate(paste(Year, Month, Day) ~ Year + Month + Day, data = df1, FUN = NROW)
paudan
fuente
0

Puede usar byfunciones ya by(df1$Year, df1$Month, count)que producirán una lista de agregación necesaria.

La salida se verá así,

df1$Month: Feb
     x freq
1 2012    1
2 2013    1
3 2014    5
--------------------------------------------------------------- 
df1$Month: Jan
     x freq
1 2012    5
2 2013    2
--------------------------------------------------------------- 
df1$Month: Mar
     x freq
1 2012    1
2 2013    3
3 2014    2
> 
helcode
fuente
0

Ya hay muchas respuestas maravillosas aquí, pero quería agregar 1 opción más para aquellos que desean agregar una nueva columna al conjunto de datos original que contiene la cantidad de veces que se repite esa fila.

df1$counts <- sapply(X = paste(df1$Year, df1$Month), 
                     FUN = function(x) { sum(paste(df1$Year, df1$Month) == x) })

Lo mismo podría lograrse combinando cualquiera de las respuestas anteriores con la merge()función.

filups21
fuente
0

Si está probando las soluciones agregadas anteriores y obtiene el error:

tipo no válido (lista) para variable

Debido a que está usando marcas de fecha o fecha y hora, intente usar as.character en las variables:

aggregate(x ~ as.character(Year) + Month, data = df, FUN = length)

En una o ambas variables.

Ulises Ítaca
fuente