计算平均每组(平均分组)

我有一个大的数据框架看起来类似于这样的:

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 

我的目标是在另一列等于某个特定值时对一列的值求平均值,并对所有值重复此操作。 即在上面的例子中,我想返回列dive每个唯一值的列speed的平均值。 所以当dive==dive1时, dive==dive1的每个值的speed是这样的。

在R中有很多方法可以做到这一点。具体来说, by aggregatesplitplyrcasttapplydata.tabledplyr等等。

一般来说,这些问题的forms是分裂应用合并。 哈德利·威克汉姆写了一篇漂亮的文章 ,让你更深入地了解整个问题的范畴,值得一读。 他的plyr包实现了通用数据结构的策略,而dplyr是一个针对dataframe进行调优的更新的实现性能。 它们允许解决相同forms的问题,但比这个更复杂。 作为解决数据操作问题的一般工具,它们是非常值得学习的。

性能是一个非常大的数据集的问题,因此很难打败基于data.table解决scheme。 但是,如果只处理中等或更小的数据集,花时间学习data.table可能不值得。 dplyr也可以很快,所以如果你想加快速度的话,这是个不错的select,但是不太需要data.table的可扩展性。

下面的许多其他解决scheme不需要任何额外的软件包。 其中一些在中大型数据集上甚至相当快。 他们的主要缺点是隐喻或灵活性。 通过比喻我的意思是说,它是一种为强迫别人解决特定types的问题而devise的工具。 我的意思是,他们缺乏解决范围广泛的类似问题或容易产生整齐的输出的能力。


例子

basefunction

tapply

 tapply(df$speed, df$dive, mean) # dive1 dive2 # 0.5419921 0.5103974 

aggregate

aggregate需要data.frames,输出data.frames,并使用公式接口。

 aggregate( speed ~ dive, df, mean ) # dive speed # 1 dive1 0.5790946 # 2 dive2 0.4864489 

by

它以用户友好的forms呈现向量,并向其应用一个函数。 但是,其产出并不是非常可操作的forms:

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

为了解决这个问题, by taRifx库中的as.data.frame方法的简单使用工作:

 library(taRifx) as.data.frame(res.by) # IDX1 value # 1 dive1 0.6736807 # 2 dive2 0.4051447 

split

顾名思义,它只执行拆分应用组合策略的“拆分”部分。 为了使剩下的工作,我会写一个小函数,使用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 

外包装

data.table

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

dplyr

 library(dplyr) group_by(df, dive) %>% summarize(m = mean(speed)) 

plyrdplyr

以下是官方网页关于plyr

使用base R函数(如splitapply系列函数)已经可以做到这一点,但plyr使得它更容易:

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

换句话说,如果你学习了一个分割应用组合操作的工具,它应该是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 

重塑2

reshape2库不是以split-apply-combine作为主要关注点。 相反,它使用两部分融合/投射策略来执行各种数据重塑任务 。 但是,由于它允许聚合function,所以可以用于这个问题。 这不是我的第一select分裂应用联合作战,但其重塑能力是强大的,因此你应该学习这个包。

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

基准

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) 

基准10行

像往常一样, data.table有一点点的开销,所以平均来说,小数据集。 虽然这些都是微秒,所以差异是微不足道的。 任何方法在这里工作正常,你应该select基于:

  • 你已经熟悉或想要熟悉的东西( plyr总是值得学习,因为它的灵活性; data.table是值得学习,如果你打算分析巨大的数据集; by aggregatesplit都是基本的Rfunction,因此普遍提供)
  • 它返回的是什么输出(numeric,data.frame或data.table – 后者是从data.frameinheritance的)

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) 

基准1e7行,10组

然后data.tabledplyr使用data.table的操作显然是要走的路。 某些方法( aggregatedcast )开始显得非常缓慢。

1000万行,1000组

如果你有更多的团体,差异会变得更加明显。 1000个组和10 7 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) 

在这里输入图像描述

所以data.table继续扩展,并且在dplyr运行的data.table也运行良好, dplyr上的data.frame接近一个数量级的慢。 split / sapply策略似乎在组数方面performance不佳(意味着split()可能会很慢,而sapply很快)。 by继续是相对有效的 – 在5秒,这是绝对明显的用户,但对于这样一个大数据集仍然不是不合理的。 但是,如果您经常使用这种大小的数据集, data.table显然是一种可行的select – 100%的数据表可以获得最佳性能,或者dplyr使用data.table作为可行的select。

 aggregate(speed~dive,data=df,FUN=mean) dive speed 1 dive1 0.7059729 2 dive2 0.5473777 

2015更新与dplyr:

 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