为什么rbindlist比rbind“更好”?


135

我正在浏览文档,data.table并从这里的一些对话中也注意到了rbindlist可能比更好rbind

我想知道为什么是rbindlist优于rbind以及其中场景rbindlist的确有过人之处过rbind

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

Answers:


155

rbindlist是的优化版本do.call(rbind, list(...)),使用时速度较慢rbind.data.frame


真正的优势在哪里

一些问题可以显示出哪里rbindlist发光

快速向量化数据列表按行合并

使用do.call和ldply将一长串data.frames(〜1百万)转换为单个data.frames时出现问题

这些基准测试显示了测试速度。


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.framesdata.tables,并且将返回不带行名的data.table

您可以使用do.call(rbind, list(...)) see 混淆行名

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


记忆效率

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

rbind.data.frame在中实现R,它会进行大量分配,并且会使用attr<-class<-并且rownames<-所有这些都将(内部)创建所创建data.frame的副本。


1
仅供参考attr<-class<-(我认为)rownames<-全部修改到位。
哈德利

5
@hadley你确定吗?尝试DF = data.frame(a=1:3); .Internal(inspect(DF)); tracemem(DF); attr(DF,"test") <- "hello"; .Internal(inspect(DF))
马特·道尔

4
rbind.data.frame具有特殊的“劫持”逻辑-当其第一个参数为时data.table,它会调用.rbind.data.table,这会进行一点检查,然后在rbindlist内部调用。因此,如果您已经有data.table要绑定的对象,则rbind和之间的性能差异可能很小rbindlist
肯·威廉姆斯

6
mnel,此帖子可能需要编辑,因为rbindlist它现在可以按名称匹配(use.names=TRUE),还可以填充缺少的列(fill=TRUE)。我已经更新了这个这个这个帖子。您介意编辑此文件吗?如果可以,可以吗?两种方法对我来说都很好。
阿伦(Arun)2014年

1
dplyr::rbind_list也非常相似
哈德利

48

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

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

但是,rbindlist缺少某些功能(例如检查因子水平或匹配名称)对它的影响很小(或没有),因此比快rbind.data.frame。这是因为它们是用C精心实现的,并且针对速度和内存进行了优化。

这里有一个基准,突出了高效的结合,同时通过列名的匹配,以及使用rbindlistuse.names功能v1.9.3。数据集由10000个数据帧组成,每个数据帧的大小为10 * 500。

注意:这个测试已经更新到包括比较dplyrbind_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秒。与基本解决方案相比,它的速度快了14倍,比dplyr的版本快18倍。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.