Program :: Iteration with map

Published onesixx on

 

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/

 

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 함수는 input으로 vector를 받아서, 각 vector에 함수를 적용하고, 같은이름과 length의 새로운 vector를 output으로 return한다. map 함수의 접미사는 vector 유형을 결정한다. 
for루프의 속도는 많이 향상되었기 때문에, Map 함수는 속도보다는 코드의 간결/정확함을 위해 사용한다고 생각하면 된다. 

df <- tibble(a=rnorm(10), b=sample(100,10), c=rchisq(10,2), d=rbinom(10,100,0.5))

purrr의 장점

  • 일관된 문법 , 일관된 return type,  (sapply() & lapply()는 return이 상황에 따라 다르다.)
  • Pipe사용가능
  • 함수의 인수( mean()함수의  trim이나 na.rm )을 바로 붙여 사용할수 있다. 

    map_dbl(df, mean, trim = 0.5)
    
    df %>% map_dbl(mean, trim=0.5)
    
    #>      a      b      c      d 
    #>  0.237 -0.218  0.254 -0.133
  • map functions 은  name을 유지한다.

    z <- list(x = 1:3, y = 4:5)
    map_int(z, length)
    #> x y 
    #> 3 2
  • 간결한 shortcuts 
     . 은 대명사  (for루프에서 i가 현재 index를 대신하듯이,  . 은 현재 list요소를 대신한다)

    models <- mtcars %>% split(.$cyl) %>% 
      #map(function(df) lm(mpg ~ wt, data = df))
       map(            ~lm(mpg ~ wt, data=.))

    널리 정의된 컴포넌트를 가져올때는 바로 이름을 사용할수 있다. 

    #extracting named components is a common operation
    
    models %>% 
      map(summary) %>% 
        #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)

 

Map() …

numbers <- list(11, 12, 13, 14)

# number가 짝수이면, TRUE
is_even <- function(x){ !as.logical(x %% 2) }

map_if()

map_if(numbers, is_even, sqrt)
[[1]]
[1] 11

[[2]]
[1] 3.464102

[[3]]
[1] 13

[[4]]
[1] 3.741657

map_at()

map_at(numbers, c(1,3), sqrt)
[[1]]
[1] 3.316625

[[2]]
[1] 12

[[3]]
[1] 3.605551

[[4]]
[1] 14

 

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 be NULL.

  • error is an error object.          If the operation was successful, this will be NULL.

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()

http://r4ds.had.co.nz/iteration.html#dealing-with-failure

map2() 와 pmap() 함수 :: argument가 여러개일때 Mapping

numbers <- list(11, 12, 13, 14)

# number가 짝수이면, TRUE
is_even <- function(x){ !as.logical(x %% 2) }

Map2

numbers2 <- list(1, 2, 3, 4)

map2(numbers, numbers2, `+`)
[[1]]
[1] 12

[[2]]
[1] 14

[[3]]
[1] 16

[[4]]
[1] 18

 cf.

> `+`(1,3)
[1] 4

ex>

set.seed(666); options(digits=2)

mu    <- list(5, 10, -3)
sigma <- list(1,  5, 10)

 

mu %>% map(rnorm, n = 5) %>% str()

seq_along(mu) %>% map(~rnorm(5, mu[[.]], sigma[[.]])) %>% str()
map2(mu, sigma, rnorm, n = 5) %>% str()

pmap => map3(), map4(),map5()………

n <- list(1, 3, 5)

args1 <- list(n, mu, sigma) 
args1 %>% pmap(rnorm) %>% str()

args2 <- list(mean = mu, sd = sigma, n = n)
args2 %>% pmap(rnorm) %>% str()

params <- tribble( 
    ~mean, ~sd, ~n, 
        5,   1,  1, 
       10,   5,  3,
       -3,  10,  5)

params %>% pmap(rnorm)

 

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”)

 

 

 

 

Categories: Tidyverse

onesixx

Blog Owner

Leave a Reply

Your email address will not be published.