【发布时间】:2015-10-29 09:31:47
【问题描述】:
我曾经用 dplyr 来实现我的数据整理,但有些计算“慢”。特别是按组划分的子集,我读到当有很多组时 dplyr 很慢,并且基于this benchmark data.table 可能会更快,所以我开始学习 data.table。
这里是如何用 250k 行和大约 230k 组重现接近我的真实数据的东西。我想按 id1、id2 分组,并为每个组使用 max(datetime) 对行进行子集化。
数据
# random datetime generation function by Dirk Eddelbuettel
# https://stackoverflow.com/questions/14720983/efficiently-generate-a-random-sample-of-times-and-dates-between-two-dates
rand.datetime <- function(N, st = "2012/01/01", et = "2015/08/05") {
st <- as.POSIXct(as.Date(st))
et <- as.POSIXct(as.Date(et))
dt <- as.numeric(difftime(et,st,unit="sec"))
ev <- sort(runif(N, 0, dt))
rt <- st + ev
}
set.seed(42)
# Creating 230000 ids couples
ids <- data.frame(id1 = stringi::stri_rand_strings(23e4, 9, pattern = "[0-9]"),
id2 = stringi::stri_rand_strings(23e4, 9, pattern = "[0-9]"))
# Repeating randomly the ids[1:2000, ] to create groups
ids <- rbind(ids, ids[sample(1:2000, 20000, replace = TRUE), ])
# Adding random datetime variable and dummy variables to reproduce real datas
datas <- transform(ids,
datetime = rand.datetime(25e4),
var1 = sample(LETTERS[1:6], 25e4, rep = TRUE),
var2 = sample(c(1:10, NA), 25e4, rep = TRUE),
var3 = sample(c(1:10, NA), 25e4, rep = TRUE),
var4 = rand.datetime(25e4),
var5 = rand.datetime(25e4))
datas.tbl <- tbl_df(datas)
datas.dt <- data.table(datas, key = c("id1", "id2"))
我找不到使用 data.table 按组划分子集的直接方法,所以我问了这个问题:Filter rows by groups with data.table
我们建议我使用 .SD:
datas.dt[, .SD[datetime == max(datetime)], by = c("id1", "id2")]
但是我有两个问题,它适用于日期但不适用于 POSIXct ("Error in UseMethod("as.data.table") : 'as.data.table' 没有适用于类“c('POSIXct', 'POSIXt')"") 的对象的适用方法,这非常慢。例如使用日期:
> system.time({
+ datas.dt[, .SD[as.Date(datetime) == max(as.Date(datetime))], by = c("id1", "id2")]
+ })
utilisateur système écoulé
207.03 0.00 207.48
所以我找到了其他更快的方法来实现这一点(并保持日期时间) data.table :
功能
f.dplyr <- function(x) x %>% group_by(id1, id2) %>% filter(datetime == max(datetime))
f.dt.i <- function(x) x[x[, .I[datetime == max(datetime)], by = c("id1", "id2")]$V1]
f.dt <- function(x) x[x[, datetime == max(datetime), by = c("id1", "id2")]$V1]
但后来我认为 data.table 会快得多,与 dplyr 的时间差异并不显着。
微基准测试
mbm <- microbenchmark(
dplyr = res1 <- f.dplyr(datas.tbl),
data.table.I = res2 <- f.dt.i(datas.dt),
data.table = res3 <- f.dt(datas.dt),
times = 50L)
Unit: seconds
expr min lq mean median uq max neval
dplyr 31.84249 32.24055 32.59046 32.61311 32.88703 33.54226 50
data.table.I 30.02831 30.94621 31.19660 31.17820 31.42888 32.16521 50
data.table 30.28923 30.84212 31.09749 31.04851 31.40432 31.96351 50
我是否遗漏/误用了 data.table 的某些内容?您有加快计算速度的想法吗?
任何帮助将不胜感激!谢谢
编辑:有关用于微基准测试的系统和软件包版本的一些精度。 (计算机不是战争机器,12Go i5)
系统
sessionInfo()
R version 3.1.3 (2015-03-09)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1
locale:
[1] LC_COLLATE=French_France.1252 LC_CTYPE=French_France.1252
[3] LC_MONETARY=French_France.1252 LC_NUMERIC=C
[5] LC_TIME=French_France.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] readr_0.1.0 ggplot2_1.0.1 microbenchmark_1.4-2
[4] data.table_1.9.4 dplyr_0.4.1 plyr_1.8.2
loaded via a namespace (and not attached):
[1] assertthat_0.1 chron_2.3-45 colorspace_1.2-6 DBI_0.3.1
[5] digest_0.6.8 grid_3.1.3 gtable_0.1.2 lazyeval_0.1.10
[9] magrittr_1.5 MASS_7.3-39 munsell_0.4.2 parallel_3.1.3
[13] proto_0.3-10 Rcpp_0.11.5 reshape2_1.4.1 scales_0.2.4
[17] stringi_0.4-1 stringr_0.6.2 tools_3.1.3
> packageVersion("data.table")
[1] ‘1.9.4’
> packageVersion("dplyr")
[1] ‘0.4.1’
【问题讨论】:
-
您想获取等于 max 的所有值,还是只获取第一个值,例如
which.max返回?datas.dt[, .SD[as.Date(datetime) == max(as.Date(datetime))], by = c("id1", "id2")]也是一种不好的做法。您应该在子集之前将date转换为IDate类。 -
只是为了好玩,您可以将
x %>% group_by(id1, id2) %>% slice(which(datetime == max(datetime)))添加到您的比较中吗? -
另外,
datas.dt[, datetime := as.IDate(datetime)] ; system.time(datas.dt[datas.dt[, .I[datetime == max(datetime)], by = c("id1", "id2")]$V1])只运行 5 秒,而使用.SD时为 200 秒,所以我很难相信你的基准测试。 -
@DavidArenburg,恭喜,虽然这不是我想要的比较。无论如何,我只是出于好奇而问。
-
@docendodiscimus 我不是在吹牛什么的,所以不确定你在祝贺我什么。 OP 正在寻找
data.table解决方案,因为他认为它会比dplyr更快 - 这就是为什么我将您的建议与data.table进行比较,以防他的假设是错误的。
标签: r performance data.table dplyr