为什么rbindlist比rbind"更好"?

Chi*_*til 129 r rbind data.table rbindlist

我正在阅读文档,data.table并且还注意到了一些关于SO的对话,这些对话rbindlist应该比我更好rbind.

我想知道为什么rbindlist比真正优秀的rbind场景更好?rbindlistrbind

在内存利用方面有什么优势吗?

mne*_*nel 150

rbindlist是一个优化版本do.call(rbind, list(...)),以使用时速度慢而闻名rbind.data.frame


它真正擅长的地方

一些问题表明rbindlist闪耀在哪里

按行列出data.frames的快速矢量化合并

使用do.call和ldply将很长的data.frames(~100万)列表转换为单个data.frame

这些基准测试表明它的速度有多快.


rbind.data.frame很慢,有一个原因

rbind.data.frame进行大量检查,并按名称进行匹配.(即rbind.data.frame将说明列可能处于不同的顺序,并按名称匹配),rbindlist不进行此类检查,并将按位置加入

例如

do.call(rbind, list(data.frame(a = 1:2, b = 2:3), data.frame(b = 1:2, a = 2:3)))
##    a b
## 1  1 2
## 2  2 3
## 3  2 1
## 4  3 2

rbindlist(list(data.frame(a = 1:5, b = 2:6), data.frame(b = 1:5, a = 2:6)))
##     a b
##  1: 1 2
##  2: 2 3
##  3: 1 2
##  4: 2 3
Run Code Online (Sandbox Code Playgroud)

rbindlist的一些其他限制

由于已修复的错误,它过去很难处理factors:

rbindlist两个data.tables,其中一个有因子,另一个有列的字符类型(Bug#2650)

它有重复的列名称的问题

请参阅 警告消息:在rbindlist(allargs)中:强制引入的NA:data.table中可能出现的错误?(Bug#2384)


rbind.data.frame rownames可能令人沮丧

rbindlist可以处理lists data.framesdata.tables,并将返回没有rownames的data.table

你可以使用do.call(rbind, list(...)) see 来获得rownames的混乱

在do.call中使用rbind时如何避免重命名行?


记忆效率

在内存rbindlist方面实现C,内存高效,它用于setattr通过引用设置属性

rbind.data.frame实现R,它做了很多分配,并使用attr<-(class<-rownames<-所有这些将(内部)创建创建的data.frame的副本.

  • mnel,这篇文章可能需要编辑,现在`rbindlist`能够通过名称匹配(`use.names = TRUE`)并填充缺少的列(`fill = TRUE`).我已更新[this](http://stackoverflow.com/a/23923024/559784),[this](http://stackoverflow.com/a/23933711/559784)和[this](http:// stackoverflow.com/a/23933873/559784)发布.你介意编辑这个吗?或者如果我这样做可以吗?无论哪种方式都很好. (6认同)
  • @hadley你确定吗?试试`DF = data.frame(a = 1:3); .内部(检查(DF)); tracemem(DF); attr(DF,"test")< - "你好"; .内部(检查(DF))`. (5认同)
  • `rbind.data.frame`有一个特殊的"劫持"逻辑 - 当它的第一个参数是`data.table`时,它调用`.rbind.data.table`,它会做一点检查,然后在内部调用`rbindlist` .因此,如果你已经有`data.table`对象绑定,那么`rbind`和`rbindlist`之间的性能差异可能很小. (4认同)
  • 仅供参考,`attr&lt;-`、`class&lt;-` 和(我认为)`rownames&lt;-` 都进行了适当的修改。 (2认同)

Aru*_*run 43

通过v1.9.2,rbindlist进化了很多,实现了许多功能,包括:

另外,在中v1.9.2,rbind.data.table也获得了一个fill参数,允许通过填充缺失列进行绑定,在R中实现.

现在v1.9.3,对这些现有功能进行了更多改进:

  • rbindlist获得一个参数use.names,默认情况下是FALSE为了向后兼容.
  • rbindlist也获得了一个参数fill,默认情况下也是FALSE为了向后兼容.
  • 这些功能都是用C语言实现的,并且在添加功能时要小心写,以免速度受损.
  • 因为rbindlist现在可以通过名称匹配并填充缺少的列,现在rbind.data.table只需调用rbindlist.唯一的区别是use.names=TRUE默认情况下rbind.data.table,为了向后兼容性.

rbind.data.frame由于副本(@mnel指出的)可以避免(通过转移到C),因此减慢了很多.我认为这不是唯一的原因.rbind.data.frame当每个data.frame有许多列并且有许多这样的data.frames要绑定时,检查/匹配列名的实现也会变得更慢(如下面的基准测试所示).

然而,rbindlist缺少(编辑)某些特征(如检查因子水平或匹配名称)对于它的速度非常小(或没有)rbind.data.frame.这是因为它们在C中经过精心实施,针对速度和内存进行了优化.

这是一个基准,它突出了有效的绑定,同时匹配列名以及使用rbindlistuse.names功能v1.9.3.数据集由10000个data.frames组成,每个都是10*500.

注意:这个基准已经更新,包括与dplyr's 的比较bind_rows

library(data.table) # 1.11.5, 2018-06-02 00:09:06 UTC
library(dplyr) # 0.7.5.9000, 2018-06-12 01:41:40 UTC
set.seed(1L)
names = paste0("V", 1:500)
cols = 500L
foo <- function() {
    data = as.data.frame(setDT(lapply(1:cols, function(x) sample(10))))
    setnames(data, sample(names))
}
n = 10e3L
ll = vector("list", n)
for (i in 1:n) {
    .Call("Csetlistelt", ll, i, foo())
}

system.time(ans1 <- rbindlist(ll))
#  user  system elapsed 
# 1.226   0.070   1.296 

system.time(ans2 <- rbindlist(ll, use.names=TRUE))
#  user  system elapsed 
# 2.635   0.129   2.772 

system.time(ans3 <- do.call("rbind", ll))
#   user  system elapsed 
# 36.932   1.628  38.594 

system.time(ans4 <- bind_rows(ll))
#   user  system elapsed 
# 48.754   0.384  49.224 

identical(ans2, setDT(ans3)) 
# [1] TRUE
identical(ans2, setDT(ans4))
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)

在不检查名称的情况下绑定列只需1.3,因为检查列名和绑定只需要1.5秒.与基础解决方案相比,这比基本解决方案快14倍,比dplyr版本快18倍.


归档时间:

查看次数:

77844 次

最近记录:

6 年,10 月 前