R Academy · Урок

reduce(), accumulate() и walk()

Сворачивайте списки, создавайте накопительные результаты и применяйте побочные эффекты с помощью walk().

Урок 3 из 413 шагов

«reduce(), accumulate() и walk()» — бесплатный урок R Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения R Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс R Academy содержит 4 уроков всего.

За пределами map() — другие функции purrr

Функция map() применяет функцию независимо к каждому элементу, но для некоторых задач требуется объединять элементы (reduce), накапливать промежуточные результаты или применять функции исключительно ради побочных эффектов (walk). В purrr для каждого такого случая предусмотрены отдельные функции.

library(purrr)

# Example: combining a list of numbers into a single value
nums <- list(1, 2, 3, 4, 5)

# We want: 1 + 2 + 3 + 4 + 5 = 15
# map() can't do this — it returns 5 separate results
# reduce() folds the list into one value
result <- reduce(nums, `+`)
cat('Sum via reduce:', result)

reduce() — свёртка списка в одно значение

reduce(.x, .f, .init) последовательно применяет .f к элементам .x слева направо, сворачивая список в одно значение. reduce(list(a,b,c), f) вычисляет f(f(a,b),c).

library(purrr)

# reduce with max: find the overall maximum
values <- list(42, 17, 89, 55, 23)
result <- reduce(values, max)
cat('Maximum:', result, '\n')

# reduce with paste: concatenate strings
words <- list('The', 'quick', 'brown', 'fox')
reduce(words, paste)

reduce() с .init

Аргумент .init задаёт начальное значение (seed). Первый вызов имеет вид f(.init, x[1]). Это особенно важно, если список может быть пустым (результатом будет .init) или если требуется определённое начальное состояние.

library(purrr)

# Running count of elements matching a condition
data <- list(5, 12, 3, 18, 7, 15, 2)

# Count values greater than 10, starting from 0
reduce(data, function(count, x) count + (x > 10), .init = 0L)

# Also useful for string building:
reduce(c('A','B','C'), paste, sep='-', .init='START')

reduce() с датафреймами — последовательное объединение

Одно из самых мощных применений reduce() — объединение списка датафреймов. Вместо записи left_join(left_join(df1, df2), df3) используйте reduce(list_of_dfs, left_join, by='key').

library(purrr)
library(dplyr)

base_df <- data.frame(id=1:3, name=c('Alice','Bob','Carol'))
scores <- data.frame(id=1:3, score=c(85,90,78))
grades <- data.frame(id=1:3, grade=c('B','A','C'))
city <- data.frame(id=1:3, city=c('NYC','LA','Chicago'))

dfs <- list(base_df, scores, grades, city)

reduce(dfs, left_join, by='id')

accumulate() — сохранение промежуточных результатов

accumulate(.x, .f) похожа на reduce(), но сохраняет все промежуточные значения. Функция возвращает вектор (или список) той же длины, что и входные данные, показывая накопленный результат после каждого шага.

library(purrr)

# Running sum — same as cumsum() but via accumulate
values <- c(100, 120, 95, 140, 160)
accumulate(values, `+`)

# Running maximum
accumulate(c(50, 52, 48, 55, 53, 58), max)

accumulate() для пошаговых результатов

accumulate() особенно полезна для отслеживания шагов алгоритма, посимвольного построения строк и любых процессов, в которых нужно просматривать каждое промежуточное состояние, а не только итоговый результат.

library(purrr)

# Compound interest step by step
principal <- 1000
rates <- c(0.05, 0.05, 0.05, 0.05, 0.05)  # 5% per year

accumulate(rates, function(balance, rate) {
  round(balance * (1 + rate), 2)
}, .init = principal)

walk() — побочные эффекты без возвращаемого результата

walk(.x, .f) применяет .f к каждому элементу .x исключительно ради побочных эффектов (вывода, записи файлов, отправки сообщений). Функция неявно возвращает .x, поэтому её можно использовать в конвейере, не разрывая цепочку.

library(purrr)

# Print a summary for each dataset
datasets <- list(
  mtcars = mtcars[,1:3],
  iris = iris[,1:3]
)

walk(datasets, function(df) {
  cat('Rows:', nrow(df), '| Cols:', ncol(df),
      '| NAs:', sum(is.na(df)), '\n')
})

walk() в конвейере

