Bir değişkeni gruba göre toplama


357

İki sütunlu bir veri çerçevem ​​var. İlk sütun "İlk", "İkinci", "Üçüncü" gibi kategoriler içerir ve ikinci sütunda "Kategori" den belirli grupların kaç kez gördüğümü gösteren sayılar bulunur.

Örneğin:

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

Verileri Kategoriye göre sıralamak ve tüm Frekansları toplamak istiyorum:

Category     Frequency
First        30
Second       5
Third        34

Bunu R'de nasıl yaparım?


1
R tabanındaki en hızlı yol rowsum.
Michael M

Yanıtlar:


388

Kullanma aggregate:

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

Yukarıdaki örnekte, içinde birden çok boyut belirtilebilir list. Aynı veri türüne sahip birden çok toplu metrikler aşağıdakiler yoluyla dahil edilebilir cbind:

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

(@thelatemail comment gömme) aggregatede bir formül arayüzüne sahiptir

aggregate(Frequency ~ Category, x, sum)

Veya birden çok sütunu toplamak istiyorsanız, .gösterimi kullanabilirsiniz (bir sütun için de çalışır)

aggregate(. ~ Category, x, sum)

veya tapply:

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

Bu verileri kullanma:

x <- data.frame(Category=factor(c("First", "First", "First", "Second",
                                      "Third", "Third", "Second")), 
                    Frequency=c(10,15,5,2,14,20,3))

4
@ AndrewMcKinlay, R, istatistik ve diğer işlevler için sembolik formülleri tanımlamak için yaklaşık işareti kullanır. "Kategoriye Göre Frekans Modeli" veya " Kategoriye Göre Frekans" olarak yorumlanabilir . Tüm diller, burada R'de yapıldığı gibi sembolik bir işlevi tanımlamak için özel bir operatör kullanmaz. Belki tilde operatörünün "doğal dil yorumlaması" ile daha anlamlı (hatta sezgisel) hale gelir. Şahsen bu sembolik formül temsilini bazı daha ayrıntılı alternatiflerden daha iyi buluyorum.
r2evans

1
R için yeni olmak (ve OP ile aynı soruları sormak), her alternatifin ardındaki sözdiziminin biraz daha ayrıntısından faydalanacağım. Örneğin, daha büyük bir kaynak tablom varsa ve yalnızca iki boyut artı toplanan metrikleri alt seçmek istiyorsanız, bu yöntemlerden herhangi birini uyarlayabilir miyim? Söylemesi zor.
Dodecaphone

236

Bu amaçla dplyr paketini de kullanabilirsiniz :

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

Veya birden çok özet sütun için (bir sütunla da çalışır):

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

Yerleşik veri kümesini kullanarak dplyr işlevlerini kullanarak verileri gruplara göre özetlemenin bazı örnekleri 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))

%>%Operatör dahil daha fazla bilgi için dplyr girişine bakın .


1
Diğer cevaplarda sunulan data.table ve agrega alternatifleriyle karşılaştırıldığında ne kadar hızlı?
asieira

5
@asieira, ki bu en hızlı ve farkın ne kadar büyük olduğu (ya da fark göze çarpıyorsa) her zaman veri boyutunuza bağlı olacaktır. Genellikle, büyük veri kümeleri, örneğin bazı GB için, data.table büyük olasılıkla en hızlı olacaktır. Daha küçük veri boyutlarında, grup sayısına bağlı olarak da data.table ve dplyr genellikle yakındır. Ancak, veri, tablo ve dplyr temel işlevlerden çok daha hızlı olacaktır (bazı işlemler için 100-1000 kat daha hızlı olabilir). Ayrıca buraya
talat

1
İkinci örnekte "eğlenceler" ne anlama geliyor?
lauren.marietta

@ lauren.marietta, funs()argümanında özet olarak uygulamak istediğiniz summarise_allişlevleri ve ilgili işlevleri belirtebilirsiniz ( summarise_at, summarise_if)
talat

76

Rcs tarafından verilen cevap işe yarıyor ve basit. Ancak, daha büyük veri kümelerini yönetiyorsanız ve performans artışı istiyorsanız daha hızlı bir alternatif vardır:

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 

Bunu data.frame ve yukarıdakileri kullanarak aynı şeyle karşılaştıralım:

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 

Ve sütunu korumak istiyorsanız bu sözdizimidir:

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

Aşağıdaki kodun gösterdiği gibi, fark daha büyük veri kümelerinde daha belirgin hale gelecektir:

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 

