for ループが遅いなんて,誰が言った?
https://blog.goo.ne.jp/r-de-r/e/49dd48ded7e3605578bbcde95731e412
https://blog.goo.ne.jp/r-de-r/e/4515f9867b017eed1a97cdb8173d8cdc
https://blog.goo.ne.jp/r-de-r/e/1a760a8bee2e3dfb57ffd9235294d4bd
https://blog.goo.ne.jp/r-de-r/e/e1ced6d1474bb9da6a1cf089c15da319
は,2012年の2月末の記事である。
10000×1000のデータフレームでは,forループ≒sapply > colMeans > apply
それを行列に変換したものでは,colMeans > forループ > apply
その後 2018年の3月末に,同じ趣旨で別の人が記事を書いた。
data.frameの高速演算には列ごとならlapply、行ごとならReduceを使おう
https://qiita.com/Atsushi776/items/176426e2195b18eb65b4
行列の場合もデータフレームの場合も,速度は,行列用の関数 > apply > for
データフレームの場合は,lapply が群を抜いて速い。sapply は lapply の結果を simplify するので,ちょっとだけ余分な時間が掛かる。
後者の場合(列ごとの演算の場合)に for は遅いとしているが,
Rのforは遅いと誰が言った? (data.frameの高速演算には列ごとならlapply、行ごとならReduceを使おうの補足)
https://qiita.com/Atsushi776/items/c31f2345b9c698354c81
行ごとの場合で,100行100,000列 の場合は reduce を使う場合より for ループの方が速い。
ということで,for も場合により速いということ。
その後3年も経って,R も進化したかどうか,どうなっているかをやってみた。
列ごとの集計についてのみ。
> sessionInfo()
R version 4.0.5 Patched (2021-03-31 r80136)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7
nr = 5000
nc = 100
set.seed(777)
x = matrix(rnorm(nr*nc), nr, nc)
d = as.data.frame(x)
library(microbenchmark)
cpu = microbenchmark(
lapply.df = lapply(d, sum),
sapply.df = sapply(d, sum),
for.loop.df = {
m1 = numeric(nc)
for (i in 1:nc) {
m1[i] = sum(d[,i])
}
m1
},
colSums.df = colSums(d),
apply.df = apply(d, 2, sum),
colSums.mat = colSums(x),
for.loop.mat = {
M1 = numeric(nc)
for (i in 1:nc) {
M1[i] = sum(x[,i])
}
M1
},
apply.mat = apply(x, 2, sum),
unit="ms"
)
par(mar=c(2.7, 5.8, 0.5, 0.5), mgp=c(1.0, 0.5, 0), tcl=-0.2)
plot(cpu,
col=rep(c("blue", "red"), c(5, 3)),
las=1, horizontal=TRUE, log = "x",
xlab="", ylab=""
)
mtext("cpu time in milliseconds", 1, 1.5)
実行結果
1. 小規模なデータセット
nr = 5000
nc = 100
Unit: milliseconds
expr min lq mean median uq max neval cld
lapply.df 1.047031 1.081145 1.1332266 1.1010855 1.1366530 1.768589 100 a
sapply.df 1.074273 1.108415 1.1708123 1.1437340 1.1752400 1.874276 100 a
for.loop.df 4.970888 5.328881 6.8997624 5.4409915 5.5839455 65.615935 100 c
colSums.df 3.362236 3.518504 3.7168504 3.5814160 3.6827735 9.154761 100 b
apply.df 10.747753 12.295381 12.7935086 12.4636335 12.7963960 16.218739 100 e
colSums.mat 0.420709 0.438020 0.4669311 0.4453005 0.4666935 0.740547 100 a
for.loop.mat 7.191930 7.561473 8.6612821 7.7115335 8.0154290 48.668296 100 d
apply.mat 7.518857 9.143223 9.4147229 9.3349020 9.5605895 12.260340 100 d
所見
データフレームの場合は,lapply, sapply が速く,apply は一番遅い。
colSums は以外と遅い。
for loop を使う場合は,colSums より遅い。
行列の場合は,colSums がダントツで速い。
for loop は遅いが,apply よりは速い。
2. 大規模なデータセット
nr = 50000
nc = 1000
Unit: milliseconds
expr min lq mean median uq max neval cld
lapply.df 109.28227 154.7461 238.27326 226.45196 294.1781 606.8323 100 a
sapply.df 109.75268 150.9724 223.23908 207.86459 276.3348 641.9150 100 a
for.loop.df 126.39996 186.2769 271.16484 245.08860 338.0778 1575.5968 100 a
colSums.df 306.50184 440.1110 661.95370 585.96930 825.0703 1725.2760 100 b
apply.df 1262.78601 1791.5473 2603.76553 2430.82437 3461.5491 7413.4465 100 e
colSums.mat 43.77521 64.4854 89.69947 85.70177 108.2785 215.9835 100 a
for.loop.mat 464.34522 718.2325 1059.18931 894.83492 1333.1409 3511.8967 100 c
apply.mat 960.38814 1426.7567 2049.73518 1778.87375 2604.3161 5885.0181 100 d
所見
データフレームの場合は,lapply, sapply, for loop が同じくらい速く,apply は一番遅い。
colSums は以外と遅い。
行列の場合は,colSums がダントツで速い。
for loop は遅いが,apply よりは速い。
結論
for はそんなに遅くない。