Зачем использовать purrr:: map вместо lapply?
есть ли причина, по которой я должен использовать
map(<list-like-object>, function(x) <do stuff>)
вместо
lapply(<list-like-object>, function(x) <do stuff>)
результат должен быть таким же, и критерии, которые я сделал, похоже, показывают, что lapply
немного быстрее (это должно быть как map
необходимо оценить все нестандартные оценки ввода).
Итак, есть ли причина, по которой для таких простых случаев я должен фактически рассмотреть переход на purrr::map
? Я не спрашиваю здесь о том, нравится или не нравится синтаксис, другое функциональные возможности, предоставляемые purrr и т. д., но строго о сравнении purrr::map
С lapply
предполагая использование стандартной оценки, т. е. map(<list-like-object>, function(x) <do stuff>)
. Есть ли какое-либо преимущество, что purrr::map
в плане производительности, обработки исключений и т. д.? В комментариях предполагают, что это не так, но, возможно, кто-то мог бы пояснить немного подробнее?
3 ответов
Если единственная функция, которую вы используете из purrr-это map()
, то нет,
преимущества не существенны. Как богатый очков Pauloo, основной
преимущество map()
- это помощники, которые позволяют писать компактный
код для общих особых случаев:
~ . + 1
эквивалентноfunction(x) x + 1
-
list("x", 1)
эквивалентноfunction(x) x[["x"]][[1]]
. Эти помощники немного более общие, чем[[
- см.?pluck
для сведения. Для сведения rectangling, в С каждого.dir("\.csv$") %>% set_names() %>% map(read.csv) %>% imap(~ transform(.x, filename = .y))
walk()
возвращает свой входной сигнал незримо; и полезно когда вы вызов функции для ее побочных эффектов (т. е. запись файлов в диск.)
не говоря уже о других помощниках, таких как safely()
и partial()
.
лично я считаю, что когда я использую purrr, я могу написать функциональный код с меньше трением и большей легкостью; он уменьшает зазор между размышляющий до идеи и ее реализации. Но ваш пробег может варьироваться; нет необходимости использовать purrr, если это действительно не поможет вам.
Microbenchmarks
да map()
немного медленнее, чем lapply()
. Но стоимость использования
map()
или lapply()
управляется тем, что вы отображаете, а не накладными расходами
выполнения цикла. Microbenchmark ниже предполагает, что стоимость
из map()
по сравнению с lapply()
около 40 НС в элемент, который
кажется маловероятным материальное воздействие большинство R код.
library(purrr)
n <- 1e4
x <- 1:n
f <- function(x) NULL
mb <- microbenchmark::microbenchmark(
lapply = lapply(x, f),
map = map(x, f)
)
summary(mb, unit = "ns")$median / n
#> [1] 490.343 546.880
этот онлайн учебник purrr подчеркивает удобство не нужно явно выписывать анонимные функции при использовании purrr, что вместе с функциями карты конкретного типа делает его очень функциональным.
1. purrr:: карта синтаксически намного удобнее, чем lapply
извлечь второй элемент списка
map(list, 2) # and it's done like magic
что, как отметил @F. Privé, то же самое as:
map(list, function(x) x[[2]])
С lapply
lapply(list, 2) # doesn't work
нам нужно передать ему анонимную функцию
lapply(list, function(x) x[[2]]) # now it works
или, как отметил @RichScriven, мы можем просто пройти [[
в качестве аргумента в lapply
lapply(list, `[[`, 2) # a bit more simple syntantically
в фоновом режиме purr принимает в качестве аргумента числовой или символьный вектор и использует его в качестве функции подмножества. Если вы делаете много и много подмножеств списки с использованием lapply и tire либо определения пользовательской функции, либо написания анонимной функции для подмножества, удобство является одной из причин перейти к мурлыканью.
2. Тип-специфические функции карты просто много строк кода
- map_chr()
- map_lgl()
- map_int()
- map_dbl()
- map_df () - мой любимый, возвращает фрейм данных.
каждая из этих специфичных для типа функций карты возвращает атомарный список, а не список, который map()
и lapply()
автоматически вернуться. Если вы имеете дело с вложенными списками, которые имеют атомарные векторы внутри, вы можете использовать эти функции карты для конкретного типа, чтобы вытащить векторы напрямую или принудить векторы в векторы int, dbl, chr. Еще один момент для удобства и функциональности.
3. Удобство в сторону, lapply быстрее, чем карта.
используя функции удобства муррр, по мере того как @F. Privé указал вне замедляет немного снизил обработку. Давайте наперегонки каждый из 4 случаев, которые я представил выше.
# devtools::install_github("jennybc/repurrrsive")
library(repurrrsive)
library(purrr)
library(microbenchmark)
library(ggplot2)
mbm <- microbenchmark(
lapply = lapply(got_chars[1:4], function(x) x[[2]]),
lapply_2 = lapply(got_chars[1:4], `[[`, 2),
map_shortcut = map(got_chars[1:4], 2),
map = map(got_chars[1:4], function(x) x[[2]]),
times = 100
)
autoplot(mbm)
и победитель....
lapply(list, `[[`, 2)
в сумме, если скорость-это то, что вы после: base:: lapply
если простой синтаксис-это ваш джем: purrr::карта
если мы не рассматриваем аспекты вкуса (в противном случае этот вопрос должен быть закрыт) или последовательность синтаксиса, стиль и т. д., Ответ Нет, нет особых причин использовать map
вместо lapply
или другие варианты семейства apply, такие как stricter vapply
.
PS: Для тех людей, которые безвозмездно downvoting, просто помните, что OP написал:
Я не спрашиваю здесь о том, нравится или не нравится синтаксис, другой функциональные возможности, предоставляемые purrr и т. д. но строго о сравнение purrr:: map с lapply, предполагающим использование стандарта оценка
если вы не считаете синтаксис или другие функциональные возможности purrr
, нет никаких особых причин использовать map
. Я использую purrr
Я и я в порядке с ответом Хэдли, но он по иронии судьбы идет по тем самым вещам, которые ОП заявил заранее, он не спрашивал.