为什么rbindlist比rbind“更好”?

我正在通过data.table文档,也注意到从这里的一些对话, rbindlist应该比rbind更好。

我想知道为什么rbindlistrbind更好,在哪种情况下rbindlist真的超越rbind

在内存使用方面有没有优势?

rbindlistdo.call(rbind, list(...))一个优化版本,在使用rbind.data.frame


它在哪里真的很好

一些显示rbindlist闪耀的问题

按行快速vector化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))) ## ab ## 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))) ## ab ## 1: 1 2 ## 2: 2 3 ## 3: 1 2 ## 4: 2 3 

rbindlist的一些其他限制

过去由于一个已经被修复的错误,过去一直努力处理factors

rbindlist两个data.tables,其中一个具有因子,另一个具有字符types ( Bug#2650 )

它有重复的列名称的问题

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


rbind.data.frame rownames会令人沮丧

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

你可以使用do.call(rbind, list(...))看看do.call(rbind, list(...))

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


内存效率

在内存方面, rbindlist是在C实现的,所以是内存高效的,它使用setattr通过引用来设置属性

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

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

  • 在绑定时select列的最高SEXPTYPE – 在closuresFR#2456和Bug#4981的 v1.9.2实现。
  • 正确处理factor列 – 首先在v1.8.10实现,closuresBug#2650,并在v1.9.2仔细扩展到绑定sorting因子,closuresFR#4856和Bug#5019 。

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

现在在v1.9.3 ,这些现有function还有更多的改进:

  • rbindlist获得一个参数use.names ,默认情况下为FALSE以实现向后兼容性。
  • rbindlist也获得了一个参数fill ,为了向后兼容,默认情况下也是FALSE
  • 这些function都是用C语言实现的,并且在添加function的同时,仔细书写,不会影响速度。
  • 由于rbindlist现在可以按名称匹配并填充缺失的列, rbindlist现在只需调用rbindlist 。 唯一的区别是对于rbind.data.table ,默认情况下use.names=TRUE ,以便向后兼容。

rbind.data.frame主要是由于拷贝(也指出了@mnel),可以避免的(通过移动到C)减慢了一些。 我认为这不是唯一的原因。 当每个data.frame有很多列时,检查/匹配rbind.data.frame列名的rbind.data.frame也会变慢,并且有很多这样的data.frames要绑定(如下面的基准所示)。

然而,这个rbindlist缺less某些特征(比如检查因子级别或匹配的名字)对rbind.data.frame权重很小(或没有)。 这是因为他们在C中谨慎实现,针对速度和内存进行了优化。

这是一个基准,它突出了在按列名进行匹配时的高效绑定,以及使用rbindlistuse.names特性。 数据集由10000个大小为10 * 500的dataframe组成。

 require(data.table) 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 # 3.419 0.278 3.718 system.time(ans2 <- rbindlist(ll, use.names=TRUE)) # user system elapsed # 5.311 0.471 5.914 system.time(ans3 <- do.call("rbind", ll)) # user system elapsed # 1097.895 1209.823 2438.452 identical(ans2, setDT(ans3)) # [1] TRUE 

在没有检查名字的情况下,对列进行绑定的时间仅为3.7秒,因为检查列名和绑定的时间只需要1.2秒。 与基本解决scheme相比,这个速度快了400倍。