Поскольку walk() неявно возвращает .x, её можно вставить в конвейер dplyr, чтобы записывать в журнал или выводить промежуточные результаты, не нарушая последовательность операций. Представляйте её как точку подключения для отладки или ведения журнала.

library(purrr)
library(dplyr)

results <- list(
  East = data.frame(rep=c('A','B'), sales=c(100,120)),
  West = data.frame(rep=c('C','D'), sales=c(200,190))
)

# Log each region's data, then continue processing
region_totals <- results %>%
  walk(~cat('Processing:', nrow(.x), 'rows\n')) %>%
  map_dbl(~sum(.x$sales))

print(region_totals)

walk2() — побочные эффекты с двумя входными наборами

walk2(.x, .y, .f) — версия walk() для двух входных наборов. Классический пример: запись нескольких датафреймов в файлы, где .x — список датафреймов, а .y — список путей к файлам.

library(purrr)

data_list <- list(
  east = data.frame(x=1:3, y=4:6),
  west = data.frame(x=7:9, y=10:12)
)

file_paths <- list(
  '/tmp/east_data.csv',
  '/tmp/west_data.csv'
)

# Write each data frame to its corresponding path
walk2(data_list, file_paths, function(df, path) {
  write.csv(df, path, row.names=FALSE)
  cat('Written:', path, '\n')
})

iwalk() — обход с индексом

iwalk(.x, .f) похожа на walk(), но передаёт имя элемента (или индекс) вторым аргументом .y. Это удобно, когда в побочном эффекте нужно знать, какой именно элемент обрабатывается.

library(purrr)

region_counts <- list(East=150, West=200, North=85, South=120)

# Print each region with its rank
iwalk(region_counts, function(count, name) {
  cat(name, ':', count, 'customers\n')
})

reduce_right() — свёртка справа налево

reduce(.x, .f, .dir='backward') (или устаревшая reduce_right()) применяет функцию справа налево: f(a, f(b, f(c, d))). Это важно для некоммутативных операций, например вычитания или построения строк.

library(purrr)

words <- c('fox','brown','quick','The')

# Left-to-right: 'The quick brown fox'
reduce(rev(words), paste)

# Right-to-left: same effect but starting from right
reduce(words, function(a, b) paste(b, a))

Быстрая проверка

Когда следует использовать walk() вместо map()?

Итоги: reduce, accumulate, walk

Главные выводы о reduce, accumulate и walk:

  • reduce(.x, .f) — сворачивает список в одно значение (слева направо); .init задаёт начальное значение
  • reduce(list_of_dfs, left_join) — мощный шаблон для последовательного объединения датафреймов
  • accumulate(.x, .f) — похожа на reduce, но возвращает все промежуточные результаты
  • walk(.x, .f) — применяется ради побочных эффектов (вывод, запись, ведение журнала); возвращает .x
  • walk2(.x, .y, .f) — walk для двух входных наборов (например, датафрейм и путь к файлу)
  • iwalk(.x, .f) — обход с именем элемента в качестве второго аргумента
library(purrr)
library(dplyr)

# reduce() joins a list of data frames, accumulate() shows interim states
monthly_sums <- c(100, 120, 95, 140, 160, 130)

cat('reduce (total):      ', reduce(monthly_sums, `+`), '\n')
cat('accumulate (running):', accumulate(monthly_sums, `+`), '\n')

# walk() for side-effect logging
results <- list(a=42, b=17, c=89)
walk(results, ~cat('Value:', .x, '\n'))
Можно начать бесплатно

Изучай R с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
43
Уроки
159

Часто задаваемые вопросы

Урок «reduce(), accumulate() и walk()» бесплатный?

Да — полный текст урока «reduce(), accumulate() и walk()» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс R Academy, подпишись на CoddyKit PRO. Курс R Academy содержит 4 уроков всего.

Чему я научусь в уроке «reduce(), accumulate() и walk()»?

Сворачивайте списки, создавайте накопительные результаты и применяйте побочные эффекты с помощью walk(). Ты практикуешь R Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать R Academy?

Предыдущий опыт не требуется. R Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «reduce(), accumulate() и walk()»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке R Academy?

Да. Каждый урок R Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. map() и типизированные варианты
  2. map2() и pmap() для нескольких входных данных
  3. reduce(), accumulate() и walk()
  4. keep(), discard() и фильтрация списков
← Назад к R Academy