Veriler R'deki gruba göre nasıl özetlenir? [kapalı]


181

Bunun gibi R veri çerçevem ​​var:

        age group
1   23.0883     1
2   25.8344     1
3   29.4648     1
4   32.7858     2
5   33.6372     1
6   34.9350     1
7   35.2115     2
8   35.2115     2
9   35.2115     2
10  36.7803     1
...

Veri çerçevesini aşağıdaki biçimde almam gerekiyor:

group mean     sd
1     34.5     5.6
2     32.3     4.2
...

Grup numarası değişebilir, ancak isimleri ve miktarları arayarak elde edilebilir. levels(factor(data$group))

Sonucu elde etmek için verilerde hangi manipülasyonlar yapılmalıdır?


Sonuç veri çerçevesindeki virgüller özel bir şey ifade ediyor mu, yoksa sadece ondalık sayı mı?
mpiktas

@mpiktas Not ettiğiniz için teşekkür ederiz. Düzeltildi. Bunlar yerel meselelerdi (ben rusça) - ondalık ayırma için virgül kullanıyoruz.
Yuriy Petrovskiy

3
Bundan şüphelenmiştim. Avrupa’nın tamamı İngilizler dışında virgül kullanıyor.
mpiktas

4
İngiliz olmama rağmen, ondalık ayırıcı için noktayı tercih ediyorum.
Roman Luštrik

1
Bu türden sonraki kodlama soruları için bkz aggregate. tapply, Ve ardından stackoverflow.com .
conjugateprior

Yanıtlar:


140

İşte plyr kullanarak tek satır varyantı ddply :

dt <- data.frame(age=rchisq(20,10),group=sample(1:2,20,rep=T))
ddply(dt,~group,summarise,mean=mean(age),sd=sd(age))

Yeni paket data.table kullanarak başka bir satır varyantı .

dtf <- data.frame(age=rchisq(100000,10),group=factor(sample(1:10,100000,rep=T)))
dt <- data.table(dtf)
dt[,list(mean=mean(age),sd=sd(age)),by=group]

Bu daha hızlıdır, ancak bu yalnızca 100k satır içeren tablolarda farkedilir. 2.53 Ghz Core 2 Duo işlemci ve R 2.11.1 ile Macbook Pro'mdaki zamanlamalar:

> system.time(aa <- ddply(dtf,~group,summarise,mean=mean(age),sd=sd(age)))
utilisateur     système      écoulé 
      0.513       0.180       0.692 
> system.time(aa <- dt[,list(mean=mean(age),sd=sd(age)),by=group])
utilisateur     système      écoulé 
      0.087       0.018       0.103 

Kullanırsak daha fazla tasarruf mümkündür setkey:

> setkey(dt,group)
> system.time(dt[,list(mean=mean(age),sd=sd(age)),by=group])
utilisateur     système      écoulé 
      0.040       0.007       0.048 

2
@chl, bana bu yeni data.table paketini deneme şansı verdi . Gerçekten umut verici görünüyor.
mpiktas

7
Data.table için +6000. 100d'den küçük veri setlerinde bile benim için ddply'dan çok daha hızlı (sadece 20k satırlı bir tane var). Uyguladığım fonksiyonlarla ilgili bir şey olmalı, ancak ddply birkaç dakika ve data.table alacaktır.
atomik,

Basit yazım hatası: İkinci kod bloğu dt <- data.table(dtf)yerine demek istediğini düşünüyorum dt <- data.table(dt). Bu şekilde, veri tablosunu paketin dtişlevinden ziyade veri çerçevesinden yaratıyorsunuz stats. Düzenlemeyi denedim, ancak altı karakterin altında düzenlemeler yapamam.
Christopher Bottoms

Benim düşünceme göre (bu durumda alçak gönüllü değil) data.table, veri toplamanın en iyi yoludur ve bu cevap harika, ama yine de sadece yüzeyi çiziyor. Sözdizimsel olarak üstün olmasının yanı sıra, aynı zamanda oldukça esnektir ve birleşme ve iç mekaniği içeren birçok gelişmiş özelliğe sahiptir. Daha fazla bilgi için SSS bölümüne, github sayfasına veya kursa bakın.
geneorama

97

Bir olasılık, toplama işlevini kullanmaktır . Örneğin,

aggregate(data$age, by=list(data$group), FUN=mean)[2]

size istenen sonucun ikinci sütununu verir.


1
Yerel yardım sunucunuza :-) +1 bağlantı vermeyin, ancak yorumlarımı @ steffen kullanıcısının yanıtına bakın.
chl

Arayarak bir şey data.frame(group=levels(factor(data$group)),mean=(aggregate(data$age, by=list(data$group), FUN=mean)$x),sd=(aggregate(data$age, by=list(data$group), FUN=sd)$x))yaptım ama bunun doğru yol olduğundan emin değilim. Bağlanmış sütunların sonuçlarının farklı sırayla olacağından emin olacağımı sanmıyorum (bunun mümkün olduğunu düşünüyorum). Senin fikrin nedir?
Yuriy Petrovskiy

