Подсчитайте количество строк в каждой группе

121

У меня есть фрейм данных, и я хотел бы подсчитать количество строк в каждой группе. Я регулярно использую эту aggregateфункцию для суммирования данных следующим образом:

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

Теперь я хотел бы посчитать наблюдения, но не могу найти подходящего аргумента FUN. Интуитивно я думал, что это будет так:

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

Но не тут-то было.

Любые идеи?


Некоторые данные игрушек:

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
источник
17
nrow, NROW, length...
Джошуа Ulrich
15
Я продолжаю читать этот вопрос как просьбу о забавном способе подсчета вещей (в отличие от многих неудобных способов, я думаю).
Hong Ooi
6
@JoshuaUlrich: nrowне работает для меня , но NROWи lengthработал отлично. +1
Prolix

Ответы:

70

Текущая лучшая практика (tidyverse):

require(dplyr)
df1 %>% count(Year, Month)
geotheory
источник
Есть ли способ агрегировать переменную и делать подсчет (например, 2 функции в агрегировании: среднее + счетчик)? Мне нужно получить среднее значение столбца и количество строк для того же значения в другом столбце
sop
1
Я бы cbindрезультаты aggregate(Sepal.Length ~ Species, iris, mean)иaggregate(Sepal.Length ~ Species, iris, length)
геотеория
Я сделал это, но кажется, что я получаю 2 раза каждый столбец, кроме агрегированного; так что я сделал их слияние, и вроде все в порядке
sop
6
Я не знаю, но это тоже может быть полезно ...df %>% group_by(group, variable) %>% mutate(count = n())
Манодж Кумар
1
Да, dplyr сейчас является лучшей практикой.
geotheory
67

Следуя предложению @Joshua, вот один из способов подсчета количества наблюдений в вашем dfфрейме данных, где Year= 2007 и Month= ноя (при условии, что это столбцы):

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

и с помощью aggregate@GregSnow:

aggregate(x ~ Year + Month, data = df, FUN = length)
Бен
источник
47

dplyrpackage делает это с помощью count/ tallycommands или n()функции :

Для начала немного данных:

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

Теперь посчитаем:

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

Мы также можем использовать немного более длинную версию с трубопроводом и n()функцией:

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

или tallyфункция:

df %>% 
  group_by(year, month) %>%
  tally()
Jeremycg
источник
38

Старый вопрос без data.tableрешения. Итак, начнем ...

С помощью .N

library(data.table)
DT <- data.table(df)
DT[, .N, by = list(year, month)]
mnel
источник
1
стандарт в настоящее время для использования .()вместо list()и setDT()для преобразования data.frame в data.table. Итак, за один шаг setDT(df)[, .N, by = .(year, month)].
sindri_baldur
23

Самый простой вариант использования aggregate- это lengthфункция, которая даст вам длину вектора в подмножестве. Иногда нужно использовать немного более надежный function(x) sum( !is.na(x) ).

Грег Сноу
источник
18

Создайте новую переменную Countсо значением 1 для каждой строки:

df1["Count"] <-1

Затем объедините фрейм данных, суммируя по Countстолбцу:

df2 <- aggregate(df1[c("Count")], by=list(Year=df1$Year, Month=df1$Month), FUN=sum, na.rm=TRUE)
Лерой Тайрон
источник
Просто к сведению , что если вы используете по умолчанию, не формула метод aggregate, нет необходимости переименовать каждую переменную в by=как и list(year=df1$year)т.д. data.frameэто listуже так aggregate(df1[c("Count")], by=df1[c("Year", "Month")], FUN=sum, na.rm=TRUE)будет работать.
thelatemail
17

Альтернативой aggregate()функции в этом случае будет table()with as.data.frame(), которая также укажет, какие комбинации года и месяца связаны с нулевым вхождением.

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

И без нулевых комбинаций

myAns[which(myAns$Freq>0),]
БенБарнс
источник
7

Если вы хотите включить 0 отсчетов для месяцев-лет, которые отсутствуют в данных, вы можете использовать небольшое tableволшебство.

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

Например, игрушечный data.frame в вопросе df1 не содержит наблюдений за январь 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

Базовая aggregateфункция R не возвращает наблюдение за январь 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

Если вы хотите наблюдать за этим месяцем-годом с 0 в качестве счетчика, то приведенный выше код вернет data.frame со счетчиками для всех комбинаций месяц-год:

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
источник
5

Для моих агрегатов я обычно в конечном итоге хочу видеть среднее значение и «насколько велика эта группа» (также известная как длина). Так что это мой удобный фрагмент для тех случаев;

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)
лабиринт
источник
5

А решение с использованием sqldfпакета:

library(sqldf)
sqldf("SELECT Year, Month, COUNT(*) as Freq
       FROM df1
       GROUP BY Year, Month")
M--
источник
1

Учитывая ответ @Ben, R выдаст ошибку, если df1не содержит xстолбца. Но это можно элегантно решить с помощью paste:

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

Точно так же его можно обобщить, если в группировке используется более двух переменных:

aggregate(paste(Year, Month, Day) ~ Year + Month + Day, data = df1, FUN = NROW)
паудан
источник
0

Ты можешь использовать by функции, так как они by(df1$Year, df1$Month, count)будут производить список необходимого агрегирования.

Результат будет выглядеть так:

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
источник
0

Здесь уже есть множество замечательных ответов, но я хотел добавить еще один вариант для тех, кто хочет добавить новый столбец в исходный набор данных, который содержит количество повторений этой строки.

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

То же самое можно сделать, объединив любой из приведенных выше ответов с merge()функцией.

filups21
источник
0

Если вы попробуете совокупные решения, указанные выше, и получите ошибку:

недопустимый тип (список) для переменной

Поскольку вы используете дату или дату и время, попробуйте использовать as.character для переменных:

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

По одной или обеим переменным.

Одиссей Итака
источник