Jak zsumować zmienną według grupy
Mam ramkę danych z dwiema kolumnami. Pierwsza kolumna zawiera kategorie takie jak "pierwsza"," druga"," trzecia", a druga kolumna ma numery, które reprezentują liczbę razy widziałem konkretne grupy z"kategorii".
Na przykład:
Category Frequency
First 10
First 15
First 5
Second 2
Third 14
Third 20
Second 3
Chcę posortować dane według kategorii i zsumować wszystkie częstotliwości:
Category Frequency
First 30
Second 5
Third 34
Jak mam to zrobić w R?
15 answers
Używając aggregate
:
aggregate(x$Frequency, by=list(Category=x$Category), FUN=sum)
Category x
1 First 30
2 Second 5
3 Third 34
W powyższym przykładzie można określić wiele wymiarów w list
. Wiele zagregowanych metryk tego samego typu danych można włączyć za pomocą cbind
:
aggregate(cbind(x$Frequency, x$Metric2, x$Metric3) ...
(embedding @thelatemail comment), aggregate
ma też interfejs formuły
aggregate(Frequency ~ Category, x, sum)
Lub jeśli chcesz połączyć wiele kolumn, możesz użyć notacji .
(działa również dla jednej kolumny)
aggregate(. ~ Category, x, sum)
Lub tapply
:
tapply(x$Frequency, x$Category, FUN=sum)
First Second Third
30 5 34
Za pomocą dane te:
x <- data.frame(Category=factor(c("First", "First", "First", "Second",
"Third", "Third", "Second")),
Frequency=c(10,15,5,2,14,20,3))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-28 20:40:39
Możesz również użyć pakietu dplyr w tym celu:
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
Lub dla wielu kolumn podsumowania (działa również z jedną kolumną):
x %>%
group_by(Category) %>%
summarise(across(everything(), sum))
Oto kilka przykładów podsumowania danych według grup przy użyciu funkcji dplyr przy użyciu wbudowanego zestawu danych 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(across(everything(), sum))
# summarise all columns except grouping columns using "sum" and "mean"
mtcars %>%
group_by(cyl) %>%
summarise(across(everything(), list(mean = mean, sum = sum)))
# multiple grouping columns
mtcars %>%
group_by(cyl, gear) %>%
summarise(across(everything(), list(mean = mean, sum = sum)))
# summarise specific variables, not all
mtcars %>%
group_by(cyl, gear) %>%
summarise(across(c(qsec, mpg, wt), list(mean = mean, sum = sum)))
# summarise specific variables (numeric columns except grouping columns)
mtcars %>%
group_by(gear) %>%
summarise(across(where(is.numeric), list(mean = mean, sum = sum)))
Aby uzyskać więcej informacji, w tym operatora %>%
, Zobacz wprowadzenie do dplyr.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-17 03:33:03
Odpowiedź udzielona przez rcs działa i jest prosta. Jeśli jednak obsługujesz większe zbiory danych i potrzebujesz zwiększenia wydajności, istnieje szybsza alternatywa: {]}
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
Porównajmy to do tego samego, używając danych.ramka i wyżej:
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
I jeśli chcesz zachować kolumnę to jest składnia:
data[,list(Frequency=sum(Frequency)),by=Category]
# Category Frequency
# 1: First 30
# 2: Second 5
# 3: Third 34
Różnica będzie bardziej zauważalna przy większych zestawach danych, jak pokazuje poniższy kod:
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
Dla wielu agregacji, ty można łączyć lapply
i .SD
w następujący sposób
data[, lapply(.SD, sum), by = Category]
# Category Frequency
# 1: First 30
# 2: Second 5
# 3: Third 34
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-09 12:24:08
Możesz również użyć funkcji by () :
x2 <- by(x$Frequency, x$Category, sum)
do.call(rbind,as.list(x2))
Te inne pakiety (plyr, reshape) mają tę zaletę, że zwracają dane.frame, ale warto się z nią zapoznać by (), ponieważ jest to funkcja bazowa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-29 20:11:15
Kilka lat później, aby dodać kolejne proste rozwiązanie base R, którego nie ma tutaj z jakiegoś powodu - xtabs
xtabs(Frequency ~ Category, df)
# Category
# First Second Third
# 30 5 34
Or if you want a data.frame
back
as.data.frame(xtabs(Frequency ~ Category, df))
# Category Freq
# 1 First 30
# 2 Second 5
# 3 Third 34
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-03 04:47:13
library(plyr)
ddply(tbl, .(Category), summarise, sum = sum(Frequency))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-02 09:44:34
Jeśli x
jest ramką danych z Twoimi danymi, to następujące dane zrobią to, co chcesz:
require(reshape)
recast(x, Category ~ ., fun.aggregate=sum)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-02 09:44:03
Podczas gdy ostatnio stałem się konwerterem na dplyr
dla większości tego typu operacji, pakiet sqldf
jest nadal naprawdę ładny (i IMHO bardziej czytelny) dla niektórych rzeczy.
Oto przykład jak na to pytanie można odpowiedzieć za pomocą 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-17 12:12:56
Aby dodać trzecią opcję:
require(doBy)
summaryBy(Frequency~Category, data=yourdataframe, FUN=sum)
EDIT: to bardzo stara odpowiedź. Teraz polecam użycie group_by
i summarise
z dplyr
, jak w odpowiedzi @docendo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-03 04:47:46
I find ave
bardzo pomocne (i wydajne), gdy trzeba zastosować różne funkcje agregacji na różnych kolumnach (i trzeba / chce się trzymać na podstawie R): {]}
Np.
Biorąc pod uwagę to Wejście:
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
Chcemy pogrupować według Categ1
i Categ2
i obliczyć sumę Samples
i średnią z Freq
.
Oto możliwe rozwiązanie za pomocą 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),]
Wynik:
> 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-10 16:55:13
Możesz użyć funkcji group.sum
z pakietu Rfast.
Category <- Rfast::as_integer(Category,result.sort=FALSE) # convert character to numeric. R's as.numeric produce NAs.
result <- Rfast::group.sum(Frequency,Category)
names(result) <- Rfast::Sort(unique(Category)
# 30 5 34
Rfast posiada wiele funkcji grupowych i group.sum
jest jedną z nich.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-10 17:41:30
Inne rozwiązanie, które zwraca sumy według grup w macierzy lub ramce danych i jest krótkie i szybkie:
rowsum(x$Frequency, x$Category)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-01 23:20:27
Od dplyr 1.0.0
można użyć funkcji across()
:
df %>%
group_by(Category) %>%
summarise(across(Frequency, sum))
Category Frequency
<chr> <int>
1 First 30
2 Second 5
3 Third 34
Jeśli interesuje Cię wiele zmiennych:
df %>%
group_by(Category) %>%
summarise(across(c(Frequency, Frequency2), sum))
Category Frequency Frequency2
<chr> <int> <int>
1 First 30 55
2 Second 5 29
3 Third 34 190
Oraz wybór zmiennych za pomocą select helperów:
df %>%
group_by(Category) %>%
summarise(across(starts_with("Freq"), sum))
Category Frequency Frequency2 Frequency3
<chr> <int> <int> <dbl>
1 First 30 55 110
2 Second 5 29 58
3 Third 34 190 380
Przykładowe dane:
df <- read.table(text = "Category Frequency Frequency2 Frequency3
1 First 10 10 20
2 First 15 30 60
3 First 5 15 30
4 Second 2 8 16
5 Third 14 70 140
6 Third 20 120 240
7 Second 3 21 42",
header = TRUE,
stringsAsFactors = FALSE)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-14 16:43:23
Użycie cast
zamiast recast
(Uwaga 'Frequency'
jest teraz '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)
Aby uzyskać:
Category (all)
First 30
Second 5
Third 34
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-25 15:43:56
library(tidyverse)
x <- data.frame(Category= c('First', 'First', 'First', 'Second', 'Third', 'Third', 'Second'),
Frequency = c(10, 15, 5, 2, 14, 20, 3))
count(x, Category, wt = Frequency)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-21 17:03:25