9
@Yuriy Satırlar sıra dışı olmamalıdır, ancak işte bir çağrı yapmanın bir yolu aggregate():aggregate(age ~ group, data=dat, FUN = function(x) c(M=mean(x), SD=sd(x)))
lockedoff 14:11

@lockedoff: Cevabımı tamamladığınız için teşekkür ederiz!
ocram

27

Bir veri çerçevesini değiştirdiğiniz için, dplyrpaket muhtemelen bunu yapmanın en hızlı yoludur.

library(dplyr)
dt <- data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T))
grp <- group_by(dt, group)
summarise(grp, mean=mean(age), sd=sd(age))

veya dplyr/ magrittrpipe operatörünü kullanarak eşdeğerde :

library(dplyr)
dt <- data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T))
group_by(dt, group) %>%
 summarise(mean=mean(age), sd=sd(age))

EDIT boru operatörünün tam kullanımı:

library(dplyr)
data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T)) %>%
  group_by(group) %>%
  summarise(mean=mean(age), sd=sd(age))

3
İçin +1 dplyr. Birçok R görevini basitleştirdi ve bu yöntemlerin çoğu modası geçmiş oldu.
gregmacfarlane

Pipo operatörü versiyonunun tam kullanımı maalesef işe yaramıyor
dagcilibili

dplyr veya magrittr yüklediniz mi?
Bastiaan Quast

çözüme yönelik gösterdiğiniz için teşekkür ederiz. Özetle , soruna neden olan işlev plyryerine çağrıldı dplyr.
dagcilibili

12

Harika, dplyr çözümünü eklediğiniz için teşekkürler!

O zaman, dplyr ve data.table'ın çok yakın olduğu ortaya çıktı:

library(plyr)
library(dplyr)
library(data.table)
library(rbenchmark)

dtf <- data.frame(age=rchisq(100000,10),group=factor(sample(1:10,100000,rep=T)))
dt <- data.table(dtf)

setkey(dt,group)

a<-benchmark(ddply(dtf,~group,plyr:::summarise,mean=mean(age),sd=sd(age)),
         dt[,list(mean=mean(age),sd=sd(age)),by=group],
         group_by(dt, group) %>% summarise(mean=mean(age),sd=sd(age) ),
         group_by(dtf, group) %>% summarise(mean=mean(age),sd=sd(age) )
)

a[, c(1,3,4)]

data.table, data.frame'de data.table'dan ilginç bir şekilde daha hızlı görünen dplyr () ile yakından takip ederek hala en hızlı olanıdır:

                                                              test elapsed relative
1 ddply(dtf, ~group, plyr:::summarise, mean = mean(age), sd = sd(age))   1.689    4.867
2               dt[, list(mean = mean(age), sd = sd(age)), by = group]   0.347    1.000
4   group_by(dtf, group) %>% summarise(mean = mean(age), sd = sd(age))   0.369    1.063
3    group_by(dt, group) %>% summarise(mean = mean(age), sd = sd(age))   0.580    1.671

İlk başta setkey'i benchmarkın içine sokmanız gerektiğini düşündüm, ancak bunun neredeyse hiç zaman almadığı ortaya çıktı.
kasterma

10

Mevcut önerilere ek olarak describe.by, psychpaketteki işlevi kontrol etmek isteyebilirsiniz .

Bir gruplama değişkenine dayalı ortalama ve standart sapma dahil olmak üzere bir dizi tanımlayıcı istatistik sağlar.


LaTeX IME'ye ihraç etmek hoş ama biraz zor.
richiemorrisroe

10

Ben işlevi bulduk summaryByyılında Doby paketinin bunun için en uygun olduğu:

library(doBy)

age    = c(23.0883, 25.8344, 29.4648, 32.7858, 33.6372,
           34.935,  35.2115, 35.2115,  5.2115, 36.7803)
group  = c(1, 1, 1, 2, 1, 1, 2, 2, 2, 1)
dframe = data.frame(age=age, group=group)

summaryBy(age~group, data=dframe, FUN=c(mean, sd))
# 
#   group age.mean    age.sd
# 1     1 30.62333  5.415439
# 2     2 27.10507 14.640441

9

sqldfPaketi kullan . Bu, verileri özetlemek için şimdi SQL kullanmanıza izin verir. Bir kez yükledikten sonra gibi bir şey yazabilirsiniz -

sqldf('  select group,avg(age) from data group by group  ')

8

Düzenlendi: chl'nin önerisine göre

Aradığınız işleve, bir faktör tarafından belirtilen grup başına bir işlev uygulayan "tapply" denir.

# create some artificial data
set.seed(42)
groups <- 5

agedat <- c()
groupdat <- c()

for(group in 1:groups){
    agedat <- c(agedat,rnorm(100,mean=0 + group,1/group))
    groupdat <- c(groupdat,rep(group,100))
}
dat <- data.frame("age"=agedat,"group"=factor(groupdat))

