按组计算平均值

Joj*_*Ono 52 r dataframe r-faq

我有一个类似于这个的大型数据框:

df <- data.frame(dive=factor(sample(c("dive1","dive2"),10,replace=TRUE)),speed=runif(10))
> df
    dive      speed
1  dive1 0.80668490
2  dive1 0.53349584
3  dive2 0.07571784
4  dive2 0.39518628
5  dive1 0.84557955
6  dive1 0.69121443
7  dive1 0.38124950
8  dive2 0.22536126
9  dive1 0.04704750
10 dive2 0.93561651
Run Code Online (Sandbox Code Playgroud)

我的目标是在另一列等于某个值时平均一列的值,并对所有值重复此值.即在上面的示例中,我想为列speed的每个唯一值返回列的平均值dive.所以当时dive==dive1,平均值speed是这个,依此类推dive.

Ari*_*man 102

有很多方法可以做到这一点R.具体来说,by,aggregate,split,和plyr,cast,tapply,data.table,dplyr,等等.

从广义上讲,这些问题的形式是分裂 - 应用 - 结合.哈德利威克姆写了一篇精彩的文章,可以让你更深入地了解整个问题类别,值得一读.他的plyr包实现了通用数据结构的策略,并且dplyr是针对数据帧调整的更新的实现性能.它们允许解决相同形式的问题,但比这个问题复杂得多.作为解决数据操作问题的通用工具,它们非常值得学习.

性能是非常大的数据集的一个问题,因此很难打败基于的解决方案data.table.但是,如果您只处理中型数据集或更小的数据集,那么花时间学习data.table可能不值得.dplyr也可以很快,所以如果你想加快速度,它是一个很好的选择,但不太需要可扩展性data.table.

以下许多其他解决方案不需要任何其他包.其中一些甚至在中大型数据集上相当快.它们的主要缺点是隐喻或灵活性.通过比喻,我的意思是它是一种工具,专门用于强迫其他东西以"聪明"的方式解决这类特殊问题.灵活性我的意思是他们缺乏解决各种类似问题或轻松产生整洁输出的能力.


例子

base 功能

tapply:

tapply(df$speed, df$dive, mean)
#     dive1     dive2 
# 0.5419921 0.5103974
Run Code Online (Sandbox Code Playgroud)

aggregate:

aggregate 接受data.frames,输出data.frames,并使用公式接口.

aggregate( speed ~ dive, df, mean )
#    dive     speed
# 1 dive1 0.5790946
# 2 dive2 0.4864489
Run Code Online (Sandbox Code Playgroud)

by:

在其最用户友好的形式中,它接收向量并向它们应用函数.但是,它的输出不是一个非常可操作的形式:

res.by <- by(df$speed, df$dive, mean)
res.by
# df$dive: dive1
# [1] 0.5790946
# ---------------------------------------
# df$dive: dive2
# [1] 0.4864489
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,对于简单的使用byas.data.frame该方法taRifx库的工作原理:

library(taRifx)
as.data.frame(res.by)
#    IDX1     value
# 1 dive1 0.6736807
# 2 dive2 0.4051447
Run Code Online (Sandbox Code Playgroud)

split:

顾名思义,它只执行split-apply-combine策略的"拆分"部分.为了完成其余工作,我将编写一个sapply用于apply-combine 的小函数. sapply尽可能自动地简化结果.在我们的例子中,这意味着一个向量而不是data.frame,因为我们只有一维结果.

splitmean <- function(df) {
  s <- split( df, df$dive)
  sapply( s, function(x) mean(x$speed) )
}
splitmean(df)
#     dive1     dive2 
# 0.5790946 0.4864489 
Run Code Online (Sandbox Code Playgroud)

外包装

data.table:

library(data.table)
setDT(df)[ , .(mean_speed = mean(speed)), by = dive]
#    dive mean_speed
# 1: dive1  0.5419921
# 2: dive2  0.5103974
Run Code Online (Sandbox Code Playgroud)

