Cara menjumlahkan variabel berdasarkan grup

357

Saya memiliki bingkai data dengan dua kolom. Kolom pertama berisi kategori seperti "Pertama", "Kedua", "Ketiga", dan kolom kedua memiliki angka yang mewakili berapa kali saya melihat grup tertentu dari "Kategori".

Sebagai contoh:

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

Saya ingin mengurutkan data berdasarkan Kategori dan menjumlahkan semua Frekuensi:

Category     Frequency
First        30
Second       5
Third        34

Bagaimana saya melakukan ini dalam R?

pengguna5243421
sumber
1
Cara tercepat di base R adalah rowsum.
Michael M

Jawaban:

387

Menggunakan aggregate:

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

Pada contoh di atas, beberapa dimensi dapat ditentukan dalam list. Beberapa metrik teragregasi dari tipe data yang sama dapat dimasukkan melalui cbind:

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

(menanamkan komentar @thelatemail), aggregatememiliki antarmuka rumus juga

aggregate(Frequency ~ Category, x, sum)

Atau jika Anda ingin menggabungkan beberapa kolom, Anda bisa menggunakan .notasi (juga berfungsi untuk satu kolom)

aggregate(. ~ Category, x, sum)

atau tapply:

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

Menggunakan data ini:

x <- data.frame(Category=factor(c("First", "First", "First", "Second",
                                      "Third", "Third", "Second")), 
                    Frequency=c(10,15,5,2,14,20,3))
rcs
sumber
4
@AndrewMcKinlay, R menggunakan tilde untuk mendefinisikan rumus simbolik, untuk statistik dan fungsi lainnya. Ini dapat diartikan sebagai "model Frekuensi berdasarkan Kategori" atau "Frekuensi tergantung pada Kategori" . Tidak semua bahasa menggunakan operator khusus untuk mendefinisikan fungsi simbolis, seperti yang dilakukan dalam R di sini. Mungkin dengan "interpretasi bahasa alami" dari operator tilde, itu menjadi lebih bermakna (dan bahkan intuitif). Saya pribadi menemukan representasi formula simbolik ini lebih baik daripada beberapa alternatif yang lebih bertele-tele.
r2evans
1
Menjadi orang baru bagi R (dan mengajukan pertanyaan yang sama seperti OP), saya akan mendapat manfaat dari beberapa detail sintaks di balik setiap alternatif. Misalnya, jika saya memiliki tabel sumber yang lebih besar dan ingin memilih hanya dua dimensi ditambah metrik yang dijumlahkan, dapatkah saya mengadaptasi salah satu dari metode ini? Sulit dikatakan.
Dodecaphone
236

Anda juga dapat menggunakan paket dplyr untuk tujuan itu:

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

Atau, untuk beberapa kolom ringkasan (juga berfungsi dengan satu kolom):

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

Berikut adalah beberapa contoh tentang cara meringkas data dengan grup menggunakan fungsi dplyr menggunakan dataset bawaan 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))

Untuk informasi lebih lanjut, termasuk %>%operator, lihat pengantar dplyr .

talat
sumber
1
Seberapa cepat bila dibandingkan dengan data. Tabel dan agregat alternatif disajikan dalam jawaban lain?
asieira
5
@asieira, Yang tercepat dan seberapa besar perbedaannya (atau jika perbedaannya terlihat) akan selalu tergantung pada ukuran data Anda. Biasanya, untuk kumpulan data besar, misalnya beberapa GB, data.table kemungkinan besar akan lebih cepat. Pada ukuran data yang lebih kecil, data.tabel dan dplyr sering ditutup, juga tergantung pada jumlah grup. Baik data, tabel dan dplyr akan jauh lebih cepat daripada fungsi dasar, namun (bisa 100-1000 kali lebih cepat untuk beberapa operasi). Lihat juga di sini
talat
1
Apa yang dimaksud "kesenangan" dalam contoh kedua?
lauren.marietta
@ lauren.marietta Anda dapat menentukan fungsi yang ingin Anda terapkan sebagai ringkasan di dalam funs()argumen summarise_alldan fungsi yang terkait ( summarise_at, summarise_if)
talat
76

Jawaban yang diberikan oleh rcs berhasil dan sederhana. Namun, jika Anda menangani kumpulan data yang lebih besar dan membutuhkan peningkatan kinerja ada alternatif yang lebih cepat:

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 

Mari kita bandingkan dengan hal yang sama menggunakan data.frame dan yang di atas:

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 

Dan jika Anda ingin menyimpan kolom ini adalah sintaks:

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

