data.table
のドキュメントを調べていますが、SOでの会話のいくつかから、rbindlist
がrbind
よりも優れていることに気づきました。
rbindlist
がrbind
よりも優れている理由と、どのシナリオでrbindlist
がrbind
より優れているのかを知りたいのですが。
メモリ使用率の面で利点はありますか?
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は列の順序が異なる可能性があり、名前で一致するという事実を考慮します)、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
を使用してfactors
に対処するのに苦労しましたが、バグが修正されました。
rbindlistには2つのdata.tablesがあり、一方にはファクタがあり、もう一方にはカラムの文字タイプがあります ( Bug#265 )
列名の重複に問題がある
警告メッセージ:in rbindlist(allargs):強制によって導入されたNA:data.tableにバグがある可能性がありますか? ( Bug#2384 )
rbindlist
はlists
data.frames
およびdata.tables
を処理でき、行名のないdata.tableを返します
do.call(rbind, list(...))
を使用して行名の混雑を取得できます
do.call内でrbindを使用するときに行の名前変更を回避する方法
メモリに関してはrbindlist
はC
に実装されているため、メモリ効率が高いため、setattr
を使用して参照によって属性を設定します。
rbind.data.frame
はR
で実装され、多くの割り当てを行い、attr<-
(およびclass<-
とrownames<-
を使用します。これらはすべて(内部的に)のコピーを作成します作成されたdata.frame。
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
は、主に(Cに移行することで)回避できるコピー(@mnelが同様に指摘している)により、かなり遅くなります。それだけが理由ではないと思います。また、rbind.data.frame
の列名をチェック/照合するための実装は、data.frameごとに多くの列があり、バインドするそのようなdata.framesが多い場合に遅くなる可能性があります(以下のベンチマークを参照)。
ただし、rbindlist
は特定の機能(因子レベルのチェックや名前の一致など)を欠いているため、rbind.data.frame
よりも高速であることに非常に小さい(またはまったくない)重みがあります。これは、速度とメモリが最適化されたCで慎重に実装されたためです。
以下は、rbindlist
のuse.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倍高速です。