如何在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 2011年

@mpiktas谢谢您的注意。已更正。这些是语言环境问题(我是俄语)-我们使用逗号进行小数点分隔。
Yuriy Petrovskiy

3
我怀疑。除英国外,整个欧洲都使用逗号。
mpiktas 2011年

4
尽管不是英国人,但我更喜欢将点号用作小数点分隔符。
RomanLuštrik2011年

1
有关此类型的任何后续编码问题,请参见aggregatetapply然后是stackoverflow.com
conjugateprior

Answers:


140

这是使用ddplyplyr一线变体:

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]

尽管只有在具有10万行的表上才可以注意到这一点,但它速度更快。我的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 2011年

7
数据表+6000 即使对于小于100k的数据集(我的数据集只有2万行),它的确比ddply快得多。必须与我要应用的功能有关,但是ddply将花费几分钟,而data.table则需要几秒钟。
原子

简单的错字:我想你的意思dt <- data.table(dtf)dt <- data.table(dt)在第二个代码块中。这样,您是从数据框架而不是dtstats包中的函数创建数据表。我尝试对其进行编辑,但是我不能进行六个字符以下的编辑。
Christopher Bottoms 2014年

以我的观点(在这种情况下不要谦虚),这data.table是汇总数据的最佳方法,这个答案虽然很好,但仍然只是表面问题。除了在语法上优越之外,它还非常灵活,并且具有涉及连接和内部机​​制的许多高级功能。查看FAQ,github页面或课程以获取更多信息。
2014年

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 2011年

@lockedoff:谢谢您完成我的回答!
ocram 2011年

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 2014年

不幸的是,对于我来说,不能完全使用管道操作员版本
dagcilibili

您加载了dplyr或magrittr吗?
巴斯蒂安奎斯特

非常感谢@bquast指出解决方案,plyr而不是dplyr引起问题的原因是调用了summary函数。
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 2011年

10

我发现功能summaryBydoBy包是最方便的是:

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结果,因为OP要求提供两个统计信息。另外,ddplyplyr包中可以顺利完成此操作。
chl

@steffen问题是我需要我所描述的表结构。获取平均值和sd没有问题。问题在于结构。
Yuriy Petrovskiy

@chl:谢谢您的评论,不了解plyr :)。我添加了cbind,但其余部分保持不变。愿另一个人功劳,此答案应作为次优的例子。
steffen 2011年

@Yuriy:添加了cbind。如果您已经知道如何在每个组中应用功能,则可以重新编写问题(为清楚起见;)。
steffen 2011年

@steffen cbind("mean"=mperage,"stdev"=stperage) gives no 'group' column. Will be joining by cbind(group = levels(factor(data $ group)),“ mean” = mperage,“ stdev” = stperage)`对吗?
尤里·彼得罗夫斯基

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

有关其他解决方案,请参见此处

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.