Functional programming with purrr

Tidyverse purrr Functional programming

purrr 패키지로 함수형 프로그램하는 방법을 숙지합니다.

유충현 https://choonghyunryu.github.io (한국알사용자회)
2022-02-26

들어가기

반복문? 이제 for문을 잊어주세요.
lapply, sapply? 당신도 비켜주실래요?
purrr 패키지가 반복문을 깔끔하게 함수형 프로그램으로 만들어 줍니다.


미리보기

for loop, apply 계열 함수, purrr 비교

for loop, apply 계열 함수, purrr를 비교 다음과 같은 데이터 프레임과, min-max 표준화를 수행하는 함수를 만들었습니다.

distribution <- tibble::tibble(
  uniform = runif(10),
  normal = rnorm(10),
  student_t = rt(10, df = 3)
)

distribution
# A tibble: 10 × 3
   uniform  normal student_t
     <dbl>   <dbl>     <dbl>
 1  0.483   0.389     -0.586
 2  0.966  -0.367     -0.234
 3  0.833  -0.572     -0.534
 4  0.817   0.409     -0.784
 5  0.831  -0.451      0.756
 6  0.0795 -0.276     -0.471
 7  0.218   2.77       0.104
 8  0.718  -0.0110     0.988
 9  0.825  -0.761     -4.56 
10  0.321   0.749     -0.257
minmax <- function(x) {
  (x - min(x)) / diff(range(x))
}


이제 distribution의 변수인 일량난수, 정규난수, t난수를 min-max 표준화하려 합니다.


for loop

다음은 변수를 minmax로 표준화하는 for loop 구문입니다.

distribution_minmax <- distribution

for (nm in names(distribution)) {
  distribution_minmax[[nm]] <- minmax(distribution[[nm]])
}

distribution_minmax
# A tibble: 10 × 3
   uniform normal student_t
     <dbl>  <dbl>     <dbl>
 1   0.455 0.325      0.716
 2   1     0.112      0.780
 3   0.850 0.0536     0.726
 4   0.832 0.331      0.681
 5   0.847 0.0879     0.958
 6   0     0.137      0.737
 7   0.156 1          0.841
 8   0.720 0.212      1    
 9   0.840 0          0    
10   0.273 0.427      0.776

apply 계열 함수

다음은 변수를 minmax로 표준화하는 sapply 구문입니다.

distribution_minmax <- sapply(distribution, minmax)

tibble::as_tibble(distribution_minmax)
# A tibble: 10 × 3
   uniform normal student_t
     <dbl>  <dbl>     <dbl>
 1   0.455 0.325      0.716
 2   1     0.112      0.780
 3   0.850 0.0536     0.726
 4   0.832 0.331      0.681
 5   0.847 0.0879     0.958
 6   0     0.137      0.737
 7   0.156 1          0.841
 8   0.720 0.212      1    
 9   0.840 0          0    
10   0.273 0.427      0.776

purrr

다음은 변수를 minmax로 표준화하는 purrr 패키지 구문입니다.

purrr::map_df(distribution, minmax)
# A tibble: 10 × 3
   uniform normal student_t
     <dbl>  <dbl>     <dbl>
 1   0.455 0.325      0.716
 2   1     0.112      0.780
 3   0.850 0.0536     0.726
 4   0.832 0.331      0.681
 5   0.847 0.0879     0.958
 6   0     0.137      0.737
 7   0.156 1          0.841
 8   0.720 0.212      1    
 9   0.840 0          0    
10   0.273 0.427      0.776

map 계열 함수

purrr 패키지에서 대표적인 map 계열 함수는 map() 함수와 다음의 함수가 있습니다.

map 계열 함수는 함수형 프로그램에서 인수를 하나만 사용하고 계산된 결과는 “map_” 뒤에 표현되는 데이터 타입으로 반환하는 함수다.

library(purrr)

ls(pos = "package:purrr", pattern = "^map2_") %>% 
  stringr::str_remove("2")
[1] "map_chr" "map_dbl" "map_df"  "map_dfc" "map_dfr" "map_int"
[7] "map_lgl" "map_raw"


함수이름 반환 데이터 유형 반환 데이터 유형 비고
map list 리스트
map_chr character 문자형
map_dbl double 실수형
map_df data.frame 데이터프레임
map_dfc data.frame 데이터프레임 원소를 열로 붙여서 데이터프레임 생성
map_dfr data.frame 데이터프레임 원소를 행으로 붙여서 데이터프레임 생성
map_int integer 정수형
map_lgl logical 논리형
map_raw raw 문자형

map 계열 함수 예제

Hands-on 1

  1. 앞에서 만든 distribution의 다음과 같은 통계량을 구하세요.
    1. 산술평균과 표준편차를 계산하세요.
  2. 다음의 함수를 사용해서 최소값과 최대값을 구하여 결과를 비교하세요.
    1. map()
    2. map_df()


힌트

    • mean(), sd() 함수를 사용합니다.
    • 최소값과 최대값은 range() 함수로 쉽게 구할 수 있습니다.

