R Academy · Lekcja

furrr: równoległe operacje purrr

Zastąp map() bez dodatkowych zmian funkcją future_map(), aby natychmiast zrównoleglić obliczenia.

Lekcja 3 z 413 kroki

furrr: równoległe operacje purrr to bezpłatna lekcja R Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej R Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs R Academy zawiera 4 lekcji w sumie.

furrr: równoległy purrr

furrr (future + purrr) udostępnia bezpośrednie, równoległe zamienniki wszystkich funkcji purrr::map_*(). Wystarczy po ustawieniu funkcji plan() zamienić map() na future_map(), aby potok działał równolegle bez żadnych zmian strukturalnych.

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)

Jawne określenie wartości workers w funkcji plan() ogranicza liczbę równoległych sesji R. W przypadku zadań obciążających procesor często stosuje się workers = parallel::detectCores() - 1, aby pozostawić jeden rdzeń dla systemu operacyjnego.

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 i warianty typowane

Podobnie jak purrr, pakiet furrr udostępnia warianty typowane: future_map_dbl(), future_map_int(), future_map_chr() oraz future_map_lgl(). Wymuszają one typ zwracanej wartości i zwracają wektor atomowy zamiast listy.

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: odwzorowywanie dwóch danych wejściowych

future_map2(.x, .y, .f) równolegle iteruje po dwóch listach lub wektorach, przekazując funkcji odpowiadające sobie pary elementów. Jest to równoległy odpowiednik funkcji 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: kontrolowanie działania

Funkcję furrr_options() przekazuje się jako argument .options do dowolnego wywołania future_map_*(). Najważniejszym ustawieniem jest seed = TRUE, które włącza równoległy generator liczb pseudolosowych L'Ecuyer-CMRG, zapewniający powtarzalność liczb losowych między procesami roboczymi.

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)

Raportowanie postępu za pomocą progressr

Pakiet progressr integruje się z furrr, aby wyświetlać paski postępu podczas wykonywania równoległego. Proszę opakować kod w with_progress() i utworzyć funkcję progressor() wewnątrz funkcji używanej do mapowania.

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)

Zmienne globalne w furrr

Podobnie jak pakiet future, furrr automatycznie wykrywa zmienne globalne, do których odwołuje się kod wewnątrz .f. Można to zmienić za pomocą furrr_options(globals = c('var1', 'var2')), aby dokładnie określić, które zmienne globalne należy przesłać, i zmniejszyć narzut związany z dużymi środowiskami.

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: odwzorowywanie wielu argumentów

future_pmap(.l, .f) jest równoległą wersją funkcji purrr::pmap(). Przyjmuje listę wektorów lub list i przekazuje funkcji odpowiadające sobie wiersze jako nazwane argumenty, umożliwiając równoległe obliczenia dla wielu kombinacji parametrów.

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)

Benchmarking furrr i purrr

Korzyści z przetwarzania równoległego rosną wraz z ciężarem zadań. W przypadku prostych operacji (x^2) dominuje narzut i szybsze jest wykonywanie sekwencyjne. W przypadku ciężkich zadań, takich jak dopasowywanie wielu modeli, przetwarzanie równoległe pozwala znacznie skrócić czas.

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)

Obsługa błędów w future_map

Jeśli podczas obliczeń dowolnego elementu wystąpi błąd, future_map() zatrzymuje działanie i zgłasza go ponownie. Aby kontynuować mimo błędów, proszę użyć opakowań purrr::safely() lub purrr::possibly() wokół swojej funkcji.

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)

Praktyczny potok furrr

Oto kompletny potok od początku do końca: wczytanie danych, równoległe dopasowanie wielu modeli z powtarzalnymi ziarnami losowości, wyodrębnienie metryk jakości i wybranie najlepszego modelu — wszystko zgodnie z idiomem pakietu 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)

Szybkie sprawdzenie

Chcą Państwo uzyskać powtarzalne liczby losowe w kolejnych równoległych wywołaniach future_map(). Które ustawienie funkcji furrr_options() to zapewnia?

Podsumowanie: pakiet furrr

Najważniejsze informacje:

  • furrr jest bezpośrednim, równoległym zamiennikiem pakietu purrr — wystarczy zamienić map na future_map
  • Najpierw należy ustawić backend za pomocą plan(multisession, workers = n)
  • Warianty typowane: future_map_dbl(), future_map_int(), future_map_chr()
  • future_map2() i future_pmap() służą do równoległego odwzorowywania wielu danych wejściowych
  • furrr_options(seed = 42L) zapewnia powtarzalność równoległego generatora liczb pseudolosowych
  • Proszę zintegrować progressr, aby wyświetlać paski postępu podczas długich zadań równoległych
  • Proszę używać purrr::safely() wewnątrz future_map(), aby tworzyć potoki odporne na błędy
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)
Bezpłatny start

Ucz się R dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
43
Lekcje
159

Często zadawane pytania

Czy lekcja „furrr: równoległe operacje purrr” jest bezpłatna?

Tak — pełny tekst „furrr: równoległe operacje purrr” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu R Academy, przejdź na CoddyKit PRO. Kurs R Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „furrr: równoległe operacje purrr”?

Zastąp map() bez dodatkowych zmian funkcją future_map(), aby natychmiast zrównoleglić obliczenia. Ćwiczysz R Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć R Academy?

Nie wymagamy żadnego doświadczenia. R Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „furrr: równoległe operacje purrr”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji R Academy?

Tak. Każda lekcja R Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Pakiet parallel i detectCores()
  2. Framework future
  3. furrr: równoległe operacje purrr
  4. Debugowanie i równoważenie obciążenia kodu równoległego
← Powrót do R Academy