Programing

lapply 대신 purrr :: map을 사용하는 이유는 무엇입니까?

lottogame 2020. 6. 17. 20:24
반응형

lapply 대신 purrr :: map을 사용하는 이유는 무엇입니까?


내가 사용해야하는 이유가 있습니까?

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성능, 예외 처리 등의 측면에서 이점이 있습니까? 아래의 의견은 그렇지 않다고 제안하지만 누군가가 조금 더 자세히 설명 할 수 있습니까?


purrr에서 사용하는 유일한 기능이 map()아니오 인 경우에는 이점이 크지 않습니다. Rich Pauloo가 지적했듯이 주요 장점은 map()일반적인 특수한 경우에 대한 간단한 코드를 작성할 수있는 헬퍼입니다.

  • ~ . + 1 에 해당 function(x) x + 1

  • list("x", 1)와 같습니다 function(x) x[["x"]][[1]]. 이 도우미는 좀 더 일반적 입니다. 자세한 내용 [[은 참조 ?pluck하십시오. 들어 데이터 rectangling.default인수는 특히 유용합니다.

그러나 대부분 단일 *apply()/ map()기능을 사용하지 않고 많은 기능을 사용하고 있으며 purrr의 장점은 기능 간의 일관성이 훨씬 뛰어납니다. 예를 들면 다음과 같습니다.

  • 첫 번째 주장 lapply()은 데이터이다. 첫 번째 인수 mapply()는 함수입니다. 모든 맵 함수에 대한 첫 번째 인수는 항상 데이터입니다.

  • vapply(), sapply()그리고 mapply()당신은 출력에 억제 이름을 선택할 수 있습니다 USE.NAMES = FALSE; 그러나 lapply()그 주장은 없습니다.

  • 매퍼 함수에 일관된 인수를 전달하는 일관된 방법은 없습니다. 대부분의 기능을 사용할 수 ...있지만 mapply()사용 MoreArgs(당신이 기대를 호출 할 MORE.ARGS), 그리고 Map(), Filter()그리고 Reduce()새로운 익명 함수를 만들 것으로 기대합니다. 맵 함수에서 상수 인수는 항상 함수 이름 뒤에옵니다.

  • 거의 모든 purrr 함수는 유형이 안정적입니다. 함수 이름에서만 출력 유형을 예측할 수 있습니다. sapply()또는에 대해서는 사실이 아닙니다 mapply(). 그렇습니다 vapply(). 하지만에 해당하는 것은 없습니다 mapply().

당신은 이러한 작은 차이가 중요하지 않다고 생각할 수도 있습니다 (일부 사람들은 기본 R 정규 표현식보다 더 엄격한 이점이 없다고 생각하지만) 내 경험상 프로그래밍 할 때 불필요한 마찰을 일으 킵니다 (서로 다른 인수 순서는 항상 여행에 사용되었습니다) 큰 아이디어뿐만 아니라 많은 부수적 인 세부 사항도 배워야하기 때문에 함수형 프로그래밍 기술을 배우기가 더 어려워집니다.

Purrr은 또한 기본 R에없는 몇 가지 편리한 맵 변형을 채 웁니다.

  • modify()[[<-"제자리에서"수정 하는 데 사용하는 데이터 유형을 유지합니다 . _if변형 과 함께 이것은 (IMO beautiful) 코드를 허용합니다.modify_if(df, is.factor, as.character)

  • map2()x와에 동시에 매핑 할 수 있습니다 y. 이를 통해 다음과 같은 아이디어를보다 쉽게 ​​표현할 수 있습니다.map2(models, datasets, predict)

  • imap()를 통해 동시에 x그 색인 (이름 또는 위치) 을 매핑 할 수 있습니다 . 이를 csv통해 디렉토리에있는 모든 파일을 쉽게로드 할 수 있으며 filename파일에 열을 추가 할 수 있습니다.

    dir("\\.csv$") %>%
      set_names() %>%
      map(read.csv) %>%
      imap(~ transform(.x, filename = .y))
    
  • walk()입력을 보이지 않게 리턴합니다. 부작용에 대한 함수를 호출 할 때 유용합니다 (예 : 디스크에 파일 쓰기).

같은 다른 도우미를 언급하지 않는 safely()partial().

개인적으로 purrr을 사용할 때 마찰이 적고 쉽게 기능 코드를 작성할 수 있습니다. 아이디어를 생각하고 구현하는 것 사이의 간격을 줄입니다. 그러나 마일리지는 다를 수 있습니다. 실제로 도움이되지 않는 한 purrr을 사용할 필요가 없습니다.

마이크로 벤치 마크

예, map()보다 약간 느립니다 lapply(). 그러나 사용하는 비용은 map()lapply()에 의해 구동 무엇있는 거 매핑, 루프를 수행하지 오버 헤드. 아래의 마이크로 벤치 마크는 map()비교 비용이 lapply()요소 당 약 40 ns이며 대부분의 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하고 lapply정리합니다 .


1. purrr::maplapply보다 문법적으로 더 편리합니다

extract second element of the list

map(list, 2)  

which as @F. Privé pointed out, is the same as:

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

with lapply

lapply(list, 2) # doesn't work

we need to pass an anonymous function...

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

...or as @RichScriven pointed out, we pass [[ as an argument into lapply

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

So if find yourself applying functions to many lists using lapply, and tire of either defining a custom function or writing an anonymous function, convenience is one reason to move to favor purrr.

2. Type-specific map functions simply many lines of code

  • map_chr()
  • map_lgl()
  • map_int()
  • map_dbl()
  • map_df() - my favorite, returns a data frame.

Each of these type-specific map functions returns an atomic list (vector), rather than the lists returned by map() and lapply(). If you're dealing with nested lists of atomic vectors within, you can use these type-specific map functions to pull out the vectors directly, and coerce vectors directly into int, dbl, chr vectors. The base R version would look something like as.numeric(sapply(...)), as.character(sapply(...)), etc. This gives purrr another point for convenience and functionality.

3. Convenience aside, lapply is [slightly] faster than map

Using purrr's convenience functions, as @F. Privé pointed out slows down processing a bit. Let's race each of the 4 cases I presented above.

# 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

And the winner is....

lapply(list, `[[`, 2)

In sum, if raw speed is what you're after: base::lapply (although it's not that much faster)

For simple syntax and expressibility: purrr::map


This excellent purrr tutorial highlights the convenience of not having to explicitly write out anonymous functions when using purrr, and the benefits of type-specific map functions.


If we do not consider aspects of taste (otherwise this question should be closed) or syntax consistency, style etc, the answer is no, there’s no special reason to use map instead of lapply or other variants of the apply family, such as the stricter vapply.

PS: To those people gratuitously downvoting, just remember the OP wrote:

I am not asking here about one's likes or dislikes about the syntax, other functionalities provided by purrr etc., but strictly about comparison of purrr::map with lapply assuming using the standard evaluation

If you do not consider syntax nor other functionalities of purrr, there's no special reason to use map. I use purrr myself and I'm fine with Hadley's answer, but it ironically goes over the very things the OP stated upfront he was not asking.

참고URL : https://stackoverflow.com/questions/45101045/why-use-purrrmap-instead-of-lapply

반응형