Saya memiliki bingkai data berikut
x <- read.table(text = " id1 id2 val1 val2
1 a x 1 9
2 a x 2 4
3 a y 3 5
4 a y 4 9
5 b x 1 7
6 b y 4 4
7 b x 3 9
8 b y 2 8", header = TRUE)
Saya ingin menghitung mean dari val1 dan val2 yang dikelompokkan berdasarkan id1 dan id2, dan sekaligus menghitung jumlah baris untuk setiap kombinasi id1-id2. Saya dapat melakukan setiap perhitungan secara terpisah:
# calculate mean
aggregate(. ~ id1 + id2, data = x, FUN = mean)
# count rows
aggregate(. ~ id1 + id2, data = x, FUN = length)
Untuk melakukan kedua kalkulasi dalam satu panggilan, saya mencoba
do.call("rbind", aggregate(. ~ id1 + id2, data = x, FUN = function(x) data.frame(m = mean(x), n = length(x))))
Namun, saya mendapatkan hasil yang kacau bersama dengan peringatan:
# m n
# id1 1 2
# id2 1 1
# 1.5 2
# 2 2
# 3.5 2
# 3 2
# 6.5 2
# 8 2
# 7 2
# 6 2
# Warning message:
# In rbind(id1 = c(1L, 2L, 1L, 2L), id2 = c(1L, 1L, 2L, 2L), val1 = list( :
# number of columns of result is not a multiple of vector length (arg 1)
Saya dapat menggunakan paket plyr, tetapi kumpulan data saya cukup besar dan plyr sangat lambat (hampir tidak dapat digunakan) ketika ukuran kumpulan data bertambah.
Bagaimana cara menggunakan aggregate
atau fungsi lain untuk melakukan beberapa kalkulasi dalam satu panggilan?
aggregate
disebutkan dalam jawaban ada jugaby
dantapply
.Jawaban:
Anda dapat melakukan semuanya dalam satu langkah dan mendapatkan pelabelan yang tepat:
> aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) ) # id1 id2 val1.mn val1.n val2.mn val2.n # 1 a x 1.5 2.0 6.5 2.0 # 2 b x 2.0 2.0 8.0 2.0 # 3 a y 3.5 2.0 7.0 2.0 # 4 b y 3.0 2.0 6.0 2.0
Ini membuat kerangka data dengan dua kolom id dan dua kolom matriks:
str( aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) ) ) 'data.frame': 4 obs. of 4 variables: $ id1 : Factor w/ 2 levels "a","b": 1 2 1 2 $ id2 : Factor w/ 2 levels "x","y": 1 1 2 2 $ val1: num [1:4, 1:2] 1.5 2 3.5 3 2 2 2 2 ..- attr(*, "dimnames")=List of 2 .. ..$ : NULL .. ..$ : chr "mn" "n" $ val2: num [1:4, 1:2] 6.5 8 7 6 2 2 2 2 ..- attr(*, "dimnames")=List of 2 .. ..$ : NULL .. ..$ : chr "mn" "n"
Seperti yang ditunjukkan oleh @ lord.garbage di bawah, ini dapat diubah menjadi kerangka data dengan kolom "sederhana" dengan menggunakan
do.call(data.frame, ...)
str( do.call(data.frame, aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) ) ) ) 'data.frame': 4 obs. of 6 variables: $ id1 : Factor w/ 2 levels "a","b": 1 2 1 2 $ id2 : Factor w/ 2 levels "x","y": 1 1 2 2 $ val1.mn: num 1.5 2 3.5 3 $ val1.n : num 2 2 2 2 $ val2.mn: num 6.5 8 7 6 $ val2.n : num 2 2 2 2
Ini adalah sintaks untuk beberapa variabel di LHS:
aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) )
sumber
d$val1[ , ""mn"]
lihat strukturnya denganstr
.agg <- aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x)))
dengan menggunakanagg_df <- do.call(data.frame, agg)
. Lihat juga disini .Diberikan ini dalam pertanyaan:
Kemudian di
data.table
(1.9.4+
) Anda dapat mencoba:> DT id1 id2 val1 val2 1: a x 1 9 2: a x 2 4 3: a y 3 5 4: a y 4 9 5: b x 1 7 6: b y 4 4 7: b x 3 9 8: b y 2 8 > DT[ , .(mean(val1), mean(val2), .N), by = .(id1, id2)] # simplest id1 id2 V1 V2 N 1: a x 1.5 6.5 2 2: a y 3.5 7.0 2 3: b x 2.0 8.0 2 4: b y 3.0 6.0 2 > DT[ , .(val1.m = mean(val1), val2.m = mean(val2), count = .N), by = .(id1, id2)] # named id1 id2 val1.m val2.m count 1: a x 1.5 6.5 2 2: a y 3.5 7.0 2 3: b x 2.0 8.0 2 4: b y 3.0 6.0 2 > DT[ , c(lapply(.SD, mean), count = .N), by = .(id1, id2)] # mean over all columns id1 id2 val1 val2 count 1: a x 1.5 6.5 2 2: a y 3.5 7.0 2 3: b x 2.0 8.0 2 4: b y 3.0 6.0 2
Untuk membandingkan waktu
aggregate
(digunakan dalam pertanyaan dan ketiga jawaban lainnya) untukdata.table
melihat tolok ukur ini ( kasusagg
danagg.x
).sumber
Anda dapat menambahkan
count
kolom, menggabungkan dengansum
, lalu menskalakan kembali untuk mendapatkanmean
:x$count <- 1 agg <- aggregate(. ~ id1 + id2, data = x,FUN = sum) agg # id1 id2 val1 val2 count # 1 a x 3 13 2 # 2 b x 4 16 2 # 3 a y 7 14 2 # 4 b y 6 12 2 agg[c("val1", "val2")] <- agg[c("val1", "val2")] / agg$count agg # id1 id2 val1 val2 count # 1 a x 1.5 6.5 2 # 2 b x 2.0 8.0 2 # 3 a y 3.5 7.0 2 # 4 b y 3.0 6.0 2
Ini memiliki keuntungan untuk mempertahankan nama kolom Anda dan membuat satu
count
kolom.sumber
Dengan menggunakan
dplyr
paket Anda dapat mencapai ini dengan menggunakansummarise_all
. Dengan fungsi ringkasan ini Anda dapat menerapkan fungsi lain (dalam kasus inimean
dann()
) ke setiap kolom non-pengelompokan:pemberian yang mana:
id1 id2 val1_mean val2_mean val1_n val2_n 1 a x 1.5 6.5 2 2 2 a y 3.5 7.0 2 2 3 b x 2.0 8.0 2 2 4 b y 3.0 6.0 2 2
Jika Anda tidak ingin menerapkan fungsi ke semua kolom non-pengelompokan, Anda menentukan kolom yang harus diterapkan atau dengan mengecualikan yang tidak diinginkan dengan minus menggunakan
summarise_at()
fungsi:# inclusion x %>% group_by(id1, id2) %>% summarise_at(vars(val1, val2), funs(mean, n())) # exclusion x %>% group_by(id1, id2) %>% summarise_at(vars(-val2), funs(mean, n()))
sumber
Mungkin Anda ingin bergabung ?
x.mean <- aggregate(. ~ id1+id2, p, mean) x.len <- aggregate(. ~ id1+id2, p, length) merge(x.mean, x.len, by = c("id1", "id2")) id1 id2 val1.x val2.x val1.y val2.y 1 a x 1.5 6.5 2 2 2 a y 3.5 7.0 2 2 3 b x 2.0 8.0 2 2 4 b y 3.0 6.0 2 2
sumber
Anda juga dapat menggunakan
plyr::each()
untuk memperkenalkan beberapa fungsi:sumber
dplyr
Pilihan lainnya adalahacross
yang merupakan bagian dari versi dev saat ini#devtools::install_github("tidyverse/dplyr") library(dplyr) x %>% group_by(id1, id2) %>% summarise(across(starts_with("val"), list(mean = mean, n = length)))
Hasil
# A tibble: 4 x 4 # Groups: id1 [2] id1 id2 mean$val1 $val2 n$val1 $val2 <fct> <fct> <dbl> <dbl> <int> <int> 1 a x 1.5 6.5 2 2 2 a y 3.5 7 2 2 3 b x 2 8 2 2 4 b y 3 6 2 2
packageVersion("dplyr") [1] ‘0.8.99.9000’
sumber