0Pricing
R Academy · レッスン

furrr:purrr 操作の並列化

map() を future_map() に置き換えるだけで、すぐに並列化します。

「furrr:purrr 操作の並列化」はCoddyKit上の無料R Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。

furrr: 並列版purrr

furrr(future + purrr)は、すべてのpurrr::map_*()関数に対応する、置き換えて使える並列版を提供します。plan()を設定した後、map()をfuture_map()に置き換えるだけで、コードの構造をまったく変更せずにパイプラインを並列実行できます。

library(furrr)
library(future)

# Set up parallel workers
plan(multisession, workers = 4)

# Sequential (purrr)
# result <- purrr::map(1:8, ~.x^2)

# Parallel (furrr) — identical API
result <- future_map(1:8, ~.x^2)
cat(unlist(result), '\n')  # 1 4 9 16 25 36 49 64

plan(sequential)

plan(multisession, workers = 4)

plan()でworkersを明示的に指定すると、並列で実行するRセッション数に上限を設定できます。CPUバウンドなタスクでは、OS用に1コアを残すため、workers = parallel::detectCores() - 1とするのが一般的です。

library(furrr)
library(future)
library(parallel)

# Explicit worker count
n_workers <- max(1, detectCores() - 1)
plan(multisession, workers = n_workers)

cat('Active workers:', nbrOfWorkers(), '\n')
cat('Strategy:', class(plan())[1], '\n')

# Run a simple parallel task
results <- future_map_dbl(1:8, ~sqrt(.x))
cat(round(results, 3), '\n')

plan(sequential)

future_map_dblと型付きバリアント

purrrと同様に、furrrは型付きバリアントとしてfuture_map_dbl()、future_map_int()、future_map_chr()、future_map_lgl()を提供します。これらは戻り値の型を保証し、リストではなくatomic vectorを返します。

library(furrr)
plan(multisession, workers = 2)

# Returns a numeric vector
square_roots <- future_map_dbl(1:6, ~sqrt(.x))
cat('dbl:', round(square_roots, 3), '\n')

# Returns an integer vector
counts <- future_map_int(list('hello', 'world', 'R'), nchar)
cat('int:', counts, '\n')

# Returns a character vector
formatted <- future_map_chr(c(1.23, 4.56, 7.89), ~sprintf('%.1f', .x))
cat('chr:', formatted, '\n')

# Returns a logical vector
positive <- future_map_lgl(-3:3, ~.x > 0)
cat('lgl:', positive, '\n')

plan(sequential)

future_map2: 2入力のマッピング

future_map2(.x, .y, .f)は、2つのリストまたはベクトルを並列に反復処理し、対応するペアを関数に渡します。これはpurrr::map2()の並列版です。

library(furrr)
plan(multisession, workers = 2)

# Simulate different sample sizes and means
sizes <- c(100, 200, 300, 400)
means <- c(0, 5, -3, 10)

# future_map2 passes each (n, mu) pair to rnorm
samples <- future_map2(sizes, means, ~rnorm(.x, mean = .y))

# Verify: each element has the expected length and approximate mean
for (i in seq_along(samples)) {
  cat('n=', sizes[i], 'target_mean=', means[i],
      'observed_mean=', round(mean(samples[[i]]), 2), '\n')
}

plan(sequential)

furrr_options: 動作の制御

furrr_options()は、任意のfuture_map_*()呼び出しに.options引数として渡します。最も重要な設定はseed = TRUEです。これにより、ワーカー間で再現可能な乱数を生成するL'Ecuyer-CMRG並列RNGが有効になります。

library(furrr)
plan(multisession, workers = 2)

# Without seed: results differ each run
r1 <- future_map_dbl(1:4, ~rnorm(1))
r2 <- future_map_dbl(1:4, ~rnorm(1))
cat('Without seed - same?', identical(r1, r2), '\n')

# With seed: reproducible
opts <- furrr_options(seed = 42L)
r3 <- future_map_dbl(1:4, ~rnorm(1), .options = opts)
r4 <- future_map_dbl(1:4, ~rnorm(1), .options = opts)
cat('With seed - same?', identical(r3, r4), '\n')
cat('r3:', round(r3, 4), '\n')

plan(sequential)

progressrによる進捗表示

progressrパッケージはfurrrと統合して、並列実行中にプログレスバーを表示できます。コードをwith_progress()でラップし、マッピングする関数の内部でprogressor()を作成します。

library(furrr)
library(progressr)
plan(multisession, workers = 2)

# Enable progress reporting
handlers(global = TRUE)  # show progress in console

with_progress({
  p <- progressor(steps = 8)

  results <- future_map(1:8, function(i) {
    p()  # increment the progress bar
    Sys.sleep(0.1)
    i^2
  })
})

cat('Results:', unlist(results), '\n')

plan(sequential)

furrrでのグローバル変数

futureパッケージと同様に、furrrは.f内で参照されるグローバル変数を自動検出します。furrr_options(globals = c('var1', 'var2'))を使って、送信するグローバル変数を正確に指定し、大きな環境の転送に伴うオーバーヘッドを減らすこともできます。

library(furrr)
plan(multisession, workers = 2)

# Global variables auto-detected
scale_factor <- 10
offset <- 5

result <- future_map_dbl(
  1:6,
  function(x) x * scale_factor + offset
)
cat(result, '\n')  # 15 25 35 45 55 65

# Explicit globals control
opts <- furrr_options(
  globals = c('scale_factor', 'offset'),
  seed = FALSE
)
result2 <- future_map_dbl(
  1:6,
  function(x) x * scale_factor + offset,
  .options = opts
)
cat('Manual globals:', result2, '\n')

