提问人:Jojo 提问时间:7/19/2012 最后编辑:Karolis KoncevičiusJojo 更新时间:5/19/2023 访问量:210591
按组计算均值
Calculate the mean by group
问:
我有一个大数据框,看起来类似于这个:
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
我的目标是在一列等于某个值时获得一列值的平均值,并对所有值重复此操作。即在上面的示例中,我想为列的每个唯一值返回列的平均值。因此,当 时,对于 的每个值,的平均值为 this,依此类推。speed
dive
dive==dive1
speed
dive
答:
aggregate(speed~dive,data=df,FUN=mean)
dive speed
1 dive1 0.7059729
2 dive2 0.5473777
在 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
接收 data.frames,输出 data.frames,并使用公式接口。
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
拆分
:
顾名思义,它只执行拆分-应用-合并策略的“拆分”部分。为了使其余部分正常工作,我将编写一个用于 apply-combine 的小函数。 自动尽可能简化结果。在我们的例子中,这意味着一个向量而不是 data.frame,因为我们只有 1 个维度的结果。sapply
sapply
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
已经可以使用 R 函数(如 和 函数系列),但让这一切变得更容易一些 跟:
base
split
apply
plyr
- 完全一致的名称、参数和输出
- 通过软件包方便并行化
foreach
- Data.Frames、矩阵和列表的输入和输出
- 进度条,用于跟踪长时间运行的操作
- 内置错误恢复和信息丰富的错误消息
- 在所有转换中维护的标签
换句话说,如果您学习一种用于拆分-应用-合并操作的工具,它应该是 。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
基准
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
- 你已经熟悉或想要熟悉的东西(总是值得学习的,因为它的灵活性; 如果您打算分析庞大的数据集,则值得学习; 和 和 都是基本 R 函数,因此普遍可用)
plyr
data.table
by
aggregate
split
- 它返回的输出(numeric、data.frame 或 data.table,后者继承自 data.frame)
1000 万行,10 组
但是,如果我们有一个大数据集呢?让我们尝试 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)
那么,或者使用对 s 进行操作显然是要走的路。某些方法(和)开始看起来非常缓慢。data.table
dplyr
data.table
aggregate
dcast
1000 万行,1000 组
如果有更多的组,差异就会更加明显。有 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)
因此,继续很好地扩展,并且在上运行也很好,但速度慢了近一个数量级。/ 策略似乎在组数上扩展得很差(这意味着 可能很慢,而 很快)。 仍然相对高效 - 在 5 秒时,它对用户来说绝对是引人注目的,但对于这么大的数据集来说仍然不是不合理的。不过,如果您经常使用这种大小的数据集,这显然是要走的路 - 100% data.table 以获得最佳性能或用作可行的替代方案。data.table
dplyr
data.table
dplyr
data.frame
split
sapply
split()
sapply
by
data.table
dplyr
dplyr
data.table
评论
microbenchmark
ggplot2
dplyr
dplyr
data.table
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
我们已经有大量的选项可以按组获得平均值,从包中再添加一个。mosaic
mosaic::mean(speed~dive, data = df)
#dive1 dive2
#0.579 0.440
这将返回一个命名的数值向量,如果需要,我们可以将其包装在数据帧中stack
stack(mosaic::mean(speed~dive, data = df))
# values ind
#1 0.579 dive1
#2 0.440 dive2
数据
set.seed(123)
df <- data.frame(dive=factor(sample(c("dive1","dive2"),10,replace=TRUE)),
speed=runif(10))
添加替代的 base R 方法,该方法在各种情况下都保持快速。
rowsummean <- function(df) {
rowsum(df$speed, df$dive) / tabulate(df$dive)
}
借用@Ari的基准:
10 行,2 组
1000 万行,10 组
1000 万行,1000 组
用collapse
library(collapse)
library(magrittr)
df %>%
fgroup_by(dive) %>%
fsummarise(speed = fmean(speed))
# dive speed
#1 dive1 0.5788479
#2 dive2 0.4401514
数据
set.seed(123)
df <- data.frame(dive=factor(sample(c("dive1","dive2"),10,replace=TRUE)),
speed=runif(10))
使用新功能:across
df %>%
group_by(dive) %>%
summarise(across(speed, mean, na.rm = TRUE))
RCchelsie 提供的扩展答案 - 如果有人想获得数据帧中所有列的均值按组计算:
df %>%
group_by(dive) %>%
summarise(across(.cols=everything(), mean, na.rm=TRUE))
使用 dplyr(及更高版本),我们可以暂时使用参数进行分组。1.1.0
.by
这使得代码更短(正如我们避免的 和 语句),并且始终返回未分组的数据帧。group_by
ungroup
.by
library(dplyr)
df %>% summarise(speed = mean(speed), .by = dive)
# dive speed
#1 dive1 0.5788479
#2 dive2 0.4401514
你可以吃你的蛋糕,也可以吃它。timeplyr
我说的蛋糕是指语法,吃它是指蛋糕超级快。tidy
stat_summarise()
混合使用 ,并在不牺牲语法的情况下实现最佳性能。collapse
data.table
dplyr
tidy
将最有效的方法与timeplyr等效方法进行比较,我们可以看到可比的速度。data.table
# remotes::install_github("NicChr/timeplyr")
library(plyr)
library(dplyr)
library(ggplot2)
library(timeplyr)
library(data.table)
dt <- data.table(dive = factor(sample.int(10^6, size = 10^7, replace=TRUE)),
speed = runif(10^7))
setkey(dt, dive)
m2 <- microbenchmark::microbenchmark(
dt[,mean(speed),by=dive],
stat_summarise(dt, .cols = "speed",
.by = dive, stat = "mean", sort = F),
times = 15
)
print(m2, signif = 3)
#> Unit: milliseconds
#> expr
#> dt[, mean(speed), by = dive]
#> stat_summarise(dt, .cols = "speed", .by = dive, stat = "mean", sort = F)
#> min lq mean median uq max neval cld
#> 148 184 272 261 344 499 15 a
#> 139 197 283 221 328 540 15 a
autoplot(m2)
#> Coordinate system already present. Adding new coordinate system, which will
#> replace the existing one.
1000 万行,~ 100 万组
在比较内存使用率时,实际上比 .stat_summarise()
data.table
# Memory comparison
bench::mark(
DT = dt[, list(speed = mean(speed)), by = dive],
TP = stat_summarise(dt, .cols = "speed",
.by = dive, stat = "mean", sort = F),
check = FALSE
)
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 DT 194ms 308ms 3.25 248MB 4.87
#> 2 TP 133ms 277ms 3.61 68.7MB 1.81
创建于 2023-05-19 with reprex v2.0.2
上一个:变量前的美元符号
评论