Iteration with map
https://onesixx.com/13-iterating-with-purrr/
http://r4ds.had.co.nz/iteration.html#the-map-functions
https://www.rstudio.com/resources/videos/happy-r-users-purrr-tutorial/
https://github.com/rstudio/rstudio-conf/tree/master/2017/Happy_R_Users_Purrr-Charlotte_Wickham
http://purrr.tidyverse.org/reference/map.html
http://statkclee.github.io/parallel-r/ds-fp-purrr.html
http://r4ds.had.co.nz/iteration.html#the-map-functions
https://stackoverflow.com/questions/38403111/ways-to-add-multiple-columns-to-data-frame-using-plyr-dplyr-purrr
https://www.r-bloggers.com/rebuilding-map-example-with-apply-functions/
purrr에서 map()만 사용한다면 큰 의미가 없다.
https://jtr13.github.io/spring19/ss5593&fq2150.html
pacman::p_load("repurrrsive") # sw_films 각 영화 7개의 list, 각 영화는 14개의 list로 이루어짐. sw_films %>% lapply(function(x){x[["director"]]}) sw_films %>% map(~ .x[["director"]]) sw_films %>% map(~ .[["director"]])
purrr의 장점
exlist <- list(1,2,3) map(exlist, ~(.x+1)) # [[1]] # [1] 2 # [[2]] # [1] 3 # [[3]] # [1] 4
- 일관된 문법 , 일관된 return type
(sapply() & lapply()는 return이 상황에 따라 다르다.) - Pipe사용가능
- 함수의 인수(mean()함수의 trim이나 na.rm )을 바로 붙여 사용할수 있다.
dt <- data.table(a=rnorm(10), b=sample(100,10), c=rchisq(10,2), d=rbinom(10,100,0.5)) map_dbl(dt, mean, trim=0.5) # a b c d # 0.06995195 84.00000000 2.15738130 51.00000000
- map functions 은 name을 유지한다.
z <- list(x=1:3, y=4:5) map_int(z, length) # x y # 3 2 lapply(z, length) %>% as.integer() # [1] 3 2
- 간결한 shortcuts .
.
은 대명사 (for루프에서 i가 현재 index를 대신하듯이, . 은 현재 list요소를 대신한다)
function(x){ x+1 } 은~ .+1
즉 X %>% lapply(function(x) x+1) 은 X %>% map(~{.+1})
lapply(list, function(x) x[[2]]) map(list, function(x) x[[2]]) map(list, 2)
mtcars %>% split(.$cyl) %>% #map(function(df) lm(mpg ~ wt, data = df)) map(~ lm(mpg ~ wt, data=.)) %>% map(summary) %>% #extracting named components is a common operation models #map_dbl(~.$r.squared ) map_dbl( "r.squared")
- 가져올 요소의 위치(Position)을 Integer를 사용하여 대신할수 있다.
x <- list(list(1, 2, 3), list(4, 5, 6), list(7, 8, 9)) x %>% map_dbl(2) #> [1] 2 5 8
apply와 비교
sapply(iris[ ,-5], mean, na.rm=T) apply(iris[ ,-5], 2, mean, na.rm=T) iris[ ,-5] %>% apply(2,mean,na.rm=T) iris[ ,-5] %>% map_dbl(mean,na.rm=T)
반복문
1:nrow(iris) %>% map(function(rowNum){ dataRow <- iris[rowNum,] })\t
Map() 의 종류
map_returnType
map function | makes a | |
---|---|---|
map() | list | lapply(), sapply() |
map_lgl() | logical vector | |
map_int() | integer vector | |
map_dbl() | double vector | |
map_chr() | character vector |
각 map 함수는 vector를 input으로 받아서, 각 vector에 함수를 적용하고,
같은이름과 length의 새로운 vector를 output으로 return한다.
(map 함수의 접미사는 vector 유형을 결정한다.)
for루프의 속도는 많이 향상되었기 때문에, Map 함수는 속도보다는 코드의 간결/정확함을 위해 사용한다고 생각하면 된다.
map_if(), map_at()
numbers <- list(11, 12, 13, 14) is_even <- function(x){ !as.logical(x %% 2) } # number가 짝수이면, TRUE map_if(numbers, is_even, sqrt) # [[1]] # [1] 11 # [[2]] # [1] 3.464102 # [[3]] # [1] 13 # [[4]] # [1] 3.741657 map_at(numbers, c(1,3), sqrt) # [[1]] # [1] 3.316625 # [[2]] # [1] 12 # [[3]] # [1] 3.605551 # [[4]] # [1] 14
argument가 여러개일때 Mapping
map2(.x, .y, .f, ...)
set.seed(666) options(digits=2) mu <- list(5,10,-3) sigma <- list(1, 5,10) n <- list(1, 3, 5) # map(mu, rnorm, n=5) # map(seq_along(mu), ~rnorm(n=5, mean=mu[[.x]], sd=sigma[[.x]])) map2(mu, sigma, rnorm, n=5)
pmap(.l, .f, …)
arg1 <- list(n, mu, sigma) args2 <- list(mean = mu, sd = sigma, n = n) pmap(arg1, rnorm) pmap(arg2, rnorm)
example.
x <- list(1, 10, 100) y <- list(1, 2, 3) z <- list(5, 50, 500) l <- list(a=x, b=y, c=z) map2(x, y, `+`) map2(x, y, ~{.x +.y}) pmap(list(x, y, z), sum) # Matching arguments by position pmap(list(x, y, z), function(a, b, c){ a/(b+c)}) # Matching arguments by name pmap(l, function(a, b, c){ a/(b+c) })
# Split into pieces, fit model to each piece, then predict by_cyl <- mtcars %>% split(.$cyl) mods <- by_cyl %>% map(~ lm(mpg ~ wt, data = .)) map2(mods, by_cyl, predict) # Vectorizing a function over multiple arguments df <- data.frame( x = c("apple", "banana", "cherry"), pattern = c("p", "n", "h"), replacement = c("x", "f", "q"), stringsAsFactors = FALSE ) pmap(df, gsub) pmap_chr(df, gsub)
function이 여러개일때 Mapping
invoke_map() 함수
f <- c("runif", "rnorm", "rpois") param <- list( list(min = -1, max = 1), list(sd = 5), list(lambda = 10) ) invoke_map(f, param, n = 5) %>% str()
sim <- tribble( ~f, ~params, "runif", list(min = -1, max = 1), "rnorm", list(sd = 5), "rpois", list(lambda = 10) ) sim %>% mutate(sim = invoke_map(f, params, n = 10))
SOLVE ITERATION PROBLEMS
FOR EACH __DO __
You are already solving them:
copy & paste, for loops, (l/s)apply()
I'll show you an alternative purrr::map() & friends
library(purrr) library(tidyverse)
load("~/Dropbox/Rhome/purr/data/swapi.rda")
map( .x, .f, ...)
for each element of .x do .f
Other types of output : map_lgl( .x, .f, ...)
- map_lgl() logical vector
- map_int() integer vector
- map_dbl() double vector
- map_chr() character vecto
Other ways of specifying .f : map( .x, length, ...)
Other iteration functions : map2( .x, .y, .f, ...)
map() 항상 list를 return한다.
identical
map(.x, .f = some_function, ...)
map(.x, ~ some_function(.x, ...))
map(people, ~ .x[["hair_color"]])
map(people, .f = "hair_color")
map(people, "hair_color")
Dealing with failure
http://r4ds.had.co.nz/iteration.html#dealing-with-failure
여러 operation을 반복하기 위해 map 함수를 사용하다보면 그만큼 operation이 오류를 일으킬 확률이 높아진다.
하나때문에 모든 operation이 안돌아가고, 그게 어떤건지 찾는건 좀 짜증나는 일이다.
safely(.f, otherwise = NULL, quiet = TRUE)
quietly(.f)
possibly(.f, otherwise, quiet = TRUE)
safely()
뭔가 잘못되더라도, 함수가 실행되는 것을 멈추지 한는다.
Base R의 try와 비슷한 safely 는 함수를 감싸서 결과를 두가지로 알려준다.
-
result
is the original result. If there was an error, this will beNULL
. -
error
is an error object. If the operation was successful, this will beNULL
.
log(10) # 2.302585 safe_log <- safely(log)
- 정상적인 경우
str(safe_log(10)) c(10) %>% map(safely(log)) %>% str()
List of 2 $ result: num 2.3 $ error : NULL
- 에러상황 : 문자에 log를 취하려고 함
str(safe_log("a")) c("a") %>% map(safely(log)) %>% str()
List of 2 $ result: NULL $ error :List of 2 ..$ message: chr "non-numeric argument to mathematical function" ..$ call : language .f(...) ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
ex2> https://www.r-bloggers.com/lesser-known-purrr-tricks/
safely()
는 아래 possibly()
와 비슷하지만 list의 list를 return한다. (result리스트와 error 메세지)
numbers_with_error <- list(1, 2, 3, "spam", 4)
safe_sqrt <- safely(sqrt, otherwise = NA_real_) map(numbers_with_error, safe_sqrt)
possible_sqrt <- possibly(sqrt, otherwise = NA_real_) map(numbers_with_error, possible_sqrt)
possibly()
에러상황: 정의된 값으로 대체한다.
x <- list(1, 10, "a") x %>% map_dbl(possibly(log, NA_real_))
[1] 0.00 2.30 NA
quietly()
safely()와 비슷하지만, 에러를 잡는대신 printed output, messages, warnings을 잡는다.
x <- list(1, -1) x %>% map(quietly(log)) %>% str()