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

다음과 같은 형식으로 데이터 프레임을 가져와야합니다.

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 이러한 유형의 후속 코딩 질문.
공역 이전

답변:


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.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 행이있는 테이블에서만 눈에 띄지 만 더 빠릅니다. 2.53 Ghz Core 2 Duo 프로세서 및 R 2.11.1이 포함 된 Macbook Pro의 타이밍 :

> 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 100k보다 작은 데이터 세트 에서조차도 실제로 ddply보다 훨씬 빠릅니다 (단 20k 행을 가진 데이터 세트가 있음). 내가 적용하는 기능과 관련이 있어야하지만 ddply는 몇 분과 data.table이 필요합니다.
atomules

간단한 오타 : 두 번째 코드 블록 dt <- data.table(dtf)대신에 의미가 있다고 생각합니다 dt <- data.table(dt). 이렇게 dt하면 stats패키지 의 함수가 아닌 데이터 프레임에서 데이터 테이블을 작성 하게 됩니다. 편집을 시도했지만 6 자 미만으로 편집 할 수 없습니다.
Christopher Bottoms

내 (이 경우 겸손하지는 않음) 의견 data.table은 데이터를 집계하는 가장 좋은 방법 이며이 답변은 훌륭하지만 여전히 표면을 긁습니다. 구문 적으로 우수 할뿐만 아니라 매우 유연하며 조인 및 내부 메커니즘을 포함하는 많은 고급 기능이 있습니다. 자세한 내용은 FAQ, 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)))
번만

@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
일에 대한 dplyr. 그것은 많은 R 작업을 단순하게 만들었으며 이러한 방법 중 많은 것이 쓸모 없었습니다.
gregmacfarlane

파이프 운영자 버전의 전체 사용은 불행히도 저에게는 작동하지 않습니다
dagcilibili

dplyr 또는 magrittr을로드하셨습니까?
바스 티아 안 퀘스트

해결책을 지적 해 주신 @bquast에게 감사드립니다. 요약 함수가 문제를 일으키는 plyr대신 호출되었습니다 dplyr.
dagcilibili

12

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.table보다 data.frame에서 흥미롭게 보입니다.

                                                              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루프 가 필요하지 않으므로 데이터 프레임 인라인 IMO를 구성 할 수 있습니다. 를 들어 tapply전화 사용 function(x) c(mean(x),sd(x)))cbind두 통계를 요청 영업 이익과 결과. 또한 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 (group = levels (factor (data $ group)), "mean"= 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 함수 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

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

아름답 지 않습니까? 패키지 textplot()기능을 사용하여이 테이블을 pdf로 내보낼 수 있습니다 gplots.

다른 사람의 솔루션 은 여기 를 참조 하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.