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.