R:data.tableを使用した表と挿入
-
28-10-2019 - |
質問
複数のインデックスを備えた非常に大きなレコードセットを取得し、インデックスのサブセットによって決定されたグループの集計統計を計算し、テーブル内のすべての行に挿入しようとしています。ここでの問題は、これらが非常に大きなテーブルであることです - それぞれ10mを超える列。
データを再現するためのコードは以下にあります。
基本的な考え方は、IX1、IX2、IX3、...、IXKなどのインデックスのセットがあるということです。一般的に、私はそれらのうちの数だけを選択しています、たとえばix1とix2。次に、IX1とIX2の値を一致させるすべての行の集約を計算します(表示されるすべての組み合わせ) val
. 。シンプルに保つために、合計に集中します。
次の方法を試しました
スパースマトリックスを介して:値を座標リストに変換します。IE(ix1、ix2、val)を作成し、Sparsematrixを作成します。速度:良いことですが、それは必要以上のことをしており、合計よりも高次元(IX1、IX2、IX3)またはより一般的な関数に一般化することはありません。
の使用
lapply
とsplit
: :すべて(ix1、ix2、...)n-tuppleに一意の新しいインデックスを作成することにより、splitを使用して適用できます。ここで悪いことは、一意のインデックスがによって変換されることですsplit
要因になり、この変換はそれほど時間がかかります。試すsystem({zz <- as.factor(1:10^7)})
.私は今やっています
data.table
, 、次のようなコマンドを介してsumDT <- DT[,sum(val),by = c("ix1","ix2")]
. 。しかし、私はまだマージできる方法がわかりませんsumDT
とDT
, 、ようなものを介してDT2 <- merge(DT, sumDT, by = c("ix1","ix2"))
このdata.table結合のために、このdata.table結合にはより速い方法がありますか merge
私が説明した操作?
私も試しました bigsplit
から bigtabulate
パッケージ、およびその他の方法。要因に変換するものはほとんどありません - 私が知る限り、その変換プロセスは非常に遅いです。
データを生成するコード。当然のことながら、小さいものを試してみる方が良いです N
何かが機能することを確認するために、すべての方法が非常にうまくスケーリングするわけではありません N
>> 1000.
N <- 10^7
set.seed(2011)
ix1 <- 1 + floor(rexp(N, 0.01))
ix2 <- 1 + floor(rexp(N, 0.01))
ix3 <- 1 + floor(rexp(N, 0.01))
val <- runif(N)
DF <- data.frame(ix1 = ix1, ix2 = ix2, ix3 = ix3, val = val)
DF <- DF[order(DF[,1],DF[,2],DF[,3]),]
DT <- as.data.table(DF)
解決
まあ、あなたはあなたのマージをすることはあなたほど長くはないことに気付くかもしれません key
Sが適切に設定されています。
もう一度問題をセットアップしましょう:
N <- 10^6 ## not 10^7 because RAM is tight right now
set.seed(2011)
ix1 <- 1 + floor(rexp(N, 0.01))
ix2 <- 1 + floor(rexp(N, 0.01))
ix3 <- 1 + floor(rexp(N, 0.01))
val <- runif(N)
DT <- data.table(ix1=ix1, ix2=ix2, ix3=ix3, val=val, key=c("ix1", "ix2"))
これで、要約統計を計算できます
info <- DT[, list(summary=sum(val)), by=key(DT)]
列を「data.table way」、またはとマージするか、 merge
m1 <- DT[info] ## the data.table way
m2 <- merge(DT, info) ## if you're just used to merge
identical(m1, m2)
[1] TRUE
これらのマージの方法のいずれかが遅すぎる場合、あなたはトリッキーな方法を試すことができます構築することができます info
記憶を犠牲にして:
info2 <- DT[, list(summary=rep(sum(val), length(val))), by=key(DT)]
m3 <- transform(DT, summary=info2$summary)
identical(m1, m3)
[1] TRUE
さて、タイミングを見てみましょう:
#######################################################################
## Using data.table[ ... ] or merge
system.time(info <- DT[, list(summary=sum(val)), by=key(DT)])
user system elapsed
0.203 0.024 0.232
system.time(DT[info])
user system elapsed
0.217 0.078 0.296
system.time(merge(DT, info))
user system elapsed
0.981 0.202 1.185
########################################################################
## Now the two parts of the last version done separately:
system.time(info2 <- DT[, list(summary=rep(sum(val), length(val))), by=key(DT)])
user system elapsed
0.574 0.040 0.616
system.time(transform(DT, summary=info2$summary))
user system elapsed
0.173 0.093 0.267
または、中間体をスキップすることもできます info
テーブルビルディング以下があなたの好みに対してあまりにも不可解でないと思われる場合:
system.time(m5 <- DT[ DT[, list(summary=sum(val)), by=key(DT)] ])
user system elapsed
0.424 0.101 0.525
identical(m5, m1)
# [1] TRUE