Поиск…


замечания

Функция в семействе *apply является абстракцией цикла for . По сравнению с петлями for *apply функции приложения имеют следующие преимущества:

  1. Требовать меньше кода для записи.
  2. Не имеет счетчика итераций.
  3. Не использует временные переменные для хранения промежуточных результатов.

Однако 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() = like lapply() но может передавать несколько векторов в качестве входных данных для указанной функции. Может быть упрощено, например, 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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow