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

У меня есть датафрейм, и я хотел бы посчитать количество строк в каждой группе. Я регулярно использую 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))

20 ответов

Решение

Существует также df2 <- count(x, c('Year','Month')) (пакет plyr)

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

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

и с aggregate, следуя @GregSnow:

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

Мы также можем использовать dplyr,

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

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

или `функция подсчета:

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

Старый вопрос без data.table решение. Так что здесь идет...

С помощью .N

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

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

Создать новую переменную 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() функция в этом случае будет table() с 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),]

Если вы хотите включить 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

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

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

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)

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

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

С помощью collapse пакет в R

      library(collapse)
library(magrittr)
df %>% 
    fgroup_by(year, month) %>%
    fsummarise(number = fNobs(x))
library(tidyverse)

df_1 %>%
  group_by(Year, Month) %>%
  summarise(count= n()) 

Два очень быстрых варианта — это и .fcountэто быстрая версияdplyr::countи использует тот же синтаксис. Вы можете использоватьadd = TRUEчтобы добавить его как столбец (например):

      library(collapse)
fcount(df1, Year, Month) #or df1 %>% fcount(Year, Month)

#   Year Month N
# 1 2012   Feb 4
# 2 2014   Jan 3
# 3 2013   Mar 2
# 4 2013   Feb 2
# 5 2012   Jan 2
# 6 2012   Mar 2
# 7 2013   Jan 1
# 8 2014   Feb 3
# 9 2014   Mar 1

ближе кcollapseисходный синтаксис. Сначала сгруппируйте данные с помощьюGRP. Затем используйте . По умолчанию,GRPNсоздает расширенный вектор, соответствующий исходным данным. (Вdplyr, это было бы эквивалентно использованиюmutate). Использоватьexpand = FALSEдля вывода суммированного вектора.

      library(collapse)
GRPN(GRP(df1, .c(Year, Month)), expand = FALSE)

Микробенчмарк с кадром данных размером 100 000 x 3 и 4997 различными группами.collapse::fcountнамного быстрее, чем любой другой вариант.

      library(collapse)
library(dplyr)
library(data.table)
library(microbenchmark)

set.seed(1)
df <- data.frame(x = gl(1000, 100),
           y = rbinom(100000, 4, .5),
           z = runif(100000))
dt <- df

mb <- 
  microbenchmark(
  aggregate = aggregate(z ~ x + y, data = df, FUN = length),
  count = count(df, x, y),
  data.table = setDT(dt)[, .N, by = .(x, y)],
  'collapse::fnobs' = df %>% fgroup_by(x, y) %>% fsummarise(number = fnobs(z)),
  'collapse::GRPN' = GRPN(GRP(df, .c(x, y)), expand = FALSE),
  'collapse::fcount' = fcount(df, x, y)
)

# Unit: milliseconds
#             expr      min        lq       mean    median        uq      max neval
#        aggregate 159.5459 203.87385 227.787186 223.93050 246.36025 335.0302   100
#            count  55.1765  63.83560  74.715889  73.60195  79.20170 196.8888   100
#       data.table   8.4483  15.57120  18.308277  18.10790  20.65460  31.2666   100
#  collapse::fnobs   3.3325   4.16145   5.695979   5.18225   6.27720  22.7697   100
#   collapse::GRPN   3.0254   3.80890   4.844727   4.59445   5.50995  13.6649   100
# collapse::fcount   1.2222   1.57395   3.087526   1.89540   2.47955  22.5756   100

Я обычно использую табличную функцию

      
df <- data.frame(a=rep(1:8,rep(c(1,2,3, 4),2)),year=2011:2021,month=c(1,3:10))

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

Учитывая ответ @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)

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

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

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

Ты можешь использовать 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
> 

Вы также можете использоватьfcountиз моего пакета timeplyr, который принимает синтаксис dplyr, но использует свертывание под капотом.

      library(collapse)
library(timeplyr)
library(dplyr)
library(data.table)
library(microbenchmark)

set.seed(1)
df <- data.frame(x = gl(1000, 100),
                 y = rbinom(100000, 4, .5),
                 z = runif(100000))
dt <- df

mb <- 
  microbenchmark(
    aggregate = aggregate(z ~ x + y, data = df, FUN = length),
    count = count(df, x, y),
    data.table = setDT(dt)[, .N, by = .(x, y)],
    'collapse::fcount' = collapse::fcount(df, x, y),
    'timeplyr::fcount1' = timeplyr::fcount(df, x, y),
    'timeplyr::fcount2' = timeplyr::fcount(df, .cols = c("x", "y"), order = FALSE)
  )

mb
#> Unit: milliseconds
#>               expr     min        lq       mean    median        uq      max
#>          aggregate 84.0802 105.10615 123.593910 115.97675 134.65225 255.7676
#>              count 40.8108  50.82485  60.718189  56.81630  68.85530  97.4791
#>         data.table  3.7106   5.07485   6.273698   5.66645   6.44855  20.0465
#>   collapse::fcount  1.0118   1.37400   1.915809   1.61105   2.08465  13.9825
#>  timeplyr::fcount1  3.0390   3.74840   5.361852   4.56755   5.83405  44.0072
#>  timeplyr::fcount2  1.3787   1.98625   2.640338   2.47025   3.03450   8.6333
#>  neval
#>    100
#>    100
#>    100
#>    100
#>    100
#>    100

Создано 22 ноября 2023 г. с использованием reprex v2.0.2.

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

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

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

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

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

lw<- function(x){length(which(df$variable==someValue))}

agg<- aggregate(Var1~Var2+Var3, data=df, FUN=lw)

names(agg)<- c("Some", "Pretty", "Names", "Here")

View(agg)
Другие вопросы по тегам