plan(sequential)

future_pmap: 複数引数のマッピング

future_pmap(.l, .f)はpurrr::pmap()の並列版です。ベクトルまたはリストのリストを受け取り、対応する行を名前付き引数として渡すことで、複数のパラメータの組み合わせを並列に計算できます。

library(furrr)
plan(multisession, workers = 2)

# Parameter grid
params <- list(
  n    = c(50, 100, 150, 200),
  mean = c(0, 1, 2, 3),
  sd   = c(1, 2, 1, 0.5)
)

# future_pmap passes each row as arguments to rnorm
samples <- future_pmap(params, function(n, mean, sd) {
  x <- rnorm(n, mean = mean, sd = sd)
  c(obs_mean = round(mean(x), 3), obs_sd = round(sd(x), 3))
})

for (i in seq_along(samples)) {
  cat('n=', params$n[i], ':', samples[[i]], '\n')
}

plan(sequential)

furrrとpurrrのベンチマーク比較

並列化の効果はタスクの負荷に応じて大きくなります。x^2のような単純な処理ではオーバーヘッドが支配的になり、逐次実行のほうが高速です。一方、多数のモデルのフィッティングのような重い処理では、並列化によって大幅に時間を短縮できます。

library(furrr)
library(purrr)
plan(multisession, workers = 4)

# Heavy task: bootstrap a linear model 100 times
heavy <- function(i) {
  n <- 200
  df <- data.frame(x = rnorm(n), y = rnorm(n))
  coef(lm(y ~ x, data = df))[['x']]
}

seq_time <- system.time(map_dbl(1:40, heavy))[['elapsed']]
par_time <- system.time(
  future_map_dbl(1:40, heavy, .options = furrr_options(seed = TRUE))
)[['elapsed']]

cat('Sequential:', round(seq_time, 2), 's\n')
cat('Parallel:  ', round(par_time, 2), 's\n')
cat('Speedup:   ', round(seq_time / max(par_time, 0.001), 2), 'x\n')

plan(sequential)

future_mapでのエラー処理

いずれかの要素の計算でエラーが発生すると、future_map()は処理を停止してエラーを再スローします。エラーが発生しても処理を続行するには、関数をpurrr::safely()またはpurrr::possibly()でラップします。

library(furrr)
library(purrr)
plan(multisession, workers = 2)

# Wrap with safely() to capture errors as results
safe_log <- safely(log, otherwise = NA_real_)

inputs <- list(10, -1, 100, 'text', 0.5)
results <- future_map(inputs, safe_log)

for (i in seq_along(results)) {
  if (is.null(results[[i]]$error)) {
    cat('Input', i, '-> result:', round(results[[i]]$result, 4), '\n')
  } else {
    cat('Input', i, '-> error:', conditionMessage(results[[i]]$error), '\n')
  }
}

plan(sequential)

実践的なfurrrパイプライン

ここでは、データの読み込み、再現可能なシードを使った複数モデルの並列フィッティング、性能指標の抽出、最良モデルの選択までを行う、完全なエンドツーエンドのパイプラインを示します。すべてfurrrのイディオムで実装します。

library(furrr)
library(purrr)
plan(multisession, workers = 4)

set.seed(1)
n <- 300
df <- data.frame(
  x1 = rnorm(n), x2 = rnorm(n), x3 = rnorm(n),
  y  = rnorm(n)
)

formulas <- list(
  y ~ x1,
  y ~ x1 + x2,
  y ~ x1 + x2 + x3,
  y ~ x1 * x2
)

# Fit all models in parallel
models <- future_map(
  formulas,
  ~lm(.x, data = df),
  .options = furrr_options(seed = FALSE)
)

# Extract adjusted R-squared
adj_r2 <- map_dbl(models, ~summary(.x)$adj.r.squared)
cat('Adjusted R2 per model:',
    paste(round(adj_r2, 4), collapse = ', '), '\n')
cat('Best model:', which.max(adj_r2), '\n')

plan(sequential)

確認問題

並列future_map()呼び出し間で再現可能な乱数を生成したいとします。これを実現するfurrr_options()の設定はどれでしょうか。

まとめ: furrrパッケージ

重要なポイント:

  • furrrはpurrrの置き換えて使える並列版です。mapをfuture_mapに置き換えるだけで使用できます
  • まずplan(multisession, workers = n)でバックエンドを設定します
  • 型付きバリアントにはfuture_map_dbl()、future_map_int()、future_map_chr()があります
  • 複数入力の並列マッピングにはfuture_map2()とfuture_pmap()を使用します
  • 再現可能な並列RNGにはfurrr_options(seed = 42L)を設定します
  • 長時間の並列ジョブではprogressrを統合してプログレスバーを表示します
  • エラーに強いパイプラインには、future_map()内でpurrr::safely()を使用します
library(furrr)
plan(multisession, workers = 2)

results <- future_map_dbl(
  1:6,
  ~.x^2 + sqrt(.x),
  .options = furrr_options(seed = TRUE)
)
cat(round(results, 3), '\n')

plan(sequential)

よくある質問

「furrr:purrr 操作の並列化」レッスンは無料ですか?

はい。「furrr:purrr 操作の並列化」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「furrr:purrr 操作の並列化」で何を学びますか?

map() を future_map() に置き換えるだけで、すぐに並列化します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

R Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「furrr:purrr 操作の並列化」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このR Academyレッスンでコードを書いて実行できますか?

はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. parallel パッケージと detectCores()
  2. future フレームワーク
  3. furrr:purrr 操作の並列化
  4. 並列コードのデバッグと負荷分散
← R Academyに戻る