# calculate mean and stdev age per group
res <- rbind.data.frame(group=1:5, with(dat, tapply(age, group, function(x) c(mean(x), sd(x)))))
names(res) <- paste("group",1:5)
row.names(res)[2:3] <- c("mean","sd")

Gerçekten yaygın olarak kullanılan tüm veri yapılarını ve yöntemlerini açıklayan temel bir çalışma kılavuzu ile çalışmanızı öneririm. Aksi takdirde, programlama sırasında her santim sıkışmış olacaksınız. Ücretsiz kullanılabilir kaynakların toplanması için bu soruya bakın .


2
@ steffen +1 ancak burada bir fordöngüye gerek yoktur , veri çerçevesi satır içi IMO'nuzu yapılandırabilirsiniz. İçin tapplyçağrı kullanım function(x) c(mean(x),sd(x)))ve cbindher iki istatistik istedi OP ile sonucu. Ayrıca, ddplygelen plyr paketin sorunsuz yapabiliriz.
chl

@ steffen Sorun tam olarak tarif ettiğim masa yapısına ihtiyacım var. Ortalama ve sd alma konusunda hiçbir sorun yok. Sorun stucture ile.
Yuriy Petrovskiy

@chl: Yorumunuz için teşekkür ederiz, plyr :) hakkında bir şey bilmiyordum. Cbind ekledim, ama gerisini el değmemiş bıraktım. Başka biri kredi alansa, bu cevap daha az optimal bir örnek olarak kalacaktır.
steffen

@Yuriy: Eklenen cbind. Her grup için fonksiyonların nasıl uygulanacağını zaten bildiyseniz, sorunuzu yeniden şekillendirebilirsiniz (sadece açıklık için;)).
steffen

@ steffen cbind cbind("mean"=mperage,"stdev"=stperage) gives no 'group' column. Will be joining by (grup = seviye (faktör (veri $ grup))), "ortalama" = mperage, "stdev" = stperage) `doğru mu?
Yuriy Petrovskiy

7

Bir aggregates()süre önce kendim yaptığım işleve bir örnek :

# simulates data
set.seed(666)
( dat <- data.frame(group=gl(3,6), level=factor(rep(c("A","B","C"), 6)), 
                    y=round(rnorm(18,10),1)) )

> dat
   group level    y
1      1     A 10.8
2      1     B 12.0
3      1     C  9.6
4      1     A 12.0
5      1     B  7.8
6      1     C 10.8
7      2     A  8.7
8      2     B  9.2
9      2     C  8.2
10     2     A 10.0
11     2     B 12.2
12     2     C  8.2
13     3     A 10.9
14     3     B  8.3
15     3     C 10.1
16     3     A  9.9
17     3     B 10.9
18     3     C 10.3

# aggregates() function
aggregates <- function(formula, data=NULL, FUNS){ 
    if(class(FUNS)=="list"){ 
        f <- function(x) sapply(FUNS, function(fun) fun(x)) 
    }else{f <- FUNS} 
    temp <- aggregate(formula, data, f) 
    out <- data.frame(temp[,-ncol(temp)], temp[,ncol(temp)]) 
    colnames(out)[1] <- colnames(temp)[1] 
return(out) 
} 

# example 
FUNS <- function(x) c(mean=round(mean(x),0), sd=round(sd(x), 0)) 
( ag <- aggregates(y~group:level, data=dat, FUNS=FUNS) ) 

Aşağıdaki sonucu verir:

> ag
  group level mean sd
1     1     A   11  1
2     2     A    9  1
3     3     A   10  1
4     1     B   10  3
5     2     B   11  2
6     3     B   10  2
7     1     C   10  1
8     2     C    8  0
9     3     C   10  0

Belki aynı sonucu R işlevinden başlayarak split ():

> with(dat, sapply( split(y, group:level), FUNS ) )
     1:A 1:B 1:C 2:A 2:B 2:C 3:A 3:B 3:C
mean  11  10  10   9  11   8  10  10  10
sd     1   3   1   1   2   0   1   2   0

aggregatesFonksiyonun çıktısına geri döneyim. Sen kullanarak güzel bir tabloda dönüştürebilir reshape(), xtabs()ve ftable():

rag <- reshape(ag, varying=list(3:4), direction="long", v.names="y") 
rag$time <- factor(rag$time) 
ft <- ftable(xtabs(y~group+level+time, data=rag)) 
attributes(ft)$col.vars <- list(c("mean","sd")) 

Bu verir:

> ft 
             mean sd
group level         
1     A        11  1
      B        10  3
      C        10  1
2     A         9  1
      B        11  2
      C         8  0
3     A        10  1
      B        10  2
      C        10  0

Güzel değil mi? Paketin textplot()işleviyle bu tabloyu bir pdf dosyasına aktarabilirsiniz gplots.

Başkalarının çözümleri için buraya bakın .

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.