จะสรุปข้อมูลตามกลุ่มใน R ได้อย่างไร [ปิด]


181

ฉันมีกรอบข้อมูล R ดังนี้:

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

ฉันต้องการรับ data frame ในแบบฟอร์มต่อไปนี้:

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

หมายเลขกลุ่มอาจแตกต่างกัน แต่สามารถรับชื่อและปริมาณได้โดยโทร levels(factor(data$group))

การปรับแต่งใดบ้างที่ควรทำกับข้อมูลเพื่อให้ได้ผลลัพธ์


เครื่องหมายจุลภาคในกรอบข้อมูลผลลัพธ์หมายถึงบางสิ่งที่พิเศษหรือเป็นเพียงทศนิยม?
mpiktas

@mpiktas ขอบคุณที่สังเกต การแก้ไข นี่เป็นปัญหาเกี่ยวกับสถานที่ (ฉันเป็นภาษารัสเซีย) - เราใช้เครื่องหมายจุลภาคสำหรับการแยกทศนิยม
Yuriy Petrovskiy

3
ฉันสงสัยว่า ทั้งหมดของยุโรปใช้เครื่องหมายจุลภาคยกเว้นอังกฤษ
mpiktas

4
แม้จะไม่ใช่คนอังกฤษ แต่ฉันชอบดอทสำหรับตัวแยกทศนิยม
Roman Luštrik

1
ดูaggregate, tapplyแล้วstackoverflow.comสำหรับคำถามการเข้ารหัสใดภายหลังประเภทนี้
conjugateprior

คำตอบ:


140

นี่คือตัวแปรของ plyrหนึ่งบรรทัดโดยใช้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))

นี่คือตัวแปรหนึ่งบรรทัดอีกรายการหนึ่งโดยใช้data package tableใหม่

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]

อันนี้เร็วกว่าแม้ว่าจะเห็นได้ชัดเจนบนโต๊ะที่มีแถว 100k เท่านั้น กำหนดเวลาใน Macbook Pro ของฉันพร้อมโปรเซสเซอร์ 2.53 Ghz Core 2 Duo และ R 2.11.1:

> 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 

ประหยัดได้มากขึ้นถ้าเราใช้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 มันทำให้ผมมีโอกาสที่จะลองใหม่นี้data.tableแพคเกจ มันดูมีแนวโน้มจริงๆ
mpiktas

7
+6000 สำหรับ data.table มันเร็วกว่า ddply มากจริงๆสำหรับฉันในชุดข้อมูลที่มีขนาดเล็กกว่า 100k (ฉันมีหนึ่งที่มีเพียง 20k แถว) ต้องเป็นสิ่งที่ต้องทำกับฟังก์ชั่นที่ฉันใช้ แต่ ddply จะใช้เวลานาทีและ data.table ไม่กี่วินาที
atomicules

พิมพ์ง่าย: ฉันคิดว่าคุณหมายถึงdt <- data.table(dtf)แทนที่จะเป็นdt <- data.table(dt)ในบล็อกรหัสที่สอง ด้วยวิธีนี้คุณกำลังสร้างตารางข้อมูลจาก data frame แทนที่จะเป็นdtfunction จากstatsแพ็คเกจ ฉันพยายามแก้ไข แต่ฉันไม่สามารถแก้ไขได้ด้วยอักขระหกตัว
Christopher Bottoms

ในความเห็นของฉัน (ไม่เจียมเนื้อเจียมตัวในกรณีนี้) data.tableเป็นวิธีที่ดีที่สุดในการรวบรวมข้อมูลและคำตอบนี้ยอดเยี่ยม แต่ก็ยังมีเพียงรอยขีดข่วนพื้นผิว นอกจากความโดดเด่นทางวากยสัมพันธ์แล้วมันยังมีความยืดหยุ่นสูงและมีคุณสมบัติขั้นสูงมากมายที่เกี่ยวข้องกับการรวมและกลไกภายใน ตรวจสอบคำถามที่พบบ่อยหน้า GitHub หรือหลักสูตรสำหรับข้อมูลเพิ่มเติม
geneorama

97

หนึ่งเป็นไปได้คือการใช้ฟังก์ชั่นรวม ตัวอย่างเช่น

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

ให้คอลัมน์ที่สองของผลลัพธ์ที่ต้องการ


1
อย่าเชื่อมโยงไปยังเซิร์ฟเวอร์ความช่วยเหลือในพื้นที่ของคุณ :-) +1 แต่เห็นความคิดเห็นของฉันต่อการตอบสนองของ @ steffen
chl

ทำสิ่งนั้นด้วยการโทร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))แต่ฉันไม่ได้คิดว่ามันเป็นวิธีที่ถูกต้อง ฉันไม่แน่ใจว่าจะเกิดอะไรขึ้นผลลัพธ์ของคอลัมน์ที่ถูกผูกไว้จะอยู่ในลำดับที่แตกต่างกัน (ฉันคิดว่าเป็นไปได้) คู่ต่อสู้ของคุณคืออะไร?
Yuriy Petrovskiy

9
@Yuriy แถวไม่ควรออกนอกลู่นอกทาง แต่นี่เป็นวิธีหนึ่งที่จะเรียกมันได้ที่aggregate():aggregate(age ~ group, data=dat, FUN = function(x) c(M=mean(x), SD=sd(x)))
lockoff

@lockedoff: ขอบคุณสำหรับคำตอบของฉัน!
ocram

27

เนื่องจากคุณกำลังจัดการเฟรมข้อมูลdplyrแพ็กเกจน่าจะเป็นวิธีที่เร็วกว่าที่จะทำ

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