purrr 솔루션

기존에 정의된 함수를 사용합니다.

# 1.
distribution %>% 
  map_dbl(mean)
   uniform     normal  student_t 
 0.6091583  0.1882322 -0.5579321 
distribution %>% 
  map_dbl(sd)
  uniform    normal student_t 
0.3092581 1.0294314 1.5221225 
# 2.
distribution %>% 
  map(range)
$uniform
[1] 0.0795344 0.9661807

$normal
[1] -0.7614494  2.7733921

$student_t
[1] -4.562987  0.987915
distribution %>% 
  map_df(range)
# A tibble: 2 × 3
  uniform normal student_t
    <dbl>  <dbl>     <dbl>
1  0.0795 -0.761    -4.56 
2  0.966   2.77      0.988


Hands-on 2

  1. month.name는 12개 월의 영문명을 나타내는 내장 상수입니다.
    1. month.name을 조회해 보세요.
    2. 12개 월에 대해서, “문자의 개수 * 순서”(예, 8월은 8, 12월은 12)를 계산하세요.


힌트

    • 문자의 개수를 세는 함수는 nchar() 입니다.
    • 문자열 매치를 수행하여 매치되는 위치는 반환하는 match() 함수를 사용해 보세요.
    • purrr 패키지map_int() 함수가 결과를 정수로 반환합니다.

purrr 솔루션

함수형 프로그램은 기존의 함수를 사용하거나, 사용자가 함수를 정의해야 합니다.

month.name
 [1] "January"   "February"  "March"     "April"     "May"      
 [6] "June"      "July"      "August"    "September" "October"  
[11] "November"  "December" 
month.name %>% 
  purrr::map_int(
    function(x) {
      nchar(x) * match(x, month.name)
    }
  )
 [1]  7 16 15 20 15 24 28 48 81 70 88 96

map 이런게 가능한가요?

https://purrr.tidyverse.org/ 페이지의 purrr 소개에는 선형모형을 적합한 후, R2를 계산하는 예제가 있습니다.

다음을 간단하게 purrr 패키지로 처리합니다.

  1. mtcars 데이터에서 차량의 무게와 연비의 관계를 살펴보기 위한 mpg ~ wt 모형을,
  2. 차량의 실린더 개수의 조합별로 수행합니다.
  3. 수행한 모형에서 R2만 발췌합니다.

현 수행 결과를 다음 단계의 입력 값으로 사용하는 파이프 기능(%>% 연산자)이 활약하는 좋은 사례입니다.

library(purrr)

mtcars %>%
  split(.$cyl) %>% # from base R
  map(~ lm(mpg ~ wt, data = .)) %>%
  map(summary) %>%
  map_dbl("r.squared")
        4         6         8 
0.5086326 0.4645102 0.4229655 

map2 계열 함수

purrr 패키지에서 대표적인 map2 계열 함수는 map2() 함수와 다음의 함수가 있습니다.

library(purrr)

ls(pos = "package:purrr", pattern = "^map2_")
[1] "map2_chr" "map2_dbl" "map2_df"  "map2_dfc" "map2_dfr" "map2_int"
[7] "map2_lgl" "map2_raw"


함수이름 반환 데이터 유형 반환 데이터 유형 비고
map2 list 리스트
map2_chr character 문자형
map2_dbl double 실수형
map2_df data.frame 데이터프레임
map2_dfc data.frame 데이터프레임 원소를 열로 붙여서 데이터프레임 생성
map2_dfr data.frame 데이터프레임 원소를 행으로 붙여서 데이터프레임 생성
map2_int integer 정수형
map2_lgl logical 논리형
map2_raw raw 문자형

map2 계열 함수 예제

Hands-on 3

  1. 정규 난수 5개 쌍을 다음 파라미터 기준으로 생성하고 싶습니다.
    1. 평균 : (5, 10, -3)
    2. 표준편차 : (1, 5, 10)
    즉, X ~ N(5, 1), X ~ N(10, 5), X ~ N(-3, 10)을 따르는 분포에서의 난수를 각각 5개씩 추출합니다.


힌트

rnorm() 함수로 정규 난수를 생성합니다.

purrr 솔루션

다음 다이어그램을 참고하세요.

정규난수 산출 개념도
mu <- c(5, 10, -3)
sigma <- c(1, 5, 10)

map2(mu, sigma, rnorm, n = 5)
[[1]]
[1] 5.819224 4.572258 4.431857 5.008439 5.037252

[[2]]
[1]  6.927533  9.880156  4.715674  9.070893 13.899599

[[3]]
[1] -10.623390  -6.438565  -6.869899 -20.728252  -4.245847

Citation

For attribution, please cite this work as

유충현 (2022, Feb. 26). Dataholic: Functional programming with purrr. Retrieved from https://choonghyunryu.github.io/2022-02-26-purrr

BibTeX citation

@misc{유충현2022functional,
  author = {유충현, },
  title = {Dataholic: Functional programming with purrr},
  url = {https://choonghyunryu.github.io/2022-02-26-purrr},
  year = {2022}
}