Я недавно изучаю R и смущен двумя функциями: lapply
и do.call
. Кажется, что они просто похожи на map
функции в Лиспе. Но почему две функции с такими разными именами? Почему R просто не использует вызываемую функцию map
?
Я недавно изучаю R и смущен двумя функциями: lapply
и do.call
. Кажется, что они просто похожи на map
функции в Лиспе. Но почему две функции с такими разными именами? Почему R просто не использует вызываемую функцию map
?
Ответы:
Вызывается функция, Map
которая может быть похожа на map на других языках:
lapply
возвращает список той же длины, что и X, каждый элемент которого является результатом применения FUN к соответствующему элементу X.
do.call
конструирует и выполняет вызов функции из имени или функции и списка аргументов, которые ей передаются.
Map
применяет функцию к соответствующим элементам заданных векторов ... Map
- это простая оболочка, для mapply
которой не делается попыток упростить результат, как в случае с mapcar в Common Lisp (однако, аргументы повторно используются). В будущих версиях возможно будет некоторый контроль над типом результата.
Map
это обертка вокруг mapply
lapply
это частный случай mapply
Map
и lapply
во многих случаях будет аналогично.Например, вот lapply
:
lapply(iris, class)
$Sepal.Length
[1] "numeric"
$Sepal.Width
[1] "numeric"
$Petal.Length
[1] "numeric"
$Petal.Width
[1] "numeric"
$Species
[1] "factor"
И то же самое, используя Map
:
Map(class, iris)
$Sepal.Length
[1] "numeric"
$Sepal.Width
[1] "numeric"
$Petal.Length
[1] "numeric"
$Petal.Width
[1] "numeric"
$Species
[1] "factor"
do.call
принимает функцию в качестве входных данных и передает ей другие аргументы. Он широко используется, например, для объединения списков в более простые структуры (часто с помощью rbind
или cbind
).
Например:
x <- lapply(iris, class)
do.call(c, x)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
"numeric" "numeric" "numeric" "numeric" "factor"
do.call(cbind, x)
в текущей версии Error in do.call(c, x) : 'what' must be a function or character string
...
cbind()
отличается от функции c()
, и хотя это тоже работает, дает разные результаты.
lapply
применяет функцию к списку, do.call
вызывает функцию со списком аргументов. Мне кажется, это большая разница ...
Чтобы привести пример со списком:
X <- list(1:3,4:6,7:9)
Используя lapply, вы получаете среднее значение каждого элемента в списке следующим образом:
> lapply(X,mean)
[[1]]
[1] 2
[[2]]
[1] 5
[[3]]
[1] 8
do.call
выдает ошибку, поскольку среднее ожидает, что аргумент "обрезать" будет равен 1.
С другой стороны, rbind
связывает все аргументы построчно. Итак, чтобы связать X по строкам, вы выполните:
> do.call(rbind,X)
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 4 5 6
[3,] 7 8 9
Если бы вы использовали lapply
, R применился бы rbind
к каждому элементу списка, давая вам эту ерунду:
> lapply(X,rbind)
[[1]]
[,1] [,2] [,3]
[1,] 1 2 3
[[2]]
[,1] [,2] [,3]
[1,] 4 5 6
[[3]]
[,1] [,2] [,3]
[1,] 7 8 9
Чтобы иметь что-то вроде Map, вам нужно ?mapply
, а это совсем другое дело. Чтобы получить, например, среднее значение каждого элемента в X, но с другой обрезкой, вы можете использовать:
> mapply(mean,X,trim=c(0,0.5,0.1))
[1] 2 5 8
lapply
похоже на map
, do.call
нет. lapply
применяет функцию ко всем элементам списка, do.call
вызывает функцию, в которой все аргументы функции находятся в списке. Итак, для n
списка элементов lapply
есть n
вызовы функций и do.call
только один вызов функции. Так do.call
сильно отличается от lapply
. Надеюсь, это проясняет вашу проблему.
Пример кода:
do.call(sum, list(c(1, 2, 4, 1, 2), na.rm = TRUE))
а также:
lapply(c(1, 2, 4, 1, 2), function(x) x + 1)
Проще говоря:
lapply () применяет заданную функцию для каждого элемента в списке, поэтому будет несколько вызовов функций.
do.call () применяет данную функцию к списку в целом, поэтому есть только один вызов функции.
Лучший способ научиться - поиграть с примерами функций в документации R.
Хотя было много ответов, вот мой пример для справки. Предположим, у нас есть список данных как:
L=list(c(1,2,3), c(4,5,6))
Функция lapply возвращает список.
lapply(L, sum)
Вышеупомянутое означает что-то вроде ниже.
list( sum( L[[1]]) , sum( L[[2]]))
Теперь сделаем то же самое для do.call.
do.call(sum, L)
Это значит
sum( L[[1]], L[[2]])
В нашем примере он возвращает 21. Короче говоря, lapply всегда возвращает список, а тип возврата do.call действительно зависит от выполняемой функции.
do.call
почти то же самое, что иapply
в Лиспе