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?

Author: Karolis Koncevičius, 2009-11-02

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))
 433
Author: rcs,
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.

 280
Author: talat,
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
 77
Author: asieira,
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.

 41
Author: Shane,
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
 30
Author: David Arenburg,
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))
 27
Author: learnr,
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)
 24
Author: Rob Hyndman,
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
 19
Author: joemienko,
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.

 18
Author: dalloliogm,
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
 7
Author: digEmAll,
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.

 6
Author: Manos Papadakis,
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)
 5
Author: Karolis Koncevičius,
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)
 5
Author: tmfmnk,
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
 4
Author: Grant Shannon,
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)

 0
Author: user12703198,
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