わたしは,R には何の義理もないので,好き勝手言います...
> ベンチマークで利用しているデータは10,000行1,000列のデータフレームですが、for 文が有利なように恣意的に設定されている気がします。
> colMeans のボトルネックは as.matrix であり、for 文のボトルネックは R 側で行われる繰り返し処理そのものです。
> よって、同じデータサイズであっても1,000行10,000列のデータフレームであれば for 文が極端に遅くなります。
恣意的に選んだつもりはないし,普通,データ行列というのは,行数は列数より多いもの(統計データはそんな風です)。
最初に,システムの記述。
=====================================================================
Mac OS X
バージョン 10.7.3
プロセッサ 2.4 GHz Intel Core 2 Duo
メモリ 4 GB 667 MHz DDR2 SDRAM 要するに,非力なノートパソコンです(泣)
=====================================================================
> sessionInfo()
R version 2.14.1 RC (2011-12-21 r57955)
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)
locale:
[1] ja_JP.UTF-8/ja_JP.UTF-8/ja_JP.UTF-8/C/ja_JP.UTF-8/ja_JP.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods
[7] base
=====================================================================
> よって、同じデータサイズであっても1,000行10,000列のデータフレームであれば for 文が極端に遅くなります。
確かに,1,000 行 10,000 列のデータフレームだと,次のようになりますね。
> gc(); gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 340340 18.2 407500 21.8 350000 18.7
Vcells 455195 3.5 905753 7.0 879046 6.8
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 340297 18.2 467875 25 350000 18.7
Vcells 455104 3.5 905753 7 879046 6.8
> set.seed(123454321)
> nr <- 1000
> nc <- 10000
> x <- matrix(rnorm(nr*nc), nr, nc)
> d <- as.data.frame(x)
> gc(); gc(); system.time({
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 360991 19.3 667722 35.7 420021 22.5
Vcells 20486276 156.3 30561555 233.2 30560771 233.2
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 360998 19.3 667722 35.7 420021 22.5
Vcells 20486303 156.3 30561555 233.2 30560771 233.2
+ m1 <- numeric(nc)
+ for (i in 1:nc) {
+ m1[i] <- mean(d[, i])
+ }
+ })
ユーザ システム 経過
1.120 0.123 1.253
> gc(); gc(); system.time({
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 362956 19.4 667722 35.7 667722 35.7
Vcells 20502981 156.5 30561555 233.2 30560771 233.2
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 362954 19.4 667722 35.7 667722 35.7
Vcells 20502987 156.5 30561555 233.2 30560771 233.2
+ m3 <- colMeans(d)
+ })
ユーザ システム 経過
0.612 0.095 0.700 # ★★ 仰るとおり,colMeans が倍くらい速い(パチパチ)
# ★★ しかし「極端に遅くなる」わけではない
=====================================================================
でも,これは普通の統計データの状況とはちょっと違うので,せめて,行数は列数と同じにしましょうよ。
ということで,10,000 行 10,000 列のデータフレームで,やってみましょう。
> gc(); gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 363975 19.5 667722 35.7 667722 35.7
Vcells 20524784 156.6 45054625 343.8 40571168 309.6
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 363973 19.5 667722 35.7 667722 35.7
Vcells 20524790 156.6 45054625 343.8 40571168 309.6
> set.seed(123454321)
> nr <- 10000 ★★ 行数を 10 倍にしました(^_^;)
> nc <- 10000
> x <- matrix(rnorm(nr*nc), nr, nc)
> d <- as.data.frame(x)
> gc(); gc(); system.time({
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 363966 19.5 667722 35.7 667722 35.7
Vcells 200524763 1529.9 312110644 2381.3 312104760 2381.2
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 363973 19.5 667722 35.7 667722 35.7
Vcells 200524790 1529.9 312110644 2381.3 312104760 2381.2
+ m1 <- numeric(nc)
+ for (i in 1:nc) {
+ m1[i] <- mean(d[, i])
+ }
+ })
ユーザ システム 経過
1.402 0.040 1.451 # ★★ for は,原理的に言ってそう増えるわけではない。
> gc(); gc(); system.time({
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 363975 19.5 667722 35.7 667722 35.7
Vcells 200527435 1530.0 312110644 2381.3 312104760 2381.2
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 363973 19.5 667722 35.7 667722 35.7
Vcells 200527441 1530.0 312110644 2381.3 312104760 2381.2
+ m3 <- colMeans(d)
+ })
ユーザ システム 経過
3.975 15.272 191.106 # ★★ オオッ。これはなんだ。時間は倍以上掛かるし,システムは 15 秒だって?
# これ以上行数を増やして実験するなんて,もうイヤだ(^_^)
> データフレームを行列にしてから計算するのだから遅くてもしょうがないじゃあないか(怒)
それはそうだけど,前にも言ったけど,データフレームについて計算しようというのなら行列に変換するのに必要な時間も勘定されてもしょうがないでしょう。それがいやなら,前にも言ったけど,前もって行列にしておくこと。行列のときに colMeans は速いっていうのは否定していないのだから。怒りの矛先を私に向けられても,戸惑うばかり...