【发布时间】:2019-11-27 14:00:18
【问题描述】:
我正在寻找一种技巧/技术来优化如下操作:
library(dplyr)
n <- 1e5
d <- tibble(x=sample(800, n, TRUE),
y=sample(2000, n, TRUE) %>% as.Date(origin='1970-01-01'),
z=sample(5, n, TRUE),
val=runif(n))
system.time({
y_dp <- d %>%
group_by(x, y) %>%
summarize(w = val[which.max(z)])
})
# user system elapsed
# 1014.918 9.760 1027.845
这是非常普通的 - 按 2 个变量分组,根据另外 2 个变量计算每个组的标量汇总。
data.table 能够以大约 10000 倍的效率处理这种大小的数据:
library(data.table)
system.time({
y_dt <- data.table(d, key=c("x", "y")) %>%
`[`(, .(w=val[which.max(z)]), by=list(x, y)) %>%
as_tibble()
})
# user system elapsed
# 0.109 0.003 0.112
all.equal(y_dt, y_dp)
# TRUE
它大概可以通过基于键的索引(在这种情况下是排序),然后线性迭代结构来实现; dplyr 可能必须为每个组合 (x, y) 在结构中构造单独的索引。
按(x, y) 预排序也无助于dplyr 的情况,因为它似乎没有“记住”数据是按其分组依据排序的:
system.time({
y3 <- d %>%
arrange(x, y) %>%
group_by(x, y) %>%
summarize(w = val[which.max(z)])
})
# user system elapsed
# 1048.983 13.616 1070.929
确实,由于 tibble 的类和属性在排序后不会改变,似乎没有办法在之后利用排序。
想法?
编辑:我错误地写了 n
> sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-apple-darwin17.7.0 (64-bit)
Running under: macOS High Sierra 10.13.6
Matrix products: default
BLAS/LAPACK: /usr/local/Cellar/openblas/0.3.6_1/lib/libopenblasp-r0.3.6.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] dplyr_0.8.2
loaded via a namespace (and not attached):
[1] tidyselect_0.2.5 compiler_3.6.0 magrittr_1.5 assertthat_0.2.1
[5] R6_2.4.0 pillar_1.4.2 glue_1.3.1 tibble_2.1.3
[9] crayon_1.3.4 Rcpp_1.0.1 pkgconfig_2.0.2 rlang_0.4.0
[13] purrr_0.3.2
【问题讨论】:
-
对我来说,只需
0.209 0.022 0.231将summarize更改为summarise。此外,您是否也加载了plyr库。我使用set.seed(24)创建了一个可重现的示例 -
是的,我在第一个示例中得到了
1.22已过 -
啊哈-我在私人图书馆中安装了
dplyr_0.8.3,现在我也得到了0.447 0.050 0.500。这是相当戏剧性的! 0.8.3 的发行说明说“在版本 0.8.2 (#4458) 中引入了固定的性能回归”。 -
github.com/tidyverse/dplyr/issues/4458 - 除非其他人愿意,否则我可以将其写成答案。
-
仅供参考,计划对 data.table 中的分组 var[which.max(x)] 进行进一步优化:github.com/Rdatatable/data.table/issues/523 我想它适用于有序因子,不确定。
标签: r optimization dplyr data.table