Compter le nombre de lignes dans chaque groupe

121

J'ai un dataframe et je voudrais compter le nombre de lignes dans chaque groupe. J'utilise régulièrement la aggregatefonction pour additionner les données comme suit:

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

Maintenant, j'aimerais compter les observations mais je n'arrive pas à trouver le bon argument pour FUN. Intuitivement, j'ai pensé que ce serait comme suit:

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

Mais pas de chance.

Des idées?


Quelques données sur les jouets:

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
la source
17
nrow, NROW, length...
Joshua Ulrich
15
Je continue à lire cette question comme demandant une façon amusante de compter les choses (par opposition aux nombreuses façons peu amusantes, je suppose).
Hong Ooi
6
@JoshuaUlrich: nrown'a pas fonctionné pour moi , mais NROWet lengtha bien fonctionné. +1
Prolix

Réponses:

69

Les meilleures pratiques actuelles (tidyverse) sont:

require(dplyr)
df1 %>% count(Year, Month)
géothéorie
la source
Existe-t-il un moyen d'agréger une variable et de compter aussi (comme 2 fonctions d'agrégation: moyenne + décompte)? J'ai besoin d'obtenir la moyenne d'une colonne et le nombre de lignes pour la même valeur dans une autre colonne
sop
1
Je serais cbindles résultats de aggregate(Sepal.Length ~ Species, iris, mean)etaggregate(Sepal.Length ~ Species, iris, length)
geotheory
Je l'ai fait, mais il semble que j'obtiens 2 fois chaque colonne sauf celle qui est agrégée; alors j'ai fait une fusion sur eux et ça semble aller
sop
6
Je ne sais pas mais cela pourrait être utile aussi ...df %>% group_by(group, variable) %>% mutate(count = n())
Manoj Kumar
1
Oui, dplyr est la meilleure pratique maintenant.
geotheory
67

Suite à la suggestion de @ Joshua, voici une façon de compter le nombre d'observations dans votre base de dfdonnées où Year= 2007 et Month= Nov (en supposant qu'il s'agit de colonnes):

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

et avec aggregate, après @GregSnow:

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

dplyrpackage fait cela avec count/ tallycommandes, ou la n()fonction :

Tout d'abord, quelques données:

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

Maintenant le compte:

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

Nous pouvons également utiliser une version légèrement plus longue avec passepoil et la n()fonction:

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

ou la tallyfonction:

df %>% 
  group_by(year, month) %>%
  tally()
Jeremycg
la source
37

Une vieille question sans data.tablesolution. Alors voilà ...

En utilisant .N

library(data.table)
DT <- data.table(df)
DT[, .N, by = list(year, month)]
mnel
la source
1
standard de nos jours à utiliser à la .()place list()et setDT()à convertir un data.frame en data.table. Donc en une seule étape setDT(df)[, .N, by = .(year, month)].
sindri_baldur
23

L'option simple à utiliser avec aggregateest la lengthfonction qui vous donnera la longueur du vecteur dans le sous-ensemble. Parfois un peu plus robuste est à utiliser function(x) sum( !is.na(x) ).

Greg Snow
la source
18

Créez une nouvelle variable Countavec une valeur de 1 pour chaque ligne:

df1["Count"] <-1

Puis agrégez le dataframe, en additionnant par la Countcolonne:

df2 <- aggregate(df1[c("Count")], by=list(Year=df1$Year, Month=df1$Month), FUN=sum, na.rm=TRUE)
Leroy Tyrone
la source
Juste pour noter que si vous utilisez la méthode par défaut, sans formule pour aggregate, il n'est pas nécessaire de renommer chaque variable en by=like list(year=df1$year)etc. A data.frameest listdéjà un donc aggregate(df1[c("Count")], by=df1[c("Year", "Month")], FUN=sum, na.rm=TRUE)fonctionnera.
thelatemail
17

Une alternative à la aggregate()fonction dans ce cas serait table()avec as.data.frame(), qui indiquerait également quelles combinaisons d'année et de mois sont associées à zéro occurrence

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

Et sans les combinaisons nulles

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

Si vous souhaitez inclure 0 décompte pour les mois-années qui manquent dans les données, vous pouvez utiliser un peu de tablemagie.

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

Par exemple, le jouet data.frame de la question, df1, ne contient aucune observation de janvier 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 aggregatefonction de base R ne renvoie pas d'observation pour janvier 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 vous souhaitez une observation de ce mois-année avec 0 comme décompte, le code ci-dessus renverra un data.frame avec des décomptes pour toutes les combinaisons mois-année:

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
la source
5

Pour mes agrégations, je finis généralement par vouloir voir la moyenne et "quelle est la taille de ce groupe" (aka longueur). Voici donc mon extrait de code pratique pour ces occasions;

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)
Labyrinthe
la source
4

UNE solution utilisant le sqldfpackage:

library(sqldf)
sqldf("SELECT Year, Month, COUNT(*) as Freq
       FROM df1
       GROUP BY Year, Month")
M--
la source
1

Compte tenu de la réponse @Ben, R générerait une erreur s'il df1ne contient pas de xcolonne. Mais il peut être résolu avec élégance avec paste:

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

De même, il peut être généralisé si plus de deux variables sont utilisées dans le regroupement:

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

Vous pouvez utiliser des byfonctions car by(df1$Year, df1$Month, count)cela produira une liste d'agrégation nécessaire.

La sortie ressemblera à,

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
> 
code d'assistance
la source
0

Il y a déjà beaucoup de réponses merveilleuses ici, mais je voulais ajouter une option supplémentaire pour ceux qui souhaitent ajouter une nouvelle colonne à l'ensemble de données d'origine qui contient le nombre de fois que cette ligne est répétée.

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

La même chose pourrait être accomplie en combinant l'une des réponses ci-dessus avec la merge()fonction.

filups21
la source
0

Si vous essayez les solutions globales ci-dessus et que vous obtenez l'erreur:

type (liste) non valide pour la variable

Étant donné que vous utilisez des horodatages de date ou de date / heure, essayez d'utiliser as.character sur les variables:

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

Sur une ou les deux variables.

Ulysse Ithaque
la source