並列 apply 系列

R による並列プログラミング

Nabeel Imam

Data Science

妊娠中の体重増加

print(ls_weights)
$AK
  [1] 30 33 50 30 26 99 52 28 24 40 30 99 40
 [14] 20 20 39 25 30 20 33 99 54 30 99 30 38
...
$AL
   [1] 60 32 30 21 30 27 28 32 18 28 30 24 59
  [14] 30 19 30 15 17 18 53 26  5 25 41 50 20
...
$AR
  [1] 31 40 30 99 27 36 30 35 25 19 10 25 40
 [14] 33 37 12 34 99 43 26 28 99 37 30 26  6
...
1 CDC 出生データ(Natality)
R による並列プログラミング

mclapply で FORK 実行

boot_mean <- function (weights) {

  est <- rep(0, 1e4)

  for (i in 1:1e4) {
    b <- sample(weights, replace = T)
    est[i] <- mean(b)
  }

  return(est)
}

ls_boot <- mclapply(ls_weights, boot_mean,

mc.cores = 4)
  • 20〜40% 高速化
  • バックグラウンドで FORK クラスター生成
    • 事前のクラスター設定は不要
    • Windows では不可
R による並列プログラミング

parLapplyLB で負荷分散

2人の社員は仕事量が多く、1人は手持ち無沙汰。

lapply(ls_weights, length)
   AK    AL    AR    AZ    CA    CO    CT   DC    DE    FL    GA    HI    IA  ...
  260  1503   816  1649 14869  1286  1151  520   295  4693  2824   510   891  ...
1 Designed by Freepik
R による並列プログラミング

parLapplyLB で負荷分散

microbenchmark(
  "parLapply" = {
    cl <- makeCluster(4)
    ls <- parLapply(cl, ls_weights, boot_mean)
    stopCluster(cl)
  },
  "parLapplyLB" = {
    cl <- makeCluster(4)
    ls <- parLapplyLB(cl, ls_weights, boot_mean)
    stopCluster(cl)
  },
  times = 10
)
Unit: seconds
       expr      min     mean      max neval
  parLapply 15.77250 16.38548 16.83822    10
parLapplyLB 14.55609 15.49170 17.26866    10
  • 平均実行時間は16.4秒→15.5秒に低下(約5%削減)
R による並列プログラミング

複数引数を反復処理

add <- function (x, y, z) x + y + z


value1 <- c(0.5, 3.2, 5.1, 1.9) value2 <- c(0.1, 0.5, 0.2, 2.4) value3 <- 5

1回目は value1・value2・value3 の最初の要素を加算。2回目は各ベクトルの2番目…という流れ。

R による並列プログラミング

clusterMap

cl <- makeCluster(4)


clusterMap(cl, add,
x = value1, y = value2, z = value3) # 単一値 stopCluster(cl)
print(value3)
[1] 5
[[1]]
[1] 5.6

[[2]]
[1] 8.5

[[3]]
[1] 10.2

[[4]]
[1] 8.4
R による並列プログラミング

clusterMap と parLapply の比較

clusterMap(cl,         # クラスター
           add,        # 関数
           x = value1, # 複数入力
           y = value2,
           z = value3) # 単一値のリサイクル可
parLapply(cl,                # クラスター
          input,             # 単一入力
          fun,               # 関数
          arg_name = static) # エクスポートした固定値
                             # を名前付き引数に渡す
R による並列プログラミング

clusterMap で体重増加を推定

print(ls_weights)
$AK
  [1] 30 33 50 30 26 99 52 28 24 40 30 99 40
...
print(ls_plur)
$AK
  [1] 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1
  ...
boot_dist <- function (weights, pluralities) {
  est <- rep(0, 1e4)
  ratio <- weights/pluralities
  for (i in 1:1e4) {
    b <- sample(ratio, replace = T)
    est[i] <- mean(b)
  }
  return(est)
}


cl <- makeCluster(4)
result <- clusterMap(cl, boot_dist,
weights = ls_weights, pluralities = ls_plur) stopCluster(cl)
R による並列プログラミング

出生数の合計

print(mat_births)
     1   2   3   4   5   6   7   8   9  10  11  12
AK   7  13   8   8   5   9  12  19  16  16   7  16
AL  55  58  79  66  48  63  69  81  74  76  61  82
AR  28  28  41  37  40  40  42  52  40  44  33  38
AZ  69  70  70  71  68  61  73  81  76  74  87  62
CA 603 599 659 601 669 719 675 687 660 698 703 633
CO  50  55  72  56  63  56  56  70  58  48  53  58
CT  51  51  57  58  59  41  51  46  54  56  58  52
DC  25  18  14  40  22  19  24  25  20  26  20  25
...
R による並列プログラミング

行・列演算の並列化

cl <- makeCluster(4)

# 列方向の処理に parCapply parCapply(cl, mat_births, sum)
# 行方向の処理に parRapply parRapply(cl, mat_births, sum) stopCluster(cl)
  • 列ごとの合計

     1    2    3    4    5    6    7
    4258 3966 4437 4345 4371 4482 4614
     8    9   10   11   12
    4738 4686 4483 4427 4247
    
  • 行ごとの合計

    AK   AL   AR   AZ   CA   CO   CT
    136  812  463  862 7906  695  634
    DC   DE   FL   GA   HI   IA   ID
    278  151 2519 1512  254  472  237
    ...
    
R による並列プログラミング

Passons à la pratique !

R による並列プログラミング

Preparing Video For Download...