我正在尝试获取具有多个索引的非常大的记录集,计算由索引子集确定的组的汇总统计数据,然后将其插入表中的每一行。这里的问题是这些是非常大的表——每个表超过 1000 万行。
再现数据的代码如下。
基本思想是有一组索引,比如 ix1, ix2, ix3, ..., ixK。通常,我只选择其中的几个,比如 ix1 和 ix2。然后,对于名为 val
的列,我计算所有具有匹配 ix1 和 ix2 值的行的聚合(在所有出现的组合上)。为简单起见,我将重点放在总和上。
我尝试了以下方法
lapply
和 split
的使用:通过创建一个对所有 (ix1, ix2, ...) n 元组唯一的新索引,我可以使用 split 和 apply。这里不好的是唯一索引被split
转换成一个因子,这个转换非常耗时。尝试 system({zz <- as.factor(1:10^7)})
。 data.table
,通过像 sumDT <- DT[,sum(val),by = c("ix1","ix2")]
这样的命令。但是,除了通过 sumDT
DT
与 DT2 <- merge(DT, sumDT, by = c("ix1","ix2"))
合并这个 data.table 连接是否有比我描述的
merge
操作更快的方法?[我也尝试过
bigsplit
包中的 bigtabulate
和其他一些方法。任何转换为因子的东西都差不多 - 据我所知,转换过程非常缓慢。]生成数据的代码。自然,最好尝试较小的
N
以查看某些内容是否有效,但并非所有方法都可以很好地扩展 N
>> 1000。N <- 10^7
set.seed(2011)
ix1 <- 1 + floor(rexp(N, 0.01))
ix2 <- 1 + floor(rexp(N, 0.01))
ix3 <- 1 + floor(rexp(N, 0.01))
val <- runif(N)
DF <- data.frame(ix1 = ix1, ix2 = ix2, ix3 = ix3, val = val)
DF <- DF[order(DF[,1],DF[,2],DF[,3]),]
DT <- as.data.table(DF)
最佳答案
好吧,您可能会发现,只要正确设置了 key
,进行合并就不会那么糟糕。
让我们再次设置问题:
N <- 10^6 ## not 10^7 because RAM is tight right now
set.seed(2011)
ix1 <- 1 + floor(rexp(N, 0.01))
ix2 <- 1 + floor(rexp(N, 0.01))
ix3 <- 1 + floor(rexp(N, 0.01))
val <- runif(N)
DT <- data.table(ix1=ix1, ix2=ix2, ix3=ix3, val=val, key=c("ix1", "ix2"))
现在您可以计算您的汇总统计数据
info <- DT[, list(summary=sum(val)), by=key(DT)]
并以“data.table 方式”合并列,或仅使用
merge
m1 <- DT[info] ## the data.table way
m2 <- merge(DT, info) ## if you're just used to merge
identical(m1, m2)
[1] TRUE
如果这些合并方式中的任何一种都太慢,您可以尝试一种以内存为代价构建
info
的棘手方法:info2 <- DT[, list(summary=rep(sum(val), length(val))), by=key(DT)]
m3 <- transform(DT, summary=info2$summary)
identical(m1, m3)
[1] TRUE
现在让我们看看时间:
#######################################################################
## Using data.table[ ... ] or merge
system.time(info <- DT[, list(summary=sum(val)), by=key(DT)])
user system elapsed
0.203 0.024 0.232
system.time(DT[info])
user system elapsed
0.217 0.078 0.296
system.time(merge(DT, info))
user system elapsed
0.981 0.202 1.185
########################################################################
## Now the two parts of the last version done separately:
system.time(info2 <- DT[, list(summary=rep(sum(val), length(val))), by=key(DT)])
user system elapsed
0.574 0.040 0.616
system.time(transform(DT, summary=info2$summary))
user system elapsed
0.173 0.093 0.267
或者,如果以下内容对您的口味来说似乎不太高深,您可以跳过中间的
info
表构建:system.time(m5 <- DT[ DT[, list(summary=sum(val)), by=key(DT)] ])
user system elapsed
0.424 0.101 0.525
identical(m5, m1)
# [1] TRUE
关于R:使用 data.table 进行制表和插入,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7367965/