So summieren Sie eine Variable nach Gruppe

357

Ich habe einen Datenrahmen mit zwei Spalten. Die erste Spalte enthält Kategorien wie "Erste", "Zweite", "Dritte", und die zweite Spalte enthält Zahlen, die angeben, wie oft ich die spezifischen Gruppen aus "Kategorie" gesehen habe.

Zum Beispiel:

Category     Frequency
First        10
First        15
First        5
Second       2
Third        14
Third        20
Second       3

Ich möchte die Daten nach Kategorie sortieren und alle Häufigkeiten summieren:

Category     Frequency
First        30
Second       5
Third        34

Wie würde ich das in R machen?

user5243421
quelle
1
Der schnellste Weg in Basis R ist rowsum.
Michael M

Antworten:

387

Verwenden von aggregate:

aggregate(x$Frequency, by=list(Category=x$Category), FUN=sum)
  Category  x
1    First 30
2   Second  5
3    Third 34

Im obigen Beispiel können mehrere Dimensionen in der angegeben werden list. Mehrere aggregierte Metriken desselben Datentyps können integriert werden über cbind:

aggregate(cbind(x$Frequency, x$Metric2, x$Metric3) ...

(Einbettung des @ thelatemail-Kommentars), aggregatehat auch eine Formelschnittstelle

aggregate(Frequency ~ Category, x, sum)

Wenn Sie mehrere Spalten zusammenfassen möchten, können Sie die .Notation verwenden (funktioniert auch für eine Spalte).

aggregate(. ~ Category, x, sum)

oder tapply:

tapply(x$Frequency, x$Category, FUN=sum)
 First Second  Third 
    30      5     34 

Verwendung dieser Daten:

x <- data.frame(Category=factor(c("First", "First", "First", "Second",
                                      "Third", "Third", "Second")), 
                    Frequency=c(10,15,5,2,14,20,3))
rcs
quelle
4
@AndrewMcKinlay, R verwendet die Tilde, um symbolische Formeln für Statistiken und andere Funktionen zu definieren. Es kann als "Modellfrequenz nach Kategorie" oder "Häufigkeit je nach Kategorie" interpretiert werden . Nicht alle Sprachen verwenden einen speziellen Operator, um eine symbolische Funktion zu definieren, wie hier in R ausgeführt. Vielleicht wird es mit dieser "Interpretation der natürlichen Sprache" des Tilde-Operators aussagekräftiger (und sogar intuitiver). Ich persönlich finde diese symbolische Formeldarstellung besser als einige der ausführlicheren Alternativen.
R2evans
1
Da ich neu in R bin (und die gleichen Fragen stelle wie das OP), würde ich von mehr Details der Syntax hinter jeder Alternative profitieren. Kann ich beispielsweise eine dieser Methoden anpassen, wenn ich eine größere Quelltabelle habe und nur zwei Dimensionen plus summierte Metriken unterwählen möchte? Schwer zu erzählen.
Dodecaphone
236

Sie können auch das dplyr- Paket für diesen Zweck verwenden:

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

Oder für mehrere Zusammenfassungsspalten (funktioniert auch mit einer Spalte):

x %>% 
  group_by(Category) %>% 
  summarise_all(funs(sum))

Im Folgenden finden Sie einige weitere Beispiele für die Zusammenfassung von Daten nach Gruppen mithilfe von dplyr-Funktionen unter Verwendung des integrierten Datensatzes 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))

Weitere Informationen, einschließlich des %>%Bedieners, finden Sie in der Einführung zu dplyr .

Talat
quelle
1
Wie schnell ist es im Vergleich zu den in anderen Antworten dargestellten data.table- und aggregierten Alternativen?
Asieira
5
@asieira, welches am schnellsten ist und wie groß der Unterschied ist (oder ob der Unterschied spürbar ist), hängt immer von Ihrer Datengröße ab. In der Regel ist data.table für große Datenmengen, z. B. einige GB, am schnellsten. Bei kleineren Datengrößen sind data.table und dplyr häufig nahe beieinander, auch abhängig von der Anzahl der Gruppen. Sowohl Daten als auch Tabelle und dplyr sind jedoch viel schneller als Basisfunktionen (können bei einigen Operationen durchaus 100-1000-mal schneller sein). Siehe auch hier
Talat
1
Worauf beziehen sich die "Funs" im zweiten Beispiel?
lauren.marietta
@ lauren.marietta Sie können die Funktion (en) angeben, die Sie als Zusammenfassung innerhalb des funs()Arguments von summarise_allund der zugehörigen Funktionen ( summarise_at, summarise_if)
anwenden möchten
76

Die Antwort von rcs funktioniert und ist einfach. Wenn Sie jedoch größere Datenmengen verarbeiten und eine Leistungssteigerung benötigen, gibt es eine schnellere Alternative:

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 

Vergleichen wir das mit data.frame und den oben genannten oben:

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 

Und wenn Sie die Spalte behalten möchten, ist dies die Syntax:

data[,list(Frequency=sum(Frequency)),by=Category]
#    Category Frequency
# 1:    First        30
# 2:   Second         5
# 3:    Third        34

Der Unterschied wird bei größeren Datensätzen deutlicher, wie der folgende Code zeigt:

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 

Für mehrere Aggregationen können Sie kombinieren lapplyund .SDwie folgt

