我有一个类似于此的大型数据框:df <- data.frame(dive = factor(sample(c(\'dive1\',\'dive2\'), 10, replace=TRUE)), speed = runif(10) ...
我有一个与此类似的大数据框:
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
我的目标是当另一列等于某个值时获取一列值的平均值,并对所有值重复此操作。即在上面的例子中,我想为该列 speed
的每个唯一值 dive
。因此 dive==dive1
,当的平均值 speed
是这样的,对于每个值也是如此 dive
.
在 R 中有很多方法可以做到这一点。具体来说,, by
, aggregate
, split
和 plyr
, cast
, tapply
, data.table
, dplyr
,等等。
广义上讲,这些问题属于拆分-应用-合并的形式。Hadley Wickham 写了一篇 精彩的文章 ,可以让您更深入地了解整个问题类别,非常值得一读。他的 plyr
软件包实现了通用数据结构的策略,并且 dplyr
是针对数据框架进行性能调整的较新的实现。它们允许解决与此形式相同但更复杂的问题。作为解决数据操作问题的通用工具,它们非常值得学习。
性能是处理非常大的数据集时的一个问题,因此很难找到基于 的解决方案 data.table
。但是,如果你只处理中等或更小的数据集,花时间学习 data.table
可能不值得。 dplyr
也可以很快,所以如果你想加快速度,但不需要 的可扩展性,这是一个不错的选择 data.table
.
下面的许多其他解决方案不需要任何额外的软件包。其中一些甚至在中大型数据集上也相当快。它们的主要缺点是隐喻或灵活性。我所说的隐喻是指它是一种设计用于强制其他东西以“聪明”的方式解决这种特定类型问题的工具。我所说的灵活性是指它们缺乏解决广泛类似问题或轻松产生整洁输出的能力。
base
功能
点击应用:
tapply(df$speed, df$dive, mean)
# dive1 dive2
# 0.5419921 0.5103974
总计的
:
aggregate
接收数据框,输出数据框,并使用公式界面。
aggregate( speed ~ dive, df, mean )
# dive speed
# 1 dive1 0.5790946
# 2 dive2 0.4864489
经过
:
在其最用户友好的形式中,它接受向量并对其应用函数。但是,其输出的形式不太易于操作。:
res.by <- by(df$speed, df$dive, mean)
res.by
# df$dive: dive1
# [1] 0.5790946
# ---------------------------------------
# df$dive: dive2
# [1] 0.4864489
为了解决这个问题,可以简单使用 by
中的 as.data.frame
方法 taRifx
:
library(taRifx)
as.data.frame(res.by)
# IDX1 value
# 1 dive1 0.6736807
# 2 dive2 0.4051447
分裂
:
顾名思义,它只执行拆分-应用-合并策略中的“拆分”部分。为了使其余部分正常工作,我将编写一个用于 sapply
应用-合并的小函数。 sapply
自动尽可能简化结果。在我们的例子中,这意味着一个向量而不是一个数据框,因为我们只有 1 个结果维度。
splitmean <- function(df) {
s <- split( df, df$dive)
sapply( s, function(x) mean(x$speed) )
}
splitmean(df)
# dive1 dive2
# 0.5790946 0.4864489
数据表 :
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
:
R 函数(例如
base
和split
函数系列)apply
已经可以做到这一点plyr
使用以下命令可以使一切变得更容易一些:
- 完全一致的名称、参数和输出
- 通过
foreach
包- 数据框、矩阵和列表的输入和输出
- 进度条用于跟踪长时间运行的操作
- 内置错误恢复和信息错误消息
- 在所有转换中维护的标签
换句话说,如果你学习一种拆分-应用-组合操作工具,它应该是 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
库的设计主要重点不是拆分-应用-合并。相反,它使用两部分熔化/铸造策略来执行 各种数据重塑任务 。但是,由于它允许聚合函数,因此可以用于此问题。它不是我进行拆分-应用-合并操作的首选,但它的重塑功能非常强大,因此您也应该学习这个包。
library(reshape2)
dcast( melt(df), variable ~ dive, mean)
# Using dive as id variables
# variable dive1 dive2
# 1 speed 0.5790946 0.4864489
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
开销稍大一些,因此对于小型数据集来说大约是平均水平。不过,这些是微秒,因此差异很小。任何一种方法都可以在这里正常工作,您应该根据以下情况进行选择:
plyr
因其灵活性而值得学习; data.table
如果您打算分析庞大的数据集,则值得学习; by
并且 aggregate
和 split
都是基本 R 函数,因此可以普遍使用)
但是如果我们有一个大数据集怎么办?让我们尝试将 10^7 行分成 10 组。
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
s 进行操作显然是可行的方法。某些方法( aggregate
和 dcast
)开始看起来非常慢。
如果您有更多组,差异会更加明显。对于 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)
因此, data.table
继续很好地扩展,并且 dplyr
对 的操作 data.table
也运行良好,而 dplyr
则 data.frame
慢了近一个数量级。 split
/ sapply
策略似乎在组数方面扩展性较差(这意味着 split()
可能很慢, 很快 sapply
)。 by
继续相对高效 - 在 5 秒内,用户肯定能注意到,但对于如此大的数据集来说,这仍然不算不合理。不过,如果您经常处理这种大小的数据集, data.table
显然是最佳选择 - 100% data.table 以获得最佳性能或 dplyr
使用 dplyr
作为 data.table
可行的替代方案。