Birden toplanmalar için, birleştirebilir lapplyve .SDaşağıdaki gibi

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

13
+1 Ama 0.296 vs 0.059 özellikle etkileyici değil. Veri boyutu parlamak için veri boyutunun 300k satırdan çok daha büyük ve 3'ten fazla grupla olması gerekir. Örneğin, 2 milyardan fazla satırı yakında deneyeceğiz ve destekleyeceğiz, çünkü bazı veriler. 250GB RAM'e sahip ve GNU R artık uzunluğu> 2 ^ 31'i destekliyor.
Matt Dowle

2
Doğru. Görünen o ki, tüm bu RAM'lere sahip değilim ve sadece data.table'ın üstün performansına dair bazı kanıtlar sağlamaya çalışıyordu. Eminim fark daha fazla veriyle daha da büyük olur.
asieira

1
7 mil gözlem yaptım. 3 saniye sürdü ve toplam () işlemi tamamlamak için 22 saniye sürdü. Bunu bu konuya gönderecektim ve sen beni dövüyordun!
zazu

3
Bunu yazmanın daha kısa bir yolu var data[, sum(Frequency), by = Category]. İşlevin .Nyerine hangi sum()işlevi kullanabilirsiniz . data[, .N, by = Category]. İşte yararlı bir Pardus geçerli: s3.amazonaws.com/assets.datacamp.com/img/blog/...
Stophface

3
.N kullanımı, yalnızca Frekans sütunundaki tüm değerler 1'e eşit olduğunda toplama (Frekans) eşdeğer olur. Çünkü .N, her kümelenmiş kümedeki (.SD) satır sayısını sayar. Ve burada durum böyle değil.
asieira

41

Ayrıca by () işlevini de kullanabilirsiniz :

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

Bu diğer paketler (plyr, reshape) bir data.frame döndürme avantajına sahiptir, ancak temel işlev olduğu için () tarafından bilinmeye değer.


28

Birkaç yıl sonra, sadece bir nedenden dolayı burada olmayan başka bir basit temel R çözümü eklemek için- xtabs

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

Veya data.framegeri dönmek isterseniz

as.data.frame(xtabs(Frequency ~ Category, df))
#   Category Freq
# 1    First   30
# 2   Second    5
# 3    Third   34

27
library(plyr)
ddply(tbl, .(Category), summarise, sum = sum(Frequency))

23

Eğer xsizin verilerle bir dataframe, sonra şu istediğini yapar:

require(reshape)
recast(x, Category ~ ., fun.aggregate=sum)

19

Son zamanlarda dplyrbu tür işlemlerin çoğu için bir dönüştürme haline sqldfgelmeme rağmen , paket hala bazı şeyler için gerçekten güzel (ve IMHO daha okunabilir).

İşte bu sorunun nasıl cevaplanabileceğine bir örnek 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

18

Üçüncü bir seçenek eklemek için:

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

EDIT: Bu çok eski bir cevap. Şimdi kullanılmasını öneriyoruz group_byve summarisegelen dplyr@docendo cevap olarak.


7

aveFarklı sütunlara farklı toplama işlevleri uygulamanız gerektiğinde (ve R tabanına yapışmanız / istemeniz gerektiğinde) çok yararlı (ve verimli) buluyorum :

Örneğin

Bu girdi verildiğinde:

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

bu gruba göre istediğiniz Categ1ve Categ2ve toplamını hesaplamak Samplesve ortalama Freq.
İşte olası bir çözüm 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),]

Sonuç:

> 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

6

Son eklenenler dplyr::tally()artık bunu her zamankinden daha kolay hale getiriyor:

tally(x, Category)

Category     n
First        30
Second       5
Third        34

6

Sen işlevini kullanabilirsiniz group.sumgelen paket 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'ın birçok grup işlevi vardır vegroup.sumbunlardan biridir.


4

kullanılarak castyerine recast(not 'Frequency'şimdi '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)

almak:

Category (all)
First     30
Second    5
Third     34

2

Bir matristeki veya veri çerçevesindeki gruplara göre toplamları döndüren ve kısa ve hızlı olan başka bir çözüm:

rowsum(x$Frequency, x$Category)

Güzel ve gerçekten hızlı.
jay.sf

0

Yana dplyr 1.0.0, across()fonksiyon kullanılabilir:

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

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

Birden çok değişkenle ilgileniyorsanız:

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

Ve seçkin yardımcıları kullanarak değişken seçimi:

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

Örnek veri:

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.