计算平均每组(平均分组)
我有一个大的数据框架看起来类似于这样的:
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
aggregate
, split
, plyr
, cast
, tapply
, data.table
, dplyr
等等。
一般来说,这些问题的forms是分裂应用合并。 哈德利·威克汉姆写了一篇漂亮的文章 ,让你更深入地了解整个问题的范畴,值得一读。 他的plyr
包实现了通用数据结构的策略,而dplyr
是一个针对dataframe进行调优的更新的实现性能。 它们允许解决相同forms的问题,但比这个更复杂。 作为解决数据操作问题的一般工具,它们是非常值得学习的。
性能是一个非常大的数据集的问题,因此很难打败基于data.table
解决scheme。 但是,如果只处理中等或更小的数据集,花时间学习data.table
可能不值得。 dplyr
也可以很快,所以如果你想加快速度的话,这是个不错的select,但是不太需要data.table
的可扩展性。
下面的许多其他解决scheme不需要任何额外的软件包。 其中一些在中大型数据集上甚至相当快。 他们的主要缺点是隐喻或灵活性。 通过比喻我的意思是说,它是一种为强迫别人解决特定types的问题而devise的工具。 我的意思是,他们缺乏解决范围广泛的类似问题或容易产生整齐的输出的能力。
例子
base
function
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))
plyr
( dplyr
)
以下是官方网页关于plyr
:
使用
base
R函数(如split
和apply
系列函数)已经可以做到这一点,但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)
像往常一样, data.table
有一点点的开销,所以平均来说,小数据集。 虽然这些都是微秒,所以差异是微不足道的。 任何方法在这里工作正常,你应该select基于:
- 你已经熟悉或想要熟悉的东西(
plyr
总是值得学习,因为它的灵活性;data.table
是值得学习,如果你打算分析巨大的数据集;by
aggregate
和split
都是基本的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)
然后data.table
或dplyr
使用data.table
的操作显然是要走的路。 某些方法( aggregate
和dcast
)开始显得非常缓慢。
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