dplyr:

library(dplyr)
group_by(df, dive) %>% summarize(m = mean(speed))
Run Code Online (Sandbox Code Playgroud)

plyr(前面的dplyr)

以下是官方网页的内容plyr:

已经可以使用baseR函数(比如splitapply函数族)来plyr实现这一点,但是通过以下方式可以更轻松地实现:

  • 完全一致的名称,论据和输出
  • 通过foreach包装方便的并行化
  • 输入和输出到data.frames,matrices和lists
  • 进度条以跟踪长时间运行的操作
  • 内置错误恢复和信息性错误消息
  • 在所有转换中维护的标签

换句话说,如果你学习了一个用于分割 - 应用 - 组合操作的工具,它应该是plyr.

library(plyr)
res.plyr <- ddply( df, .(dive), function(x) mean(x$speed) )
res.plyr
#    dive        V1
# 1 dive1 0.5790946
# 2 dive2 0.4864489
Run Code Online (Sandbox Code Playgroud)

重塑2:

reshape2库未设计为split-apply-combine作为其主要焦点.相反,它使用两部分熔化/浇铸策略来执行各种数据整形任务.但是,由于它允许聚合功能,因此可以用于此问题.它不是我拆分应用组合操作的首选,但它的重塑功能非常强大,因此您也应该学习这个包.

library(reshape2)
dcast( melt(df), variable ~ dive, mean)
# Using dive as id variables
#   variable     dive1     dive2
# 1    speed 0.5790946 0.4864489
Run Code Online (Sandbox Code Playgroud)

基准

10行,2组

library(microbenchmark)
m1 <- microbenchmark(
  by( df$speed, df$dive, mean),
  aggregate( speed ~ dive, df, mean ),
  splitmean(df),
  ddply( df, .(dive), function(x) mean(x$speed) ),
  dcast( melt(df), variable ~ dive, mean),
  dt[, mean(speed), by = dive],
  summarize( group_by(df, dive), m = mean(speed) ),
  summarize( group_by(dt, dive), m = mean(speed) )
)

> print(m1, signif = 3)
Unit: microseconds
                                           expr  min   lq   mean median   uq  max neval      cld
                    by(df$speed, df$dive, mean)  302  325  343.9    342  362  396   100  b      
              aggregate(speed ~ dive, df, mean)  904  966 1012.1   1020 1060 1130   100     e   
                                  splitmean(df)  191  206  249.9    220  232 1670   100 a       
  ddply(df, .(dive), function(x) mean(x$speed)) 1220 1310 1358.1   1340 1380 2740   100      f  
         dcast(melt(df), variable ~ dive, mean) 2150 2330 2440.7   2430 2490 4010   100        h
                   dt[, mean(speed), by = dive]  599  629  667.1    659  704  771   100   c     
 summarize(group_by(df, dive), m = mean(speed))  663  710  774.6    744  782 2140   100    d    
 summarize(group_by(dt, dive), m = mean(speed)) 1860 1960 2051.0   2020 2090 3430   100       g 

autoplot(m1)
Run Code Online (Sandbox Code Playgroud)

基准10行

像往常一样,data.table有一点点开销,所以小数据集的平均值大约相同.然而,这些是微秒,所以差异是微不足道的.任何方法在这里工作正常,你应该选择:

  • 你已经很熟悉或者想要熟悉(plyr永远值得我们学习它的灵活性; data.table值得,如果你计划分析数据集庞大的学习,byaggregatesplit全部基础R功能,因此普遍适用)
  • 它返回什么输出(numeric,data.frame或data.table - 后者继承自data.frame)

1000万行,10组

但是,如果我们有一个大数据集怎么办?让我们尝试分成10组的10 ^ 7行.

df <- data.frame(dive=factor(sample(letters[1:10],10^7,replace=TRUE)),speed=runif(10^7))
dt <- data.table(df)
setkey(dt,dive)

