Tek bir çağrıda birkaç değişken üzerinde gruba göre birkaç özet işlevi uygulayın


93

Aşağıdaki veri çerçevesine sahibim

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)

İd1 ve id2'ye göre gruplandırılmış val1 ve val2 ortalamasını hesaplamak ve aynı anda her id1-id2 kombinasyonu için satır sayısını saymak istiyorum. Her hesaplamayı ayrı ayrı yapabilirim:

# calculate mean
aggregate(. ~ id1 + id2, data = x, FUN = mean)

# count rows
aggregate(. ~ id1 + id2, data = x, FUN = length)

Her iki hesaplamayı da tek bir aramada yapmak için denedim

do.call("rbind", aggregate(. ~ id1 + id2, data = x, FUN = function(x) data.frame(m = mean(x), n = length(x))))

Ancak, bir uyarı ile birlikte bozuk bir çıktı alıyorum:

#     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)

Plyr paketini kullanabilirdim, ancak veri setim oldukça büyük ve veri setinin boyutu büyüdüğünde plyr çok yavaş (neredeyse kullanılamaz).

aggregateBir aramada birkaç hesaplama yapmak için veya diğer işlevleri nasıl kullanabilirim ?


aggregateCevapların yanı sıra byve de var tapply.
Roman Luštrik

Yanıtlar:


156

Hepsini tek bir adımda yapabilir ve uygun etiketleme alabilirsiniz:

> 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

Bu, iki kimlik sütunu ve iki matris sütunu içeren bir veri çerçevesi oluşturur:

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"

Aşağıda @ lord.garbage tarafından belirtildiği gibi, bu, "basit" sütunlarla bir veri çerçevesine dönüştürülebilir. 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

Bu, LHS'deki birden çok değişken için sözdizimidir:

aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) )

1
Çok teşekkürler. Bir yan not olarak, sadece bir sütunu toplamayı nasıl elde ederim. Birkaç sayısal sütunum varsa, istemediğim sütunları toplamasını istemiyorum. Toplama tamamlandıktan sonra sütunları elbette atabilirim, ancak CPU döngüleri o zaman zaten harcanırdı.
brokoli

Ona yalnızca gruplanacak faktörleri ve toplanacak sütunları verirsiniz. Olasılıkla verilerde negatif sütun indeksleme kullanın veya istediğiniz sütunları formülün LHS'sine yerleştirin. (Düzenlemeye bakın.)
IRTFM

2
Windows 7 makinesinde RStudio 0.98.1014'ü kullanırken user2659402'nin güncellemesinde bahsettiği hatayla karşılaştım. Veri çerçevesini gösterildiği gibi konsola çıkarırsanız, normal görünür, ancak bunu d içine kaydedip sonra d $ val1.mn'ye erişmeye çalışırsanız, NULL döndürür. d, görünümü (d) çalıştırırsanız da bozuk olarak görünür. Güncellemedeki kodu kullanmak sorunu çözdü.
JHowIX

4
Zorlanmanızın nedeni, "vals" ların sıradan sütunlar yerine her biri iki sütun içeren matrisler olarak döndürülmesidir. d$val1[ , ""mn"]Yapıya ile bakmaya çalışın str.
IRTFM

5
Matrisleri içeren sütunları veri çerçevesine geri bağlayabilirsiniz: agg <- aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x)))kullanarak agg_df <- do.call(data.frame, agg). Ayrıca buraya bakın .
lord.garbage

30

Soruda bu göz önüne alındığında:

Plyr paketini kullanabilirdim, ancak veri setim oldukça büyük ve veri setinin boyutu büyüdüğünde plyr çok yavaş (neredeyse kullanılamaz).

Sonra data.table( 1.9.4+) 'da deneyebilirsiniz:

> 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

Karşılaştıran zamanlamaları için aggregate(söz konusu kullanılmış ve tüm diğer 3 cevaplar) için data.tablebkz bu kriter ( aggve agg.xdava).


12

Bir countsütun ekleyebilir , birlikte toplayabilir sumve ardından aşağıdakileri elde etmek için geri ölçekleyebilirsiniz mean:

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

Sütun adlarınızı koruma ve tek bir countsütun oluşturma avantajına sahiptir .


12

dplyrPaketi kullanarak bunu başarabilirsiniz summarise_all. Bu özetleme işleviyle, diğer işlevleri (bu durumda meanve n()) gruplamayan sütunların her birine uygulayabilirsiniz:

x %>%
  group_by(id1, id2) %>%
  summarise_all(funs(mean, n()))

hangi verir:

     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

İşlev (ler) i tüm gruplamayan sütunlara uygulamak istemiyorsanız, uygulanmaları gereken sütunları veya summarise_at()işlevi kullanarak istenmeyenleri bir eksi ile hariç tutarak belirtin :

# 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()))

10

Belki birleşmek istersiniz ?

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

4

plyr::each()Birden çok işlevi tanıtmak için de kullanabilirsiniz :

aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = plyr::each(avg = mean, n = length))

1

Mevcut geliştirme sürümünün bir parçası olan başka bir dplyrseçenek deacross

#devtools::install_github("tidyverse/dplyr")
library(dplyr)

x %>% 
  group_by(id1, id2) %>% 
  summarise(across(starts_with("val"), list(mean = mean, n = length)))

Sonuç

# 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’
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.