В чем разница между lapply и do.call?


150

Я недавно изучаю R и смущен двумя функциями: lapplyи do.call. Кажется, что они просто похожи на mapфункции в Лиспе. Но почему две функции с такими разными именами? Почему R просто не использует вызываемую функцию map?

Ответы:


129

Вызывается функция, Mapкоторая может быть похожа на map на других языках:

  • lapply возвращает список той же длины, что и X, каждый элемент которого является результатом применения FUN к соответствующему элементу X.

  • do.call конструирует и выполняет вызов функции из имени или функции и списка аргументов, которые ей передаются.

  • Mapприменяет функцию к соответствующим элементам заданных векторов ... Map- это простая оболочка, для mapplyкоторой не делается попыток упростить результат, как в случае с mapcar в Common Lisp (однако, аргументы повторно используются). В будущих версиях возможно будет некоторый контроль над типом результата.


  1. Map это обертка вокруг mapply
  2. lapply это частный случай mapply
  3. Поэтому 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" 

4
на самом деле я нашел do.callпочти то же самое, что и applyв Лиспе
Hanfei Sun

разве это не последний пример, который я должен дать do.call(cbind, x)в текущей версии Error in do.call(c, x) : 'what' must be a function or character string...
sindri_baldur

1
@snoram Этот пример все еще работает. Функция cbind()отличается от функции c(), и хотя это тоже работает, дает разные результаты.
Андри

63

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

35

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)

27

Проще говоря:

  1. lapply () применяет заданную функцию для каждого элемента в списке, поэтому будет несколько вызовов функций.

  2. do.call () применяет данную функцию к списку в целом, поэтому есть только один вызов функции.

Лучший способ научиться - поиграть с примерами функций в документации R.


13

Хотя было много ответов, вот мой пример для справки. Предположим, у нас есть список данных как:

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 действительно зависит от выполняемой функции.


12

lapply()это функция, подобная карте. do.call()отличается. Он используется для передачи аргументов функции в виде списка вместо их перечисления. Например,

> do.call("+",list(4,5))
[1] 9

5

Разница между ними:

lapply(1:n,function,parameters)

=> Это отправляет 1, параметры функции => это отправляет 2, параметры функции и т. Д.

do.call 

Просто отправляет 1… n как вектор и параметры функции

Итак, в apply у вас есть n вызовов функций, в do.call у вас всего один

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.