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


171

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

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


8
실제로 간단한 사용 사례의 경우 기본 R을 사용하는 것이 좋으며 종속성을 피하십시오. 이미로드 한 경우 tidyverse파이프 %>%및 익명 함수 ~ .x + 1구문을 활용할 수 있습니다.
Aurèle

49
이것은 스타일의 문제입니다. 이 깔끔한 것들이 그 위에 껍질이기 때문에 기본 R 함수가 무엇을 해야하는지 알아야합니다. 어느 시점에서 그 껍질이 깨질 것입니다.
Hong Ooi

9
~{}바로 가기 람다 ( {}물개 가 있거나없는 상태에서 나에게 평범한 거래를한다 purrr::map().)의 타입-강화는 . purrr::map_…()보다 편리하고 덜 난해하다 vapply(). purrr::map_df()매우 비싼 기능이지만 코드를 단순화한다. [lsv]apply()하지만 기본 R을 고수하는 데 아무런 문제가 없다. .
hrbrmstr

4
질문을 해 주셔서 감사합니다. 내가 본 것들도 있습니다. 나는 10 년 이상 R을 사용하고 있으며 확실히 purrr물건을 사용하지 않을 것입니다. 내 요점은 다음과 같습니다 tidyverse. 프로그래밍이 아닌 분석 / 대화 형 / 보고서에 훌륭합니다. 당신이 사용하고 lapply있거나 map프로그래밍 중이 라면 언젠가는 패키지를 만들 수 있습니다. 그런 다음 적은 종속성이 가장 좋습니다. 플러스 : 언젠가 사람들 map이 상당히 모호한 구문을 사용 하는 것을 보았습니다 . 그리고 이제 성능 테스트를 볼 apply수 있습니다. 가족에 익숙하다면 : 충실히 따르십시오.
Eric Lecoutre

4
Tim은 다음과 같이 썼습니다. "구문, purrr에서 제공하는 기타 기능에 대한 선호 또는 싫어하는 점에 대해 묻지 않고 purrr :: map과 표준 평가를 사용한다고 가정 할 때 lapply와의 비교에 대해서는 엄격히 묻습니다." 당신이 말한 것과 정확히 일치하는 것은 사람들이 가고 싶지 않다는 것입니다.
카를로스시 넬리 17

답변:


232

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

2
이 예제에서 transform ()을 사용 했습니까? 기본 R transform ()에서와 같이, 아니면 뭔가 빠졌습니까? transform ()은 파일 이름을 인자로 제공하여 행을 자연스럽게 묶고 싶을 때 경고를 생성합니다. mutate ()은 원하는 파일 이름의 문자 열을 제공합니다. 그것을 사용하지 않는 이유가 있습니까?
doctorG

2
예, 사용하는 것이 좋습니다 mutate(). 다른 dep이없는 간단한 예제를 원했습니다.
hadley

이 답변 어딘가에 유형별 성이 표시되어서는 안됩니까? 많은 스크립트에서 map_*로딩하게 purrr되었습니다. 내 코드의 일부 '제어 흐름'측면에 도움이되었습니다 ( stopifnot(is.data.frame(x))).
Fr.

2
ggplot과 data.table은 훌륭하지만 R의 모든 단일 함수에 대해 새로운 패키지가 실제로 필요합니까?
adn bps

58

편리함속도 를 비교 purrr하고 lapply정리합니다 .


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

목록의 두 번째 요소 추출

map(list, 2)  

어느 @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

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

따라서을 사용하여 많은 목록에 함수를 적용 lapply하고 사용자 정의 함수를 정의하거나 익명 함수를 작성하는 데 어려움을 겪는다면 편의가 선호되는 이유 중 하나 purrr입니다.

2. 유형별 맵은 단순히 여러 줄의 코드를 기능합니다.

  • map_chr()
  • map_lgl()
  • map_int()
  • map_dbl()
  • map_df()

이러한 유형별지도 함수 각각은 map()및 로 반환되는 목록이 아닌 벡터를 반환합니다 lapply(). 중첩 된 벡터 목록을 처리하는 경우 이러한 유형별 맵 함수를 사용하여 벡터를 직접 꺼내고 벡터를 int, dbl, chr 벡터로 직접 강제 변환 할 수 있습니다. 기본 R 버전은 다음과 같습니다 as.numeric(sapply(...)).as.character(sapply(...))

그만큼 map_<type>기능들은 또한 지정된 유형의 원자 벡터를 반환 할 수없는 경우, 그들이 실패하는 유용한 품질을 가지고. 이는 엄격한 제어 흐름을 정의 할 때 유용합니다. 여기서 어떤 방식 으로든 잘못된 개체 유형을 생성하는 경우 함수가 실패 할 수 있습니다.

3. 편의는 제쳐두고 lapply[약간]보다 빠릅니다.map

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)

여기에 이미지 설명을 입력하십시오

그리고 승자는....

lapply(list, `[[`, 2)

요컨대, 원시 속도가 당신이 따르는 것이라면 : base::lapply(그다지 빠르지는 않지만)

간단한 구문과 표현력 : purrr::map


이 훌륭한 purrr튜토리얼 은를 사용할 때 익명 함수를 명시 적으로 작성할 필요가없는 편의성purrr 과 유형별 map함수 의 이점을 강조합니다 .


2
function(x) x[[2]]just 대신에 사용하면 2속도가 느려집니다. 이 여분의 시간은 모두 확인 lapply하지 않은 것입니다.
F. Privé

17
익명의 기능이 "필요"하지 않습니다. [[함수입니다. 할 수 있습니다 lapply(list, "[[", 3).
Rich Scriven

말이되는 @RichScriven 이는 퍼를 통해 lapply를 사용하는 구문을 단순화합니다.
Rich Pauloo

37

취향 (그렇지 않으면이 질문을 닫아야합니다) 또는 구문 일관성, 스타일 등의 측면을 고려하지 않으면 대답은 '아니오'입니다. map대신 lapply엄격함과 같은 적용 제품군의 다른 변형 또는 다른 변형 을 사용할 특별한 이유가 없습니다 vapply.

추신 : 사람들이 무의식적으로 하향 조정하는 사람들에게 OP가 쓴 것을 기억하십시오.

나는 구문, purrr 등이 제공하는 다른 기능에 대한 좋아하는 것과 싫어하는 것에 대해 여기에서 묻지 않고 purrr :: map과 lapply의 표준 평가를 사용한다고 가정합니다.

의 구문이나 다른 기능을 고려하지 않으면 purrr사용할 특별한 이유가 없습니다 map. 나는 purrr나 자신을 사용 하고 Hadley의 대답은 괜찮지 만 OP가 직접 요구하지 않은 것들을 아이러니하게 전달합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.