Функции и apply-семейство
Учимся писать собственные функции и применять их ко множеству значений без громоздких циклов.
Функция — именованный блок кода, принимающий аргументы и возвращающий результат; основа повторного использования.
Когда один и тот же расчёт нужен много раз, его оформляют в функцию. А чтобы применить функцию к каждому элементу или каждой колонке, в R вместо цикла часто берут компактное apply-семейство.
Функции — это способ дать имя куску логики и перестать его дублировать. Скопированный пять раз расчёт придётся и править в пяти местах; вынесенный в функцию — в одном. Кроме того, R — функциональный язык: здесь функции можно передавать другим функциям как аргументы. Именно на этом построено apply-семейство — вы отдаёте ему функцию, а оно само применяет её ко всем элементам. Понимание этой идеи отличает уверенного пользователя R от новичка, который везде пишет циклы.
Своя функция
Функцию создают ключевым словом function и присваивают имени:
to_celsius <- function(f) {
(f - 32) * 5 / 9
}
to_celsius(98.6)Вывод:
[1] 37
Аргумент f — вход, результат последнего выражения возвращается автоматически (явный return не обязателен). Поскольку операции векторные, функция сразу работает и с вектором температур.
Цикл for
Иногда цикл нужен — например, для побочных действий:
for (name in c("Аня", "Борис")) {
print(paste("Привет,", name))
}Вывод:
[1] "Привет, Аня" [1] "Привет, Борис"
sapply и lapply вместо цикла
Чтобы применить функцию к каждому элементу и собрать результаты, берут sapply (возвращает вектор) или lapply (возвращает список):
temps_f <- c(32, 50, 98.6, 212)
sapply(temps_f, to_celsius)Вывод:
[1] 0 10 37 100
Одна строка заменила цикл: sapply прошёл по вектору и применил функцию к каждому значению.
Как работает под капотом
Семейство apply — это не «магия скорости», а способ выразить намерение «применить функцию ко всем элементам» одной строкой. Внутри R всё равно проходит по элементам, но код становится короче и яснее цикла. Разница в результате: lapply всегда возвращает список (по элементу на вход), а sapply пытается упростить его до вектора или матрицы, если получается. Когда упрощение невозможно, sapply тихо вернёт список — это источник неожиданностей, поэтому для надёжности иногда берут строгий vapply с указанием типа результата.
Частые ошибки
- Писать цикл там, где хватит векторной операции.
to_celsius(temps_f)уже работает со всем вектором —sapplyнужен лишь когда функция не векторизована. - Ждать от
sapplyвсегда вектор. Если результаты разной длины, вернётся список. - Забывать про область видимости. Переменные внутри функции локальны и не «протекают» наружу.
Итог
- Функция создаётся через
function(args) { ... }, последнее выражение возвращается. forподходит для побочных действий.sapplyприменяет функцию к элементам и упрощает результат,lapplyвозвращает список.- Часто векторная операция избавляет от цикла вовсе.