Policz liczbę wierszy w każdej grupie

121

Mam ramkę danych i chciałbym policzyć wiersze w każdej grupie. Regularnie używam aggregatefunkcji do sumowania danych w następujący sposób:

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

Chciałbym teraz policzyć obserwacje, ale nie mogę znaleźć odpowiedniego argumentu za FUN. Intuicyjnie pomyślałem, że będzie tak:

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

Ale nie ma takiego szczęścia.

Jakieś pomysły?


Niektóre dane dotyczące zabawek:

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
źródło
17
nrow, NROW, length...
Joshua Ulrich
15
Wciąż czytam to pytanie jako prośbę o fajny sposób liczenia rzeczy (w przeciwieństwie do wielu nienajlepszych sposobów, jak sądzę).
Hong Ooi
6
@JoshuaUlrich: nrownie działa dla mnie, ale NROWi lengthdziała dobrze. +1
Prolix

Odpowiedzi:

69

Aktualna najlepsza praktyka (tidyverse) to:

require(dplyr)
df1 %>% count(Year, Month)
geoteoria
źródło
Czy istnieje sposób na agregowanie zmiennej i zliczanie również (np. 2 funkcje w agregacji: średnia + liczba)? Muszę uzyskać średnią z kolumny i liczbę wierszy dla tej samej wartości w innej kolumnie
sop
1
Miałem cbindwyniki aggregate(Sepal.Length ~ Species, iris, mean)iaggregate(Sepal.Length ~ Species, iris, length)
geoteoria
Zrobiłem to, ale wygląda na to, że otrzymuję 2 razy w każdej kolumnie, z wyjątkiem tej, która jest zagregowana; więc dokonałem ich scalenia i wydaje się, że jest ok
sop
6
Nie wiem, ale to też mogłoby się przydać ...df %>% group_by(group, variable) %>% mutate(count = n())
Manoj Kumar,
1
Tak, dplyr jest teraz najlepszą praktyką.
geoteoria
67

Zgodnie z sugestią @ Joshua, oto jeden ze sposobów obliczenia liczby obserwacji w dframce danych, gdzie Year= 2007 i Month= Lis (zakładając, że są to kolumny):

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

i za pomocą aggregate@GregSnow:

aggregate(x ~ Year + Month, data = df, FUN = length)
Ben
źródło
47

dplyrpakiet robi to za pomocą count/ tallycommands lub n()funkcji :

Najpierw trochę danych:

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

Teraz liczyć:

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

Możemy też zastosować nieco dłuższą wersję z lamówką i n()funkcją:

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

lub tallyfunkcja:

df %>% 
  group_by(year, month) %>%
  tally()
jeremycg
źródło
37

Stare pytanie bez data.tablerozwiązania. Więc oto idzie ...

Za pomocą .N

library(data.table)
DT <- data.table(df)
DT[, .N, by = list(year, month)]
mnel
źródło
1
standard w dzisiejszych czasach do użycia .()zamiast list()i setDT()do konwersji data.frame do data.table. Więc w jednym kroku setDT(df)[, .N, by = .(year, month)].
sindri_baldur
23

Prostą opcją do użycia z aggregatejest lengthfunkcja, która poda długość wektora w podzbiorze. Czasami użycie jest trochę bardziej niezawodne function(x) sum( !is.na(x) ).

Greg Snow
źródło
18

Utwórz nową zmienną Counto wartości 1 dla każdego wiersza:

df1["Count"] <-1

Następnie zagreguj ramkę danych, sumując według Countkolumny:

df2 <- aggregate(df1[c("Count")], by=list(Year=df1$Year, Month=df1$Month), FUN=sum, na.rm=TRUE)
Leroy Tyrone
źródło
Wystarczy zauważyć, że jeśli używasz domyślnej, nieformułowanej metody for aggregate, nie ma potrzeby zmiany nazwy każdej zmiennej w by=like list(year=df1$year)itp. A data.framejest listjuż tak, że aggregate(df1[c("Count")], by=df1[c("Year", "Month")], FUN=sum, na.rm=TRUE)będzie działać.
thelatemail
17

Alternatywą dla aggregate()funkcji w tym przypadku byłaby table()z as.data.frame(), która wskazywałaby również, które kombinacje roku i miesiąca są powiązane z zerową liczbą wystąpień

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

I bez kombinacji występujących zero

myAns[which(myAns$Freq>0),]
BenBarnes
źródło
7

Jeśli chcesz uwzględnić 0 zliczeń dla miesięcy-lat, których brakuje w danych, możesz użyć trochę tablemagii.

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

Na przykład zabawkowa ramka data.frame w pytaniu, df1, nie zawiera żadnych obserwacji ze stycznia 2014 r.

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

Podstawowa aggregatefunkcja R nie zwraca obserwacji ze stycznia 2014 r.

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

Jeśli chcesz obserwować ten miesiąc-rok z 0 jako liczbą, powyższy kod zwróci ramkę data.frame z licznikami dla wszystkich kombinacji miesiąc-rok:

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
źródło
5

W przypadku moich agregacji zwykle chcę zobaczyć średnią i „jak duża jest ta grupa” (czyli długość). Więc to jest mój przydatny fragment na te okazje;

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)
Labirynt
źródło
5

ZA rozwiązanie za pomocą sqldfpakietu:

library(sqldf)
sqldf("SELECT Year, Month, COUNT(*) as Freq
       FROM df1
       GROUP BY Year, Month")
M--
źródło
1

Biorąc pod uwagę odpowiedź @Ben, R wyrzuci błąd, jeśli df1nie zawiera xkolumny. Ale można to elegancko rozwiązać za pomocą paste:

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

Podobnie można uogólnić, jeśli w grupowaniu są używane więcej niż dwie zmienne:

aggregate(paste(Year, Month, Day) ~ Year + Month + Day, data = df1, FUN = NROW)
paudan
źródło
0

Możesz użyć byfunkcji, ponieważ by(df1$Year, df1$Month, count)utworzą one listę potrzebnych agregacji.

Wynik będzie wyglądał następująco:

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
źródło
0

Jest tu już wiele wspaniałych odpowiedzi, ale chciałem dodać jeszcze 1 opcję dla tych, którzy chcą dodać nową kolumnę do oryginalnego zbioru danych, która zawiera liczbę powtórzeń tego wiersza.

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

To samo można osiągnąć, łącząc dowolną z powyższych odpowiedzi z merge()funkcją.

filups21
źródło
0

Jeśli wypróbowujesz powyższe rozwiązania zagregowane i pojawi się błąd:

nieprawidłowy typ (lista) dla zmiennej

Ponieważ używasz znaczników daty lub daty i godziny, spróbuj użyć as.character w zmiennych:

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

Na jednej lub obu zmiennych.

Odyseusz Itaka
źródło