Contar o número de linhas em cada grupo


121

Tenho um dataframe e gostaria de contar o número de linhas dentro de cada grupo. Eu regularmente uso a aggregatefunção para somar os dados da seguinte forma:

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

Agora, eu gostaria de contar observações, mas não consigo encontrar o argumento adequado para FUN. Intuitivamente, pensei que seria o seguinte:

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

Mas, não tive essa sorte.

Alguma ideia?


Alguns dados do brinquedo:

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))

17
nrow, NROW, length...
Joshua Ulrich

15
Eu continuo lendo esta pergunta como uma forma divertida de contar as coisas (ao contrário das muitas maneiras não engraçadas, eu acho).
Hong Ooi

6
@JoshuaUlrich: nrownão funcionou para mim, mas NROWe lengthbem trabalhado. +1
Prolix

Respostas:


69

A prática recomendada atual (tidyverse) é:

require(dplyr)
df1 %>% count(Year, Month)

Existe uma maneira de agregar uma variável e fazer a contagem também (como 2 funções na agregação: média + contagem)? Preciso obter a média de uma coluna e o número de linhas para o mesmo valor em outra coluna
final de

1
Teria cbindos resultados de aggregate(Sepal.Length ~ Species, iris, mean)eaggregate(Sepal.Length ~ Species, iris, length)
geoteoria

Eu fiz isso, mas parece que recebo 2 vezes cada coluna, exceto aquela que está agregada; então eu fiz uma mesclagem neles e parece estar ok
sop

6
Eu não sei, mas isso poderia ser útil também ...df %>% group_by(group, variable) %>% mutate(count = n())
Manoj Kumar

1
Sim, dplyr é a melhor prática agora.
geotheory

67

Seguindo a sugestão de df@Joshua , aqui está uma maneira de contar o número de observações em seu dataframe onde Year= 2007 e Month= Nov (assumindo que sejam colunas):

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

e com aggregate, a seguir @GregSnow:

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

47

dplyrpacote faz isso com count/ tallycomandos ou a n()função :

Primeiro, alguns dados:

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

Agora a contagem:

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

Também podemos usar uma versão um pouco mais longa com tubulação e a n()função:

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

ou a tallyfunção:

df %>% 
  group_by(year, month) %>%
  tally()

37

Uma velha questão sem data.tablesolução. Então aqui vai ...

Usando .N

library(data.table)
DT <- data.table(df)
DT[, .N, by = list(year, month)]

1
padrão hoje em dia para usar em .()vez de list()e setDT()para converter um data.frame em data.table. Então, em uma etapa setDT(df)[, .N, by = .(year, month)].
sindri_baldur

23

A opção simples de usar aggregateé a lengthfunção que fornecerá o comprimento do vetor no subconjunto. Às vezes é um pouco mais robusto de usar function(x) sum( !is.na(x) ).


18

Crie uma nova variável Countcom valor 1 para cada linha:

df1["Count"] <-1

Em seguida, agregue dataframe, somando pela Countcoluna:

df2 <- aggregate(df1[c("Count")], by=list(Year=df1$Year, Month=df1$Month), FUN=sum, na.rm=TRUE)

Apenas para observar que se você estiver usando o método padrão sem fórmula para aggregate, não há necessidade de renomear cada variável by=como list(year=df1$year)etc. A já data.frameé um, listentão aggregate(df1[c("Count")], by=df1[c("Year", "Month")], FUN=sum, na.rm=TRUE)funcionará.
thelatemail

17

Uma alternativa para a aggregate()função neste caso seria table()com as.data.frame(), que também indicaria quais combinações de ano e mês estão associadas a zero ocorrências

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")]))

E sem as combinações de ocorrência zero

myAns[which(myAns$Freq>0),]

7

Se quiser incluir 0 contagens para meses-anos que estão faltando nos dados, você pode usar um pouco de tablemágica.

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

Por exemplo, o brinquedo data.frame na pergunta, df1, não contém observações de janeiro 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

A aggregatefunção base R não retorna uma observação para janeiro 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

Se desejar uma observação deste mês-ano com 0 como contagem, o código acima retornará um data.frame com contagens para todas as combinações mês-ano:

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

5

Para minhas agregações, geralmente acabo querendo ver a média e "quão grande é este grupo" (também conhecido como comprimento). Portanto, este é o meu trecho útil para essas ocasiões;

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)

4

UMA solução usando sqldfpacote:

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

1

Considerando a resposta @Ben, R geraria um erro se df1não contiver xcoluna. Mas pode ser resolvido elegantemente com paste:

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

Da mesma forma, pode ser generalizado se mais de duas variáveis ​​forem usadas no agrupamento:

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

0

Você pode usar byfunções, pois by(df1$Year, df1$Month, count)isso produzirá uma lista de agregações necessárias.

A saída será semelhante a

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
> 

0

Já existem muitas respostas maravilhosas aqui, mas eu gostaria de acrescentar mais 1 opção para aqueles que desejam adicionar uma nova coluna ao conjunto de dados original que contém o número de vezes que essa linha é repetida.

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

O mesmo pode ser feito combinando qualquer uma das respostas acima com a merge()função.


0

Se você está tentando as soluções agregadas acima e obtém o erro:

tipo inválido (lista) para variável

Como você está usando carimbos de data ou data e hora, tente usar as.character nas variáveis:

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

Em uma ou ambas as variáveis.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.