Зачем использовать 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 в плане производительности, обработки исключений и т. д.? Комментарии ниже предполагают, что это не так, но, может быть, кто-то может разработать немного больше?

707   3  

3 ответов:

Если единственная функция, которую вы используете из purrr-это map(), то нет, преимущества не являются существенными. Как отмечает Рич Паулу, главное преимущество map() - это помощники, которые позволяют писать компактный код для общих особых случаев:

  • ~ . + 1 эквивалентно function(x) x + 1

  • list("x", 1) эквивалентно function(x) x[["x"]][[1]]. Эти помощники немного более общие, чем [[ - см. ?pluck для сведения. Для сведения прямоугольник, С каждого.

    dir("\.csv$") %>%
      set_names() %>%
      map(read.csv) %>%
      imap(~ transform(.x, filename = .y))
    
  • walk() возвращает его вход невидимо; и полезно, когда вы вызов функции для ее побочных эффектов (т. е. запись файлов в диск.)

не говоря уже о других помощниках, таких как safely() и partial().

лично я считаю, что когда я использую purrr, я могу написать функциональный код с меньшим трением и большей легкостью; он уменьшает зазор между размышляющий идею и ее реализацию. Но ваш пробег может варьироваться; там нет необходимости использовать муррр, если это на самом деле не поможет вам.

Microbenchmarks

да map() немного медленнее, чем lapply(). Но стоимость использования map() или lapply() управляется тем, что вы отображаете, а не накладные расходы выполнения цикла. В микротестов производительности ниже предполагают, что стоимость из map() по сравнению с lapply() составляет около 40 НС на элемент, который кажется маловероятным, чтобы существенно повлиять большинство кода Р.

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é, то же самое как:

map(list, function(x) x[[2]])

С lapply

lapply(list, 2) # doesn't work

нам нужно передать ему анонимную функцию

lapply(list, function(x) x[[2]])  # now it works

или, как указал @RichScriven, мы можем просто пройти [[ как аргумент в лапли

lapply(list, `[[`, 2)  # a bit more simple syntantically

в фоновом режиме purr принимает в качестве аргумента числовой или символьный вектор и использует его в качестве функции подмножества. Если вы делаете много и много подмножеств списки с использованием lapply и tire либо определяют пользовательскую функцию, либо пишут анонимную функцию для подмножества, удобство является одной из причин перехода к purrr.

2. Тип-специфические функции карты просто много строк кода

  • map_chr ()
  • map_lgl ()
  • map_int ()
  • map_dbl ()
  • map_df () - мой любимый, возвращает фрейм данных.

каждая из этих тип-специфических функций карты возвращает атомарный список, а не список, который map() и lapply() автоматически вернуться. Если вы имеете дело с вложенными списками, которые имеют атомарные векторы внутри, вы можете использовать эти специфические для типа функции карты, чтобы вытащить векторы напрямую или принудить векторы к векторам int, dbl, chr. Еще один момент для удобства и функциональности.

3. Удобство в сторону, lapply быстрее, чем карта.

используя функции удобства purrr, как указал @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)

enter image description here

и победитель....

lapply(list, `[[`, 2)

в общем, если скорость-это то, что вам нужно:base:: lapply

если простой синтаксис-это ваш джем: purrr::карта

если мы не рассматриваем аспекты вкуса (в противном случае этот вопрос должен быть закрыт) или последовательность синтаксиса, стиль и т. д., Ответ Нет, нет особой причины использовать map вместо lapply или другие варианты семейства apply, такие как stricter vapply.

PS: Для тех людей, которые безвозмездно downvoting, просто помните, что OP написал:

Я не спрашиваю здесь о своих симпатиях или антипатиях о синтаксисе, другой функциональные возможности, предоставляемые purrr и т. д. но строго о сравнение purrr:: map с lapply предполагая использование стандарта оценка

если вы не рассматриваете синтаксис или другие функциональные возможности purrr, нет никакой особой причины использовать map. Я использую purrr Я и я в порядке с ответом Хэдли, но он иронично проходит через те самые вещи, которые ОП заявил заранее, что он не спрашивал.

Comments

    Ничего не найдено.