143

我正在阅读data.table有关 SO 的文档,并从这里的一些对话中注意到,这rbindlist应该比rbind.

我想知道为什么rbindlist优于rbind以及在哪些场景中rbindlist真正擅长rbind

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

4

2 回答 2

162

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

rbindlist 的其他一些限制

由于已经修复了一个错误,它曾经很难处理:factors

rbindlist 两个 data.tables,其中一个具有因子,另一个具有列的字符类型错误 #2650

列名重复存在问题

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


rbind.data.frame 行名可能令人沮丧

rbindlist可以处理lists data.framesand data.tables,并且将返回一个没有行名的 data.table

do.call(rbind, list(...)) 你可以使用see进入混乱的行名

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


内存效率

就内存而言rbindlist是在 中实现的C,因此内存效率很高,它用于setattr通过引用设置属性

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

于 2013-03-28T03:16:17.583 回答
51

By v1.9.2rbindlist已经发展了很多,实现了许多功能,包括:

  • 绑定时选择最高SEXPTYPE的列 - 在v1.9.2关闭FR #2456Bug #4981中实现。
  • 正确处理factor列 - 首先在v1.8.10关闭Bug #2650中实现,并扩展到仔细绑定有序因子v1.9.2,关闭FR #4856Bug #5019

此外,在 中v1.9.2rbind.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主要是由于可以避免(通过移动到C)的副本(@mnel 也指出了这一点)而减慢了很多。我认为这不是唯一的原因。rbind.data.frame当每个 data.frame 有很多列并且有很多这样的 data.frames 要绑定时,检查/匹配列名的实现也可能会变慢(如下面的基准所示)。

但是,rbindlist缺少某些功能(例如检查因子级别或匹配名称)对它的速度比rbind.data.frame. 这是因为它们是用 C 语言精心实现的,针对速度和内存进行了优化。

这是一个基准,它突出了有效绑定,同时按列名匹配以及使用来自rbindlist. 数据集由 10000 个数据帧组成,每个数据帧大小为 10*500。use.namesv1.9.3

注意:此基准已更新,包括与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

在不检查名称的情况下绑定列只需要 1.3 秒,而检查列名和适当绑定只需要多 1.5 秒。与基础解决方案相比,这比dplyr's 版本快 14 倍和 18 倍。

于 2014-06-01T19:32:49.963 回答