m2 <- microbenchmark(
  by( df$speed, df$dive, mean),
  aggregate( speed ~ dive, df, mean ),
  splitmean(df),
  ddply( df, .(dive), function(x) mean(x$speed) ),
  dcast( melt(df), variable ~ dive, mean),
  dt[,mean(speed),by=dive],
  times=2
)

> print(m2, signif = 3)
Unit: milliseconds
                                           expr   min    lq    mean median    uq   max neval      cld
                    by(df$speed, df$dive, mean)   720   770   799.1    791   816   958   100    d    
              aggregate(speed ~ dive, df, mean) 10900 11000 11027.0  11000 11100 11300   100        h
                                  splitmean(df)   974  1040  1074.1   1060  1100  1280   100     e   
  ddply(df, .(dive), function(x) mean(x$speed))  1050  1080  1110.4   1100  1130  1260   100      f  
         dcast(melt(df), variable ~ dive, mean)  2360  2450  2492.8   2490  2520  2620   100       g 
                   dt[, mean(speed), by = dive]   119   120   126.2    120   122   212   100 a       
 summarize(group_by(df, dive), m = mean(speed))   517   521   531.0    522   532   620   100   c     
 summarize(group_by(dt, dive), m = mean(speed))   154   155   174.0    156   189   321   100  b      

autoplot(m2)
Run Code Online (Sandbox Code Playgroud)

基准1e7行,10组

那么data.table或者dplyr使用data.tables操作显然是要走的路.某些方法(aggregatedcast)开始变得非常缓慢.

1000万行,1,000组

如果你有更多的组,差异会变得更加明显.有1,000组和相同的10 ^ 7行:

df <- data.frame(dive=factor(sample(seq(1000),10^7,replace=TRUE)),speed=runif(10^7))
dt <- data.table(df)
setkey(dt,dive)

# then run the same microbenchmark as above
print(m3, signif = 3)
Unit: milliseconds
                                           expr   min    lq    mean median    uq   max neval    cld
                    by(df$speed, df$dive, mean)   776   791   816.2    810   828   925   100  b    
              aggregate(speed ~ dive, df, mean) 11200 11400 11460.2  11400 11500 12000   100      f
                                  splitmean(df)  5940  6450  7562.4   7470  8370 11200   100     e 
  ddply(df, .(dive), function(x) mean(x$speed))  1220  1250  1279.1   1280  1300  1440   100   c   
         dcast(melt(df), variable ~ dive, mean)  2110  2190  2267.8   2250  2290  2750   100    d  
                   dt[, mean(speed), by = dive]   110   111   113.5    111   113   143   100 a     
 summarize(group_by(df, dive), m = mean(speed))   625   630   637.1    633   644   701   100  b    
 summarize(group_by(dt, dive), m = mean(speed))   129   130   137.3    131   142   213   100 a     

autoplot(m3)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

所以data.table继续缩放好,dplyr操作上data.table也运作良好,与dplyrdata.frame接近一个数量级慢.该split/ sapply策略似乎在组数的扩展小(意为split()很可能慢,sapply快). by继续相对有效 - 在5秒时,它对用户来说肯定是显而易见的,但对于数据集这个大的仍然不是不合理的.不过,如果你经常有这种规模的数据集时,data.table显然是要走的路- 100%data.table为最佳性能或dplyrdplyr使用data.table作为一个可行的替代方案.

  • 神圣的打击。多么出色,精确而全面的答案。荣誉 (2认同)

Jam*_*mes 6

aggregate(speed~dive,data=df,FUN=mean)
   dive     speed
1 dive1 0.7059729
2 dive2 0.5473777
Run Code Online (Sandbox Code Playgroud)


Pie*_*une 5

dplyr 2015更新:

df %>% group_by(dive) %>% summarise(percentage = mean(speed))
Source: local data frame [2 x 2]

   dive percentage
1 dive1  0.4777462
2 dive2  0.6726483
Run Code Online (Sandbox Code Playgroud)