발렌타인 데이 무렵 즈음 dplyr 패키지가 0.8.0 version 으로 업데이트 되면서 요인 별 집계(group by)의 편의성이 더 증대 되었다.
Tidyverse
에서 올라온 dplyr 0.8.0 버전 릴리즈에 대한 소개글에 신규 함수들에
대한 튜토리얼도 간략히 제시가 되어 있었는데
따라하다 보니 더더욱 편해진 느낌을 받았다.
Grouping 의 메이저한 변화는 아래의 신규함수 이었으며
실험적으로 도입 및 관리해보는 함수라고 라벨링되어 있었다.
group_nest()
group_split()
group_cols()
group_trim()
group_map()
group_walk()
이 중 다살펴보진 못했고 group_map()
의 활용예제를 생각해
보는 시간으로 글을 적어보았다.
group_nest()
,
group_split()
group_map()
활용예제를 적기 전에 이 두개 신규함수는
그래도 언급해보고 싶었다.
요인별 데이터를 묶어서 관리할 수 있는 group_nest()
요인별로 데이터를 나누어서 볼 수 있는 group_split()
의
새로운 함수가 요인별 현황을 간단히 보여주는데 쓰임이 좋을 거라
기대되었다.
# group_nest() 를 통해 요인별로 묶기
%>%
mtcars group_nest(cyl)
## # A tibble: 3 x 2
## cyl data
## <dbl> <list>
## 1 4 <tibble [11 x 10]>
## 2 6 <tibble [7 x 10]>
## 3 8 <tibble [14 x 10]>
# group_split() 를 통해 요인별 나누어서 데이터 보기
%>%
mtcars group_split(cyl)
## [[1]]
## # A tibble: 11 x 11
## mpg cyl disp hp drat wt qsec vs am gear carb
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 2 24.4 4 147. 62 3.69 3.19 20 1 0 4 2
## 3 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2
## 4 32.4 4 78.7 66 4.08 2.2 19.5 1 1 4 1
## 5 30.4 4 75.7 52 4.93 1.62 18.5 1 1 4 2
## 6 33.9 4 71.1 65 4.22 1.84 19.9 1 1 4 1
## 7 21.5 4 120. 97 3.7 2.46 20.0 1 0 3 1
## 8 27.3 4 79 66 4.08 1.94 18.9 1 1 4 1
## 9 26 4 120. 91 4.43 2.14 16.7 0 1 5 2
## 10 30.4 4 95.1 113 3.77 1.51 16.9 1 1 5 2
## 11 21.4 4 121 109 4.11 2.78 18.6 1 1 4 2
##
## [[2]]
## # A tibble: 7 x 11
## mpg cyl disp hp drat wt qsec vs am gear carb
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 21 6 160 110 3.9 2.62 16.5 0 1 4 4
## 2 21 6 160 110 3.9 2.88 17.0 0 1 4 4
## 3 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 4 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
## 5 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4
## 6 17.8 6 168. 123 3.92 3.44 18.9 1 0 4 4
## 7 19.7 6 145 175 3.62 2.77 15.5 0 1 5 6
##
## [[3]]
## # A tibble: 14 x 11
## mpg cyl disp hp drat wt qsec vs am gear carb
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
## 2 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
## 3 16.4 8 276. 180 3.07 4.07 17.4 0 0 3 3
## 4 17.3 8 276. 180 3.07 3.73 17.6 0 0 3 3
## 5 15.2 8 276. 180 3.07 3.78 18 0 0 3 3
## 6 10.4 8 472 205 2.93 5.25 18.0 0 0 3 4
## 7 10.4 8 460 215 3 5.42 17.8 0 0 3 4
## 8 14.7 8 440 230 3.23 5.34 17.4 0 0 3 4
## 9 15.5 8 318 150 2.76 3.52 16.9 0 0 3 2
## 10 15.2 8 304 150 3.15 3.44 17.3 0 0 3 2
## 11 13.3 8 350 245 3.73 3.84 15.4 0 0 3 4
## 12 19.2 8 400 175 3.08 3.84 17.0 0 0 3 2
## 13 15.8 8 351 264 4.22 3.17 14.5 0 1 5 4
## 14 15 8 301 335 3.54 3.57 14.6 0 1 5 8
반환되는 결과가 list 이어서 lapply()
와의 호환이
부드러워 질 수 있을 것 같아 개인적으로는 환영했다.
group_map()
dplyr 0.8.0 업데이트 변화의 가장 큰 내용이지 않을까 싶다.
요인별 세부적인 연산을 적용시킬 수 있는 group_map()
함수1가 특히 가장 눈에 띄었다.
purrr 패키지의 함수형 프로그래밍을 녹인 느낌이 강했는데, 개인적으로 이
함수를 잘 활용해 보고자 마음먹었다.
이전에는 요인별 복잡한 알고리즘 연산을 수행하려 할 때는
tidyr package 의 nest()
함수2를
통해 요인 별 데이터들을 묶은 후 list object 로 만든 다음
lapply()
함수단에서 복잡한 연산을 넘기는 방식의 그룹별
연산을 애용했었다.
예를 들면 이렇다. 아래 예제는 caret::train()
함수를
이용해 요인별로 mpg
필드값을 설명하는 Random Forest
알고리즘의 머신러닝 모델 학습을 독립적으로 수행 후 요인별 각기 다른
모델객체를 반환한 예제 코드이다.
<- mtcars %>%
models1 group_by(am) %>%
::nest() %>%
tidyrpull(data) %>% # 사실 여기까지가 group_split(mtcars, am) 과 동일함
lapply(function(x) caret::train(mpg ~ ., data = x, method = "rf"))
models1
## [[1]]
## Random Forest
##
## 13 samples
## 9 predictor
##
## No pre-processing
## Resampling: Bootstrapped (25 reps)
## Summary of sample sizes: 13, 13, 13, 13, 13, 13, ...
## Resampling results across tuning parameters:
##
## mtry RMSE Rsquared MAE
## 2 3.560203 0.7957677 3.121680
## 5 3.363679 0.8190277 2.869850
## 9 3.352473 0.8122625 2.830969
##
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was mtry = 9.
##
## [[2]]
## Random Forest
##
## 19 samples
## 9 predictor
##
## No pre-processing
## Resampling: Bootstrapped (25 reps)
## Summary of sample sizes: 19, 19, 19, 19, 19, 19, ...
## Resampling results across tuning parameters:
##
## mtry RMSE Rsquared MAE
## 2 2.105099 0.7708150 1.901827
## 5 2.159168 0.7585883 1.935482
## 9 2.218085 0.7358981 1.987309
##
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was mtry = 2.
lapply 를 쓴 만큼 결과 반환물도 list 형태인데
요인 am
이 2개 종류로써 2개 요인별 모델이 네이밍 되지
않은채로 나오는 단점이 있고
코드가 약간 번잡한 면이 있는 방법이다.(for 문도 생각해 보았지만 더
복잡해 질 것 같다)
group_map()
을 사용하면 이 과정을 조금 더 직관적이고
간단한 코딩으로 수행할 수 있었다.
<- mtcars %>%
models2 group_by(am) %>%
group_map( ~ tibble(train_object = list(caret::train(mpg ~ ., data = .x, method = "rf"))))
models2
## # A tibble: 2 x 2
## # Groups: am [2]
## am train_object
## <dbl> <list>
## 1 0 <S3: train>
## 2 1 <S3: train>
group_map()
함수 는 요인별로 바인딩을 위해서 dataframe
형태로 결과가 반환되어야 하는 전제조건이 있다.
이를 위해 list 결과를 tibble()
로 감싸주었다.
그리하여 2개의 모델을 요인별로 깔끔하게 보관된 모습을 보이고 있다.
am
요인이 1
일때의 학습모델을 사용하고자 할
경우 아래처럼 뽑아서 쓰면 되겠다.
%>%
models2 filter(am == 1) %>%
pull(train_object)
## [[1]]
## Random Forest
##
## 13 samples
## 9 predictor
##
## No pre-processing
## Resampling: Bootstrapped (25 reps)
## Summary of sample sizes: 13, 13, 13, 13, 13, 13, ...
## Resampling results across tuning parameters:
##
## mtry RMSE Rsquared MAE
## 2 3.870267 0.8004452 3.335863
## 5 3.680017 0.8163241 3.111651
## 9 3.663791 0.8082755 3.094120
##
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was mtry = 9.