裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

行列あるいはデータフレームの行方向の計算

2021年07月27日 | ブログラミング

前報は,行列あるいはデータフレームの列方向の計算であった。
今回は,行方向の計算について検討する。

データ規模はやや大きい 50000×1000 を対象とした。

library(microbenchmark)

set.seed(777)
nr = 50000
nc = 1000
x = matrix(runif(nr * nc), nr, nc)
df = as.data.frame(x)

cpu = microbenchmark(
  Reduce.df = Reduce("+", df),
  rowSums.df = rowSums(df),
  apply.df = apply(df, 1, sum),
  for.loop.df = {
      m1 = numeric(nr)
      for (i in 1:nc) {
          m1 = m1 + df[, i]
      }
      m1
  },
  rowSums.mat = rowSums(x),
  apply.mat = apply(x, 1, sum),
  for.loop.mat = {
      M1 = numeric(nr)
      for (i in 1:nc) {
          M1 = M1 + x[, i]
      }
      M1
  },
  unit="ms"
)

par(mar=c(2.7, 6.0, 0.5, 0.5), mgp=c(1.0, 0.5, 0), tcl=-0.2)
plot(cpu,
  col=rep(c("blue", "red"), c(4, 3)),
  las=1, horizontal=TRUE, log = "x",
  xlab="", ylab=""
)
mtext("cpu time in nanoseconds", 1, 1.5)

Unit: milliseconds
         expr        min         lq       mean     median        uq       max neval    cld
    Reduce.df  186.39777  202.03146  243.48758  224.28278  280.4589  455.4433   100  b    
   rowSums.df  335.91715  359.85339  412.49965  409.48675  433.2498  686.4874   100   c   
     apply.df 1804.48749 1971.52982 2147.20970 2092.33050 2304.0389 3154.3865   100      f
  for.loop.df  204.47810  220.42069  274.09268  243.64281  308.5390  510.1865   100  b    
  rowSums.mat   88.33803   89.95369   99.45286   98.05859  108.2877  118.9649   100 a     
    apply.mat 1140.83549 1247.81805 1347.21777 1329.95254 1420.0599 1938.3013   100     e 
 for.loop.mat  387.88910  420.76785  498.32962  498.12949  550.1991  880.5908   100    d  

所見

1. 行列を対象とした rowSums がもっとも速い。for loop は遅いが, apply は相当遅い。
2. データフレームの場合は Reduce と for loop が同程度速く,rowSums は少し遅い。apply はとてつもなく遅い。

なお,行列を対象とする for loop の場合であるが,以下の 2 通りの書き方では M1 = M1 + x[, i] のほうが3倍ほど速いのに注目すべきである。(データフレームの場合も同じ)

理由は,計算中のメモリーアクセスが M1 = M1 + x[, i] は列方向でメモリー上で連続しているのに対し,M1[i] = sum(x[i, ]) は行方向のアクセスで,飛び飛びのメモリーを参照するためである。

system.time({ # 0.277sec.
   M1 = numeric(nr)
      for (i in 1:nc) {
        M1 = M1 + x[, i] # ベクトル演算
      }
})

system.time({ # 0.825sec.
   M1 = numeric(nr)
      for (i in 1:nr) {
        M1[i] = sum(x[i, ]) # 関数の結果を要素に代入
      }
})

system.time({ # 0.140sec.
      m1 = numeric(nr)
      for (i in 1:nc) {
          m1 = m1 + df[, i] # ベクトル演算
      }
})

system.time({ # 0.875sec.
   m1 = numeric(nr)
      for (i in 1:nr) {
        m1[i] = sum(x[i, ]) # 関数の結果を要素に代入
      }
})

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村