Как суммировать переменную по группе
Скажем, у меня есть два столбца данных. Первый содержит такие категории, как "Первый", "Второй", "Третий" и т.д. Во втором есть числа, которые представляют количество раз, когда я видел "Первое".
Например:
Category Frequency
First 10
First 15
First 5
Second 2
Third 14
Third 20
Second 3
Я хочу сортировать данные по категориям и суммировать частоты:
Category Frequency
First 30
Second 5
Third 34
Как бы это сделать в R?
Ответы
Ответ 1
Использование aggregate
:
aggregate(x$Frequency, by=list(Category=x$Category), FUN=sum)
Category x
1 First 30
2 Second 5
3 Third 34
В приведенном выше примере в list
можно указать несколько измерений. Множество агрегированных показателей одного и того же типа данных можно включить через cbind
:
aggregate(cbind(x$Frequency, x$Metric2, x$Metric3) ...
(вложение комментария @thelatemail), aggregate
имеет интерфейс формулы
aggregate(Frequency ~ Category, x, sum)
Или, если вы хотите объединить несколько столбцов, вы можете использовать .
обозначение (работает и для одного столбца)
aggregate(. ~ Category, x, sum)
или tapply
:
tapply(x$Frequency, x$Category, FUN=sum)
First Second Third
30 5 34
Используя эти данные:
x <- data.frame(Category=factor(c("First", "First", "First", "Second",
"Third", "Third", "Second")),
Frequency=c(10,15,5,2,14,20,3))
Ответ 2
Для этого вы также можете использовать пакет dplyr :
library(dplyr)
x %>%
group_by(Category) %>%
summarise(Frequency = sum(Frequency))
#Source: local data frame [3 x 2]
#
# Category Frequency
#1 First 30
#2 Second 5
#3 Third 34
Или для нескольких столбцов сводки (также работает с одним столбцом):
x %>%
group_by(Category) %>%
summarise_all(funs(sum))
Вот еще несколько примеров того, как суммировать данные по группам, используя функции dplyr, используя встроенный набор данных mtcars
:
# several summary columns with arbitrary names
mtcars %>%
group_by(cyl, gear) %>% # multiple group columns
summarise(max_hp = max(hp), mean_mpg = mean(mpg)) # multiple summary columns
# summarise all columns except grouping columns using "sum"
mtcars %>%
group_by(cyl) %>%
summarise_all(sum)
# summarise all columns except grouping columns using "sum" and "mean"
mtcars %>%
group_by(cyl) %>%
summarise_all(funs(sum, mean))
# multiple grouping columns
mtcars %>%
group_by(cyl, gear) %>%
summarise_all(funs(sum, mean))
# summarise specific variables, not all
mtcars %>%
group_by(cyl, gear) %>%
summarise_at(vars(qsec, mpg, wt), funs(sum, mean))
# summarise specific variables (numeric columns except grouping columns)
mtcars %>%
group_by(gear) %>%
summarise_if(is.numeric, funs(mean))
Для получения дополнительной информации, включая оператор %>%
, см. введение к dplyr.
Ответ 3
Ответ, предоставленный rcs, работает и прост. Однако, если вы работаете с большими наборами данных и нуждаетесь в повышении производительности, существует более быстрая альтернатива:
library(data.table)
data = data.table(Category=c("First","First","First","Second","Third", "Third", "Second"),
Frequency=c(10,15,5,2,14,20,3))
data[, sum(Frequency), by = Category]
# Category V1
# 1: First 30
# 2: Second 5
# 3: Third 34
system.time(data[, sum(Frequency), by = Category] )
# user system elapsed
# 0.008 0.001 0.009
Сравним это с тем же, используя data.frame и выше:
data = data.frame(Category=c("First","First","First","Second","Third", "Third", "Second"),
Frequency=c(10,15,5,2,14,20,3))
system.time(aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum))
# user system elapsed
# 0.008 0.000 0.015
И если вы хотите сохранить столбец, это синтаксис:
data[,list(Frequency=sum(Frequency)),by=Category]
# Category Frequency
# 1: First 30
# 2: Second 5
# 3: Third 34
Разница станет более заметной с более крупными наборами данных, как показывает следующий код:
data = data.table(Category=rep(c("First", "Second", "Third"), 100000),
Frequency=rnorm(100000))
system.time( data[,sum(Frequency),by=Category] )
# user system elapsed
# 0.055 0.004 0.059
data = data.frame(Category=rep(c("First", "Second", "Third"), 100000),
Frequency=rnorm(100000))
system.time( aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum) )
# user system elapsed
# 0.287 0.010 0.296
Для нескольких агрегатов вы можете комбинировать lapply
и .SD
следующим образом
data[, lapply(.SD, sum), by = Category]
# Category Frequency
# 1: First 30
# 2: Second 5
# 3: Third 34
Ответ 4
Это несколько связанный с этим вопросом.
Вы также можете просто использовать функцию by():
x2 <- by(x$Frequency, x$Category, sum)
do.call(rbind,as.list(x2))
Эти другие пакеты (plyr, reshape) имеют преимущество в возвращении data.frame, но его стоит знать с помощью(), поскольку это базовая функция.
Ответ 5
library(plyr)
ddply(tbl, .(Category), summarise, sum = sum(Frequency))
Ответ 6
Несколько лет спустя, просто чтобы добавить еще одно простое решение base R, которого здесь нет для некоторых reason- xtabs
xtabs(Frequency ~ Category, df)
# Category
# First Second Third
# 30 5 34
Или, если вы хотите вернуть data.frame
as.data.frame(xtabs(Frequency ~ Category, df))
# Category Freq
# 1 First 30
# 2 Second 5
# 3 Third 34
Ответ 7
Если x
- это кадр данных с вашими данными, то следующее будет делать то, что вы хотите:
require(reshape)
recast(x, Category ~ ., fun.aggregate=sum)
Ответ 8
В то время как я недавно стал конвертировать в dplyr
для большинства этих типов операций, пакет sqldf
по-прежнему очень хорош (и IMHO более читабельным) для некоторых вещей.
Вот пример того, как можно ответить на этот вопрос с помощью sqldf
x <- data.frame(Category=factor(c("First", "First", "First", "Second",
"Third", "Third", "Second")),
Frequency=c(10,15,5,2,14,20,3))
sqldf("select
Category
,sum(Frequency) as Frequency
from x
group by
Category")
## Category Frequency
## 1 First 30
## 2 Second 5
## 3 Third 34
Ответ 9
Просто чтобы добавить третий вариант:
require(doBy)
summaryBy(Frequency~Category, data=yourdataframe, FUN=sum)
РЕДАКТИРОВАТЬ: это очень старый ответ. Теперь я бы порекомендовал использовать group_by
и summarise
из dplyr
, как в ответе @docendo.
Ответ 10
Недавно добавленный dplyr::tally()
теперь делает это проще, чем когда-либо:
tally(x, Category)
Category n
First 30
Second 5
Third 34
Ответ 11
Я считаю, ave
очень полезно (и эффективно), когда вам необходимо применять различные функции агрегации на разных колонках (и вы должны/хотите придерживаться на базе R):
например
Учитывая этот вклад:
DF <-
data.frame(Categ1=factor(c('A','A','B','B','A','B','A')),
Categ2=factor(c('X','Y','X','X','X','Y','Y')),
Samples=c(1,2,4,3,5,6,7),
Freq=c(10,30,45,55,80,65,50))
> DF
Categ1 Categ2 Samples Freq
1 A X 1 10
2 A Y 2 30
3 B X 4 45
4 B X 3 55
5 A X 5 80
6 B Y 6 65
7 A Y 7 50
мы хотим сгруппировать по Categ1
и Categ2
и вычислить сумму Samples
и среднего значения Freq
.
Вот возможное решение с использованием ave
:
# create a copy of DF (only the grouping columns)
DF2 <- DF[,c('Categ1','Categ2')]
# add sum of Samples by Categ1,Categ2 to DF2
# (ave repeats the sum of the group for each row in the same group)
DF2$GroupTotSamples <- ave(DF$Samples,DF2,FUN=sum)
# add mean of Freq by Categ1,Categ2 to DF2
# (ave repeats the mean of the group for each row in the same group)
DF2$GroupAvgFreq <- ave(DF$Freq,DF2,FUN=mean)
# remove the duplicates (keep only one row for each group)
DF2 <- DF2[!duplicated(DF2),]
Результат:
> DF2
Categ1 Categ2 GroupTotSamples GroupAvgFreq
1 A X 6 45
2 A Y 9 40
3 B X 7 50
6 B Y 6 65
Ответ 12
Вы можете использовать функцию group.sum
из пакета Rfast.
Category <- Rfast::as_integer(Category,result.sort=FALSE) # convert character to numeric. R as.numeric produce NAs.
result <- Rfast::group.sum(Frequency,Category)
names(result) <- Rfast::Sort(unique(Category)
# 30 5 34
Rfast имеет много групповых функций, и group.sum
является одной из них.
Ответ 13
используя cast
вместо recast
(note 'Frequency'
теперь 'value'
)
df <- data.frame(Category = c("First","First","First","Second","Third","Third","Second")
, value = c(10,15,5,2,14,20,3))
install.packages("reshape")
result<-cast(df, Category ~ . ,fun.aggregate=sum)
чтобы получить:
Category (all)
First 30
Second 5
Third 34