【问题标题】:cbind vs rbind with data.tablecbind vs rbind 与 data.table
【发布时间】:2015-06-03 14:58:47
【问题描述】:

我注意到对于 data.tables,cbind 比 rbind 花费的时间要长得多。这是什么原因?

> dt <- as.data.table(mtcars)                             
> new.dt <- copy(dt)                                      
> timeit({for (i in 1:100) dt.new <- rbind(dt.new, dt)})  
   user  system elapsed                                   
  0.237   0.012   0.253                                   
> new.dt <- copy(dt)                                      
> timeit({for (i in 1:100) dt.new <- cbind(dt.new, dt)})  
   user  system elapsed                                   
 14.795   0.090  14.912    

在哪里

timeit <- function(expr)
{
    ptm <- proc.time()
    expr
    proc.time() - ptm
}

【问题讨论】:

  • 我不知道data.table的内部结构,但我想添加新记录(行)比用新变量(列)重组表更容易。
  • @zx8754 是的,我也同意我的怀疑,很好奇具体的瓶颈是什么。也许是内存分配,也许是更大的内存需求,希望熟悉包细节的人能阐明..
  • 除非我弄错了,否则在data.table 上调用rbind 将调度rbind.data.table,它调用data.table 函数rbindlist- 用C 实现,而且速度非常快。请参阅@Arun 的答案here。最重要的是,几乎可以肯定,按列修改和按行修改(关于对象如何存储在内存中)之间存在根本差异,因此这并不是真正的“苹果对苹果”的比较。这很可能是data.table 实现:= 以有效修改列的原因。
  • @andrew 因为基础rbindcbind 不是通用的,data.table 在加载时修改了这些基础函数。查看rbind.data.framecbind.data.frame 的代码以了解发生了什么。
  • @MatthiasDiener 该链接似乎已过时。当前版本没有cbind.data.table的帮助页面。

标签: r data.table


【解决方案1】:

最终,我认为这归结为alloc.col 由于循环从列中删除各种属性而变慢。我不完全确定为什么会这样,也许 Arun 或 Matt 可以解释。

如下所示,cbind 的基本操作比rbind 快得多:

cbind.dt.simple = function(...) {
  x = c(...)
  setattr(x, "class", c("data.table", "data.frame"))
  ans = .Call(data.table:::Calloccolwrapper, x, max(100L, ncol(x) + 64L), FALSE)
  .Call(data.table:::Csetnamed, ans, 0L)
}

library(microbenchmark)

microbenchmark(rbind(dt, dt), cbind(dt, dt), cbind.dt.simple(dt, dt))
#Unit: microseconds
#                    expr      min        lq      mean    median        uq       max neval
#           rbind(dt, dt)  785.318  996.5045 1665.1762 1234.4045 1520.3830 21327.426   100
#           cbind(dt, dt) 2350.275 3022.5685 3885.0014 3533.7595 4093.1975 21606.895   100
# cbind.dt.simple(dt, dt)   74.125  116.5290  168.5101  141.9055  180.3035  1903.526   100

【讨论】:

  • 我发现cbind2 &lt;- function(...) (setattr(do.call(c,list(...)),"class",c("data.table","data.frame"))) 更快。我并没有真正了解C* 函数发生了什么,所以也许我遗漏了一些东西。 all.equal(cbind.dt.simple(dt, dt),cbind2(dt,dt)) # TRUE
  • @Frank 对,但这会留下一些不完整的东西,例如尝试cbind2(dt, dt)[, newcol := 5]
  • 啊,我明白了。我的下一个想法setDT(setattr(do.call(c,list(...)),"class",c("data.frame"))) 要慢得多。哦,只是 setDT(do.call(c,list(...))) 有效,但速度相同(慢)。
  • 是的,我玩过as.data.tablesetDT(而不是cbind 中使用的data.table::data.table),虽然它们快一点,但它们会显着减慢alloc.col 电话
猜你喜欢
  • 2018-06-06
  • 2017-02-17
  • 1970-01-01
  • 2015-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-05
  • 2014-12-05
相关资源
最近更新 更多