rbindlistがrbindより「優れている」のはなぜですか?


135

私はのドキュメントをdata.table調べているところ、SO rbindlistよりも優れていると思われるSOに関する会話の一部に気づきましたrbind

なぜがrbindlist優れているのかrbind、どのシナリオでrbindlist本当に優れているのrbindかを知りたいのですが。

メモリ使用率の点で利点はありますか?

回答:


155

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 2つのdata.tablesで、1つは因子を持ち、もう1つは列の文字タイプを持ちますバグ#2650

列名の重複に関する問題があります

警告メッセージを参照してください :rbindlist(allargs)内:強制によって導入されたNA:data.tableのバグの可能性?バグ#2384


rbind.data.frame行名はイライラすることがあります

rbindlistlists data.framesおよびを処理できdata.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<-すべてが適切に変更されます。
ハドリー2013年

5
@hadleyよろしいですか?お試しくださいDF = data.frame(a=1:3); .Internal(inspect(DF)); tracemem(DF); attr(DF,"test") <- "hello"; .Internal(inspect(DF))
Matt Dowle 2013年

4
rbind.data.frame特別な「ハイジャック」ロジックがあります。最初の引数がの場合、代わりにをdata.table呼び出し.rbind.data.tableます。これは、少しチェックを行ってからrbindlist内部的に呼び出します。したがって、data.tableバインドするオブジェクトがすでにある場合、rbindとのパフォーマンスの違いはほとんどありませんrbindlist
ケンウィリアムズ

6
mnel、この投稿はおそらく編集が必要rbindlistですが、名前で一致させることができ(use.names=TRUE)、欠落している列を埋めることもできます(fill=TRUE)。これこれこの投稿を更新しました。これを編集してもよろしいですか、それとも私がそれでいいですか?どちらの方法も私には問題ありません。
アルン

1
dplyr::rbind_listこれもかなり似ています
ハドリー2014

48

によってv1.9.2rbindlistかなり進化し、次のような多くの機能を実装しました。

  • SEXPTYPEバインド中に最上位の列を選択する-FR#2456およびバグ#4981をv1.9.2閉じるときに実装されました。
  • factor列を適切に処理する-最初にバグ#2650v1.8.10閉じるときに実装され、順序付けされた要素を慎重にバインドするように拡張v1.9.2、同様に、FR#4856Bug#5019を閉じます。

さらに、ではv1.9.2、Rに実装されている、欠落している列を埋めることでバインドできるようにする引数rbind.data.tableも取得しfillました。

現在では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で慎重に実装したためです。

以下は、列名で照合しながら、rbindlistuse.names機能を使用して効率的なバインディングを強調するベンチマークですv1.9.3。データセットは、サイズが10 * 500の10000個のdata.framesで構成されています。

NB:このベンチマークではとの比較含むように更新されたdplyrのを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のバージョンより14倍高速で、18倍高速です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.