4

我正在尝试获取具有多个索引的非常大的记录集,计算由索引子集确定的组的聚合统计数据,然后将其插入表中的每一行。这里的问题是这些表非常大——每个表超过 1000 万行。

复制数据的代码如下。

基本思想是有一组索引,比如 ix1, ix2, ix3, ..., ixK。一般来说,我只选择其中的几个,比如 ix1 和 ix2。然后,我为名为val. 为简单起见,我将重点放在总和上。

我尝试了以下方法

  1. 通过稀疏矩阵:将值转换为坐标列表,即 (ix1, ix2, val),然后创建一个 sparseMatrix - 这很好地总结了所有内容,然后我只需从稀疏矩阵表示转换回坐标列表。速度:很好,但它做的超出了必要的范围,它没有推广到更高的维度(例如 ix1、ix2、ix3)或更通用的函数而不是总和。

  2. 使用lapplyand split:通过为所有 (ix1, ix2, ...) n 元组创建一个唯一的新索引,然后我可以使用拆分和应用。这里的坏处是唯一索引被转换split为一个因子,这种转换非常耗时。试试system({zz <- as.factor(1:10^7)})

  3. 我现在正在尝试data.table通过类似的命令sumDT <- DT[,sum(val),by = c("ix1","ix2")]。但是,除了通过类似的方法之外,我还没有看到如何sumDT与合并DTDT2 <- merge(DT, sumDT, by = c("ix1","ix2"))

这个 data.table 连接有没有比merge我描述的操作更快的方法?

【我也试过bigsplitbigtabulate包里,还有一些其他的方法。任何转换为​​因子的东西都差不多 - 据我所知,转换过程非常缓慢。]


生成数据的代码。自然,最好尝试更小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)
4

1 回答 1

4

好吧,您可能会发现,只要您key的 s 设置正确,进行合并并不是那么糟糕。

让我们再次设置问题:

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
于 2011-09-09T22:55:27.273 回答