Perbedaannya akan menjadi lebih terlihat dengan kumpulan data yang lebih besar, karena kode di bawah ini menunjukkan:

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 

Untuk beberapa agregasi, Anda dapat menggabungkan lapplydan .SDsebagai berikut

data[, lapply(.SD, sum), by = Category]
#    Category Frequency
# 1:    First        30
# 2:   Second         5
# 3:    Third        34
asieira
sumber
13
+1 Tapi 0,296 vs 0,059 tidak terlalu mengesankan. Ukuran data harus jauh lebih besar dari 300k baris, dan dengan lebih dari 3 grup, untuk data. Tabel dapat bersinar. Kami akan mencoba dan mendukung lebih dari 2 miliar baris segera misalnya, karena beberapa pengguna data.tabel memiliki 250GB RAM dan GNU R sekarang mendukung panjang> 2 ^ 31.
Matt Dowle
2
Benar. Ternyata saya tidak memiliki semua RAM itu, dan hanya mencoba memberikan beberapa bukti data.tabel kinerja yang unggul. Saya yakin perbedaannya akan lebih besar dengan lebih banyak data.
asieira
1
Saya memiliki 7 juta pengamatan, dplyr memerlukan 0,3 detik dan agregat () membutuhkan 22 detik untuk menyelesaikan operasi. Saya akan mempostingnya di topik ini dan Anda mengalahkan saya untuk itu!
zazu
3
Bahkan ada cara yang lebih pendek untuk menulis ini data[, sum(Frequency), by = Category]. Anda bisa menggunakan fungsi .Npengganti yang mana sum(). data[, .N, by = Category]. Berikut ini adalah lembar contekan yang
Stophface
3
Menggunakan .N akan setara dengan jumlah (Frekuensi) hanya jika semua nilai dalam kolom Frekuensi sama dengan 1, karena .N menghitung jumlah baris di setiap himpunan agregat (.SD). Dan bukan itu masalahnya di sini.
asieira
41

Anda juga dapat menggunakan fungsi by () :

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

Paket-paket lain (plyr, membentuk kembali) memiliki manfaat mengembalikan data.frame, tetapi layak untuk dikenali oleh () karena itu adalah fungsi dasar.

Shane
sumber
28

Beberapa tahun kemudian, hanya untuk menambahkan solusi R dasar sederhana yang tidak hadir di sini untuk beberapa alasan- xtabs

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

Atau jika Anda ingin data.framekembali

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

Jika xbingkai data dengan data Anda, maka hal berikut akan melakukan apa yang Anda inginkan:

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

Sementara saya baru-baru ini menjadi konversi dplyruntuk sebagian besar jenis operasi ini, sqldfpaket itu masih sangat bagus (dan IMHO lebih mudah dibaca) untuk beberapa hal.

Berikut adalah contoh bagaimana pertanyaan ini dapat dijawab 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
sumber
18

Hanya untuk menambahkan opsi ketiga:

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

EDIT: ini adalah jawaban yang sangat lama. Sekarang saya akan merekomendasikan penggunaan group_bydan summarisedari dplyr, seperti pada jawaban @docendo.

dalloliogm
sumber
7

Saya merasa avesangat membantu (dan efisien) ketika Anda perlu menerapkan fungsi agregasi yang berbeda pada kolom yang berbeda (dan Anda harus / ingin tetap pada basis R):

misalnya

Diberikan masukan ini:

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

kami ingin kelompok dengan Categ1dan Categ2dan menghitung jumlah Samplesdan rata-rata dari Freq.
Berikut adalah solusi yang mungkin menggunakan 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),]

Hasil:

> 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
menggali semua
sumber
6

Yang baru ditambahkan dplyr::tally()sekarang menjadikan ini lebih mudah dari sebelumnya:

tally(x, Category)

Category     n
First        30
Second       5
Third        34
dmca
sumber
6

Anda bisa menggunakan fungsi group.sumdari 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 memiliki banyak fungsi kelompok dangroup.summerupakan salah satunya.

Manos Papadakis
sumber
4

menggunakan castbukan recast(catatan 'Frequency'sekarang '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)

mendapatkan:

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

Solusi lain yang mengembalikan jumlah oleh kelompok dalam matriks atau bingkai data dan pendek dan cepat:

rowsum(x$Frequency, x$Category)
Karolis Koncevičius
sumber
Bagus, dan memang cepat.
jay.sf
0

Karena dplyr 1.0.0, across()fungsi tersebut dapat digunakan:

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

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

Jika tertarik pada banyak variabel:

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

Dan pemilihan variabel menggunakan pembantu pilih:

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

Contoh data:

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
sumber