R Language
* применять семейство функций (функционалов)
Поиск…
замечания
Функция в семействе *apply
является абстракцией цикла for
. По сравнению с петлями for
*apply
функции приложения имеют следующие преимущества:
- Требовать меньше кода для записи.
- Не имеет счетчика итераций.
- Не использует временные переменные для хранения промежуточных результатов.
Однако for
циклов более общие и могут дать нам больший контроль, позволяющий достичь сложных вычислений, которые не всегда тривиальны, используя функции *apply
.
Отношения между for
петель и *apply
функции объясняются в документации for
петель .
Члены *apply
Family
Семейство функций *apply
содержит несколько вариантов того же принципа, которые различаются в основном в зависимости от вида возвращаемого ими результата.
функция | вход | Выход |
---|---|---|
apply | matrix , data.frame или array | вектор или матрица (в зависимости от длины каждого возвращаемого элемента) |
sapply | вектор или list | вектор или матрица (в зависимости от длины каждого возвращаемого элемента) |
lapply | вектор или list | list |
vapply | вектор или `список | вектор или матрица (в зависимости от длины каждого возвращаемого элемента) назначенного пользователем класса |
mapply | множественные векторы, lists или комбинация | list |
См. «Примеры», чтобы узнать, как используется каждая из этих функций.
Использовать анонимные функции с применением
apply
используется для оценки функции (возможно, анонимной) над полями массива или матрицы.
Давайте используем набор iris
для иллюстрации этой идеи. Набор данных iris
имеет измерения 150 цветов из 3 видов. Посмотрим, как структурирован этот набор данных:
> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
Теперь представьте, что вы хотите знать среднее значение каждой из этих переменных. Одним из способов решения этой проблемы может быть использование цикла for
, но программисты R часто предпочитают использовать apply
(по причинам, см. Примечания):
> apply(iris[1:4], 2, mean)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
- В первом параметре подмножество
iris
включает только первые 4 столбца, потому чтоmean
работает только с числовыми данными. - Второе значение параметра
2
означает, что мы хотим работать только с столбцами (второй индекс массива r × c);1
даст ряд строк.
Точно так же мы можем рассчитать более значимые значения:
# standard deviation
apply(iris[1:4], 2, sd)
# variance
apply(iris[1:4], 2, var)
Предостережение : R имеет некоторые встроенные функции, которые лучше вычисляют суммы столбцов и строк и означают: colMeans
и rowMeans
.
Теперь давайте сделаем другую и более значимую задачу: вычислим среднее значение только для тех значений, которые больше 0.5
. Для этого мы создадим нашу собственную mean
функцию.
> our.mean.function <- function(x) { mean(x[x > 0.5]) }
> apply(iris[1:4], 2, our.mean.function)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.665347
(Обратите внимание на разницу в значении Petal.Width
)
Но что, если мы не хотим использовать эту функцию в остальной части нашего кода? Затем мы можем использовать анонимную функцию и написать наш код следующим образом:
apply(iris[1:4], 2, function(x) { mean(x[x > 0.5]) })
Итак, как мы видели, мы можем использовать apply
для выполнения той же операции над столбцами или строками набора данных, используя только одну строку.
Предостережение . Поскольку apply
возвращает очень разные виды вывода в зависимости от длины результатов указанной функции, это может быть не лучший выбор в тех случаях, когда вы не работаете в интерактивном режиме. Некоторые из других *apply
семейные функции, являются немного более предсказуемыми (см. Примечания).
Загрузка массового файла
для большого количества файлов, которые, возможно, необходимо будет использовать в аналогичном процессе и с хорошо структурированными именами файлов.
во-первых, должен быть создан вектор имен файлов для доступа, для этого есть несколько вариантов:
Создание вектора вручную с помощью
paste0()
files <- paste0("file_", 1:100, ".rds")
Использование
list.files()
с термином поиска в регулярном выражении для типа файла требует знания регулярных выражений ( regex ), если в каталоге находятся другие файлы того же типа.files <- list.files("./", pattern = "\\.rds$", full.names = TRUE)
где X
- вектор части используемого формата имен файлов.
lapply
выводит каждый ответ как элемент списка.
readRDS
специфичен для файлов .rds
и будет меняться в зависимости от применения процесса.
my_file_list <- lapply(files, readRDS)
Это не обязательно быстрее, чем цикл for от тестирования, но позволяет всем файлам быть элементом списка, не назначая их явно.
Наконец, нам часто приходится загружать сразу несколько пакетов. Этот трюк может сделать это довольно легко, применив library()
ко всем библиотекам, которые мы хотим импортировать:
lapply(c("jsonlite","stringr","igraph"),library,character.only=TRUE)
Объединение нескольких `data.frames` (` lapply`, `mapply`)
В этом упражнении мы создадим четыре модели линейной регрессии начальной загрузки и объединим резюме этих моделей в единый кадр данных.
library(broom)
#* Create the bootstrap data sets
BootData <- lapply(1:4,
function(i) mtcars[sample(1:nrow(mtcars),
size = nrow(mtcars),
replace = TRUE), ])
#* Fit the models
Models <- lapply(BootData,
function(BD) lm(mpg ~ qsec + wt + factor(am),
data = BD))
#* Tidy the output into a data.frame
Tidied <- lapply(Models,
tidy)
#* Give each element in the Tidied list a name
Tidied <- setNames(Tidied, paste0("Boot", seq_along(Tidied)))
На этом этапе мы можем использовать два подхода для вставки имен в data.frame.
#* Insert the element name into the summary with `lapply`
#* Requires passing the names attribute to `lapply` and referencing `Tidied` within
#* the applied function.
Described_lapply <-
lapply(names(Tidied),
function(nm) cbind(nm, Tidied[[nm]]))
Combined_lapply <- do.call("rbind", Described_lapply)
#* Insert the element name into the summary with `mapply`
#* Allows us to pass the names and the elements as separate arguments.
Described_mapply <-
mapply(
function(nm, dframe) cbind(nm, dframe),
names(Tidied),
Tidied,
SIMPLIFY = FALSE)
Combined_mapply <- do.call("rbind", Described_mapply)
Если вы являетесь поклонником magrittr
стиля magrittr
, вы можете выполнить всю задачу в одной цепочке (хотя это может быть неразумно делать, если вам нужны какие-либо промежуточные объекты, такие как сами объекты модели):
library(magrittr)
library(broom)
Combined <- lapply(1:4,
function(i) mtcars[sample(1:nrow(mtcars),
size = nrow(mtcars),
replace = TRUE), ]) %>%
lapply(function(BD) lm( mpg ~ qsec + wt + factor(am), data = BD)) %>%
lapply(tidy) %>%
setNames(paste0("Boot", seq_along(.))) %>%
mapply(function(nm, dframe) cbind(nm, dframe),
nm = names(.),
dframe = .,
SIMPLIFY = FALSE) %>%
do.call("rbind", .)
Использование встроенных функционалов
Встроенные функционалы: lapply (), sapply () и mapply ()
R поставляется со встроенными функционалами, из которых, возможно, наиболее известными являются семейство функций. Ниже приведено описание некоторых из наиболее распространенных функций приложения:
-
lapply()
= принимает список в качестве аргумента и применяет указанную функцию к списку. -
sapply()
= то же, что иlapply()
но пытается упростить вывод к вектору или матрице.-
vapply()
= вариантsapply()
в котором должен указываться тип выходного объекта.
-
-
mapply()
= likelapply()
но может передавать несколько векторов в качестве входных данных для указанной функции. Может быть упрощено, например,sapply()
.-
Map()
является псевдонимомmapply()
сSIMPLIFY = FALSE
.
-
lapply ()
lapply()
может использоваться с двумя разными итерациями:
-
lapply(variable, FUN)
-
lapply(seq_along(variable), FUN)
# Two ways of finding the mean of x
set.seed(1)
df <- data.frame(x = rnorm(25), y = rnorm(25))
lapply(df, mean)
lapply(seq_along(df), function(x) mean(df[[x]))
sapply ()
sapply()
попытается разрешить свой вывод либо вектору, либо матрице.
# Two examples to show the different outputs of sapply()
sapply(letters, print) ## produces a vector
x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE))
sapply(x, quantile) ## produces a matrix
mapply ()
mapply()
работает так же, как и lapply()
за исключением того, что он может принимать несколько векторов в качестве входных данных (следовательно, m для многомерного).
mapply(sum, 1:5, 10:6, 3) # 3 will be "recycled" by mapply
Использование пользовательских функций
Пользовательские функции
Пользователи могут создавать свои собственные функционалы в различной степени сложности. Следующие примеры взяты из « Функционалов » Хэдли Уикхэма:
randomise <- function(f) f(runif(1e3))
lapply2 <- function(x, f, ...) {
out <- vector("list", length(x))
for (i in seq_along(x)) {
out[[i]] <- f(x[[i]], ...)
}
out
}
В первом случае randomise
принимает единственный аргумент f
и вызывает его на выборке из Uniform случайных величин. Чтобы продемонстрировать эквивалентность, назовем set.seed
ниже:
set.seed(123)
randomise(mean)
#[1] 0.4972778
set.seed(123)
mean(runif(1e3))
#[1] 0.4972778
set.seed(123)
randomise(max)
#[1] 0.9994045
set.seed(123)
max(runif(1e3))
#[1] 0.9994045
Второй пример - это повторная реализация base::lapply
, которая использует функции для применения операции ( f
) к каждому элементу в списке ( x
). Параметр ...
позволяет пользователю передавать дополнительные аргументы в f
, например, параметр na.rm
в mean
функции:
lapply(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
#
# [[2]]
# [1] NA
lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
#
# [[2]]
# [1] NA
lapply(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
#
# [[2]]
# [1] 4
lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
#
# [[2]]
# [1] 4