data[, lapply(.SD, sum), by = Category]
#    Category Frequency
# 1:    First        30
# 2:   Second         5
# 3:    Third        34
Asieira
quelle
13
+1 Aber 0,296 vs 0,059 sind nicht besonders beeindruckend. Die Datengröße muss viel größer als 300.000 Zeilen und mit mehr als 3 Gruppen sein, damit data.table glänzt. Wir werden beispielsweise versuchen, bald mehr als 2 Milliarden Zeilen zu unterstützen, da einige Benutzer von data.table über 250 GB RAM verfügen und GNU R jetzt eine Länge von> 2 ^ 31 unterstützt.
Matt Dowle
2
Wahr. Es stellte sich heraus, dass ich nicht über den gesamten Arbeitsspeicher verfüge und lediglich versuchte, einige Beweise für die überlegene Leistung von data.table zu liefern. Ich bin sicher, dass der Unterschied mit mehr Daten noch größer wäre.
Asieira
1
Ich hatte 7 mil Beobachtungen dplyr dauerte 0,3 Sekunden und aggregat () brauchte 22 Sekunden, um die Operation abzuschließen. Ich wollte es zu diesem Thema posten und du hast mich geschlagen!
Zazu
3
Es gibt einen noch kürzeren Weg, dies zu schreiben data[, sum(Frequency), by = Category]. Sie können verwenden, .Nwelche die sum()Funktion ersetzt. data[, .N, by = Category]. Hier ist ein nützliches Cheatsheet: s3.amazonaws.com/assets.datacamp.com/img/blog/…
Stophface
3
Die Verwendung von .N würde nur dann der Summe (Häufigkeit) entsprechen, wenn alle Werte in der Spalte "Frequenz" gleich 1 wären, da .N die Anzahl der Zeilen in jeder aggregierten Menge (.SD) zählt. Und das ist hier nicht der Fall.
Asieira
41

Sie können auch die Funktion by () verwenden:

x2 <- by(x$Frequency, x$Category, sum)
do.call(rbind,as.list(x2))

Diese anderen Pakete (plyr, reshape) haben den Vorteil, dass ein data.frame zurückgegeben wird, aber es lohnt sich, mit () vertraut zu sein, da es sich um eine Basisfunktion handelt.

Shane
quelle
28

Einige Jahre später, nur um eine weitere einfache Basis-R-Lösung hinzuzufügen, die hier aus irgendeinem Grund nicht vorhanden ist: xtabs

xtabs(Frequency ~ Category, df)
# Category
# First Second  Third 
#    30      5     34 

Oder wenn Sie einen data.frameRücken wollen

as.data.frame(xtabs(Frequency ~ Category, df))
#   Category Freq
# 1    First   30
# 2   Second    5
# 3    Third   34
David Arenburg
quelle
27
library(plyr)
ddply(tbl, .(Category), summarise, sum = sum(Frequency))
learnr
quelle
23

Wenn xes sich bei Ihren Daten um einen Datenrahmen handelt, wird Folgendes ausgeführt:

require(reshape)
recast(x, Category ~ ., fun.aggregate=sum)
Rob Hyndman
quelle
19

Während ich kürzlich dplyrfür die meisten dieser Arten von Operationen konvertiert wurde , ist das sqldfPaket für einige Dinge immer noch sehr schön (und meiner Meinung nach besser lesbar).

Hier ist ein Beispiel, wie diese Frage beantwortet werden kann 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
Joemienko
quelle
18

Nur um eine dritte Option hinzuzufügen:

require(doBy)
summaryBy(Frequency~Category, data=yourdataframe, FUN=sum)

EDIT: Dies ist eine sehr alte Antwort. Jetzt würde ich die Verwendung von group_byund summarisevon empfehlen dplyr, wie in @docendo Antwort.

dalloliogm
quelle
7

Ich finde es avesehr hilfreich (und effizient), wenn Sie verschiedene Aggregationsfunktionen auf verschiedene Spalten anwenden müssen (und Sie müssen / wollen auf Basis R bleiben):

z.B

Angesichts dieser Eingabe:

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

wir wollen nach und gruppieren Categ1und Categ2die Summe von Samplesund den Mittelwert von berechnen Freq.
Hier ist eine mögliche Lösung mit 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),]

Ergebnis:

> 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
digEmAll
quelle
6

Das kürzlich hinzugefügte dplyr::tally()macht dies jetzt einfacher als je zuvor:

tally(x, Category)

Category     n
First        30
Second       5
Third        34
dmca
quelle
6

Sie können die Funktion group.sumaus dem Paket Rfast verwenden .

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 hat viele Gruppenfunktionen undgroup.sumist eine davon.

Manos Papadakis
quelle
4

Verwenden castanstelle von recast(Hinweis 'Frequency'ist jetzt '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)

bekommen:

Category (all)
First     30
Second    5
Third     34
Grant Shannon
quelle
2

Eine andere Lösung, die Summen nach Gruppen in einer Matrix oder einem Datenrahmen zurückgibt und kurz und schnell ist:

rowsum(x$Frequency, x$Category)
Karolis Koncevičius
quelle
Schön und in der Tat schnell.
jay.sf
0

Da könnte dplyr 1.0.0die across()Funktion verwendet werden:

df %>%
 group_by(Category) %>%
 summarise(across(Frequency, sum))

  Category Frequency
  <chr>        <int>
1 First           30
2 Second           5
3 Third           34

Bei Interesse an mehreren Variablen:

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

Und die Auswahl von Variablen mit ausgewählten Helfern:

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

Beispieldaten:

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)
tmfmnk
quelle