หรือเทียบเท่าโดยใช้ตัวดำเนินการdplyr/ magrittrไปป์:

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

แก้ไขการใช้งานเต็มรูปแบบของผู้ควบคุมท่อ:

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
+1 dplyrสำหรับ มันทำให้งาน R หลายอย่างง่ายและวิธีการเหล่านี้ล้าสมัยมากมาย
gregmacfarlane

การใช้งานเต็มรูปแบบของผู้ปฏิบัติงาน
ไพพ์

คุณโหลด dplyr หรือ magrittr หรือไม่?
Bastiaan Quast

ขอบคุณมาก @bquast สำหรับการชี้ไปที่วิธีการแก้ปัญหาฟังก์ชั่นการสรุปถูกเรียกจากplyrแทนที่จะdplyrเป็นสาเหตุของปัญหา
dagcilibili

12

เยี่ยมมากขอบคุณ bquast สำหรับการเพิ่มโซลูชัน dplyr!

ปรากฎว่า dplyr และ data.table อยู่ใกล้มาก:

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 ยังคงเร็วที่สุดตามด้วย dplyr () ซึ่งใกล้เคียงกับ data.frame ที่น่าสนใจยิ่งกว่า data.table:

                                                              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

ตอนแรกฉันคิดว่าคุณต้องย้าย setkey ไปเป็นเกณฑ์มาตรฐาน แต่กลับกลายเป็นว่าแทบไม่ต้องใช้เวลาเลย
kasterma

10

นอกเหนือจากคำแนะนำที่มีอยู่คุณอาจต้องการตรวจสอบdescribe.byฟังก์ชันในpsychแพ็คเกจ

มันจัดทำสถิติเชิงพรรณนาจำนวนหนึ่งรวมถึงค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานตามตัวแปรการจัดกลุ่ม


มันดี แต่ค่อนข้างยุ่งยากในการส่งออกไปยัง LaTeX IME
richiemorrisroe

10

ฉันได้พบฟังก์ชั่นsummaryByในแพคเกจ doByจะสะดวกที่สุดสำหรับเรื่องนี้:

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

ใช้sqldfแพ็คเกจ สิ่งนี้อนุญาตให้คุณใช้ SQL เพื่อสรุปข้อมูล เมื่อคุณโหลดมันคุณสามารถเขียนสิ่งที่ชอบ -

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

8

แก้ไข:ตามคำแนะนำของ chl

ฟังก์ชั่นที่คุณกำลังมองหาเรียกว่า "tapply" ซึ่งใช้ฟังก์ชั่นต่อกลุ่มที่ระบุโดยปัจจัย

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

ฉันขอแนะนำให้ทำงานผ่านบทช่วยสอนพื้นฐาน R ที่อธิบายโครงสร้างข้อมูลและวิธีการที่ใช้กันทั่วไปทั้งหมด มิฉะนั้นคุณจะติดทุกนิ้วระหว่างการเขียนโปรแกรม ดูคำถามนี้เพื่อรวบรวมทรัพยากรที่มีอยู่ฟรี


2
@steffen +1 แต่ไม่จำเป็นต้องforวนซ้ำที่นี่คุณสามารถสร้าง dataframe ของคุณแบบอินไลน์ IMO สำหรับการtapplyโทรให้ใช้function(x) c(mean(x),sd(x)))และcbindผลลัพธ์ตามที่ OP ขอให้ทั้งสองสถิติ นอกจากนี้ddplyจากแพ็คเกจplyrสามารถทำสิ่งนี้ได้อย่างราบรื่น
chl

@steffen ปัญหาคือฉันต้องการโครงสร้างตารางที่ฉันอธิบาย ไม่มีปัญหากับการหาวิธีการและ sd ปัญหาเกิดขึ้นกับโครงสร้าง
Yuriy Petrovskiy

@chl: ขอบคุณสำหรับความคิดเห็นของคุณไม่ทราบเกี่ยวกับ plyr :) ฉันเพิ่ม cbind แต่ไม่เหลือส่วนที่เหลือแตะ ขอให้อีกคนหนึ่งเอาเครดิตคำตอบนี้จะยังคงเป็นตัวอย่างที่เหมาะสมที่สุด
steffen

@Yuriy: เพิ่ม cbind หากคุณทราบวิธีการใช้ฟังก์ชั่นต่อกลุ่มคุณสามารถกำหนดคำถามของคุณใหม่ (เพื่อความชัดเจน;)
steffen

@steffen cbind("mean"=mperage,"stdev"=stperage) gives no 'group' column. Will be joining by cbind (กลุ่ม = ระดับ (ปัจจัย (ข้อมูล $ กลุ่ม)), "หมายถึง" = mperage "STDEV" = stperage) `ถูกต้องหรือไม่
Yuriy Petrovskiy

7

นี่คือตัวอย่างของฟังก์ชั่นที่aggregates()ฉันทำเองเมื่อไม่นานมานี้:

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

มันให้ผลลัพธ์ดังต่อไปนี้:

> 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

บางทีคุณอาจได้ผลลัพธ์เดียวกันโดยเริ่มจากการแบ่งฟังก์ชัน R ():

> 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

ขอผมกลับไปที่เอาท์พุทของaggregatesฟังก์ชั่น คุณสามารถเปลี่ยนมันในตารางที่สวยงามใช้reshape(), xtabs()และ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")) 

สิ่งนี้ให้:

> 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

สวยใช่มั้ย คุณสามารถส่งออกตารางนี้เป็น pdf ด้วยtextplot()ฟังก์ชั่นของgplotsแพคเกจ

ดูที่นี่สำหรับวิธีแก้ไขปัญหาของผู้อื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.