이번시간은 randomforest를 사용하여 사이영상 수상을 예측해보도록 하겠습니다. 그전에 사이영상은 간단히 말하여
사이 영 상(Cy Young Award)은 메이저 리그 베이스볼에서 매년 각 리그의 최고 투수에게 주어지는 상이다.
ko.wikipedia.org/wiki/%EC%82%AC%EC%9D%B4_%EC%98%81_%EC%83%81
data의 형태를 살펴보면 다음과 같습니다.
1.시즌(season): 명목형
2.이름(name): 명목형
3.팀(team):명목형
4.리그(lg): 명목형
5.사이영상 수상 여부(cy, O X로 표시): 범주형
6.사이영상 투표 득점(vote): 연속형
7.승리 순위(w): 순서형
8.패배 순위(l): 순서형
9.팬그래프스 대체 선수 대비 승리 기여도 순위(war): 순서형
10.평균자책점 순위(era) : 순서형
11.수비영향을 제거한 평균자책점 순위(fip): 순서형
12.투구 이닝 순위(ip): 순서형
13.삼진 순위(k): 순서형
14.볼넷 순위(bb): 순서형
15.피홈런 순위(hr): 순서형
16.9이닝당 탈삼진(K/9) 순위(k9): 순서형
17.9이닝당 볼넷(BB/9) 순위 (bb9): 순서형
18.삼진 대 볼넷 비율(K/BB) 순위(kbb): 순서형
19.인플레이타율(BABIP) 순위(babip) : 순서형
library(tidyverse)
library(tidymodels)
cya <- read_csv("C:/Users/pc-14/Downloads/cya.csv")
> head(cya)
# A tibble: 6 x 19
season name team lg cy vote w l war era fip ip k bb hr k9 bb9 kbb babip
<dbl> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2019 Gerrit Cole Astros AL X 0 2 2 1 1 1 3 1 6 13 1 4 2 4
2 2019 Lance Lynn Rangers AL X 0 4 15 2 7 3 4 4 16 5 7 11 6 21
3 2019 Justin Verlander Astros AL X 0 1 3 3 2 4 1 2 3 21 2 2 1 1
4 2019 Charlie Morton Rays AL X 0 4 3 4 3 2 9 5 14 1 5 13 5 12
5 2019 Shane Bieber Indians AL X 0 7 8 5 4 5 2 3 1 17 6 1 3 11
혹시 tidyverse를 실행시키다가 또는 다른 패키지를 실행시키다가 다음과 같은 에러가 뜨면 패키지의 버전이 너무 낮아서 업그레이드가 필요하다는 말이니 삭제한뒤 다시 설치하시길 바랍니다
tidymodels 에러
library('tidyverse')
Error: package or namespace load failed for ‘tidymodels’ in loadNamespace(i, c(lib.loc, .libPaths()), versionCheck = vI[[i]]):
namespace ‘tibble’ 3.0.5 is already loaded, but >= 3.1 required
2019년의 데이터는 수상여부도 없고 투표결과도 없기때문에 일단 빼준다. 그리고 학습용 데이터와 테스트데이터를 70:30 비율로 구분해준다.
> cya_2019 <- filter(cya,season==2019)
> cya_pre <- filter(cya,season != 2019)
> ## 학습용데이터 70%, 시험용데이터 30% 나누는 과정
> cya_split <- cya_pre %>% initial_split(prop=0.7)
> cya_split
<Analysis/Assess/Total>
<526/225/751>
> ##어떤 데이터가 학습용인지, 테스터용인지 알아볼때
> cya_split %>% training()
# A tibble: 526 x 19
season name team lg cy vote w l war era fip ip k bb hr k9 bb9 kbb babip
<dbl> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2018 Marco Gonzales Mariners AL X 0 12 13 9 17 8 19 23 1 3 20 4 5 26
2 2018 Dylan Bundy Orioles AL X 0 23 25 25 25 25 2 13 14 26 9 15 16 25
3 2018 Carlos Carrasco Indians AL X 0 4 14 6 8 4 23 3 6 10 5 6 3 24
4 2018 Mike Leake Mariners AL X 0 18 15 20 20 18 9 26 3 12 26 3 14 22
5 2018 Jakob Junis Royals AL X 0 22 21 22 21 22 5 17 7 23 18 8 10 20
> cya_split %>% testing()
# A tibble: 225 x 19
season name team lg cy vote w l war era fip ip k bb hr k9 bb9 kbb babip
<dbl> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2018 Luis Severino Yankees AL X 1 3 9 5 9 5 21 7 9 7 7 7 4 23
2 2018 Dallas Keuchel Astros AL X 0 14 19 10 14 11 16 21 16 5 24 11 21 21
3 2018 Mike Clevinger Indians AL X 0 11 10 8 6 9 18 8 21 9 11 19 20 12
4 2018 James Shields White Sox AL X 0 26 26 24 24 24 3 20 24 25 23 21 25 4
5 2018 Reynaldo Lopez White Sox AL X 0 25 18 19 16 21 6 22 23 13 22 23 24 3
오늘 최종적으로 해볼 데이터 예측은 범주형(O,X)인 cy의 여부를 예측할거지만
randomforest에서는 vote를 종속변수로 두고 나머지 순서형 변수(w,l~ kbb,babip)를 독립변수로 둬서 vote를 예측하겠습니다. 자세한 이유는 나중에 설명하겠습니다. 참고로 randomforest에서는 season, name, team, lg는 사용되지 않았습니다.
> ##cya_split 데이터중 학습용데이터에서 상관관계가 큰 변수확인, 평균을 0으로 하는 척도로 변환
> cya_recipe <- cya_split %>%
+ training() %>%
+ recipe(vote~w+l+war+era+fip+ip+k+bb+hr+k9+bb9+kbb+babip) %>%
+ step_corr(all_predictors()) %>%
+ step_center(all_predictors(),-all_outcomes()) %>%
+ step_scale(all_predictors(),-all_outcomes()) %>%
+ prep()
> cya_recipe
Data Recipe
Inputs:
role #variables
outcome 1
predictor 13
Training data contained 526 data points and no missing data.
Operations:
Correlation filter removed fip, bb9, k [trained]
Centering for w, l, war, era, ip, bb, hr, k9, kbb, babip [trained]
Scaling for w, l, war, era, ip, bb, hr, k9, kbb, babip [trained]
분석결과 fip, bb9, k가 분석에서 제외되었으며 나머지 변수 10개만 사용된것을 알수있습니다.
다음으로 bake와 juice를 이용해서 훈련용데이터와 테스트용데이터로 구분해줍니다.
bake는 시험용 데이터처리, juice는 데스트용 데이터처리에 사용
> ## 학습용,테스트용 데이터 처리
> cya_testing <- cya_recipe %>%
+ bake(cya_split %>% testing())
> cya_testing
# A tibble: 225 x 11
w l war era ip bb hr k9 kbb babip vote
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 -1.42 -0.931 -1.27 -0.928 0.0966 -0.892 -1.11 -1.11 -1.35 0.220 1
2 -0.477 -0.0740 -0.850 -0.512 -0.320 -0.316 -1.28 0.312 0.0825 0.0518 0
3 -0.734 -0.845 -1.02 -1.18 -0.153 0.0952 -0.945 -0.776 -0.00176 -0.705 0
> cya_training <- cya_recipe %>% juice()
> cya_training
# A tibble: 526 x 11
w l war era ip bb hr k9 kbb babip vote
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 -0.648 -0.588 -0.933 -0.263 -0.0700 -1.55 -1.44 -0.0226 -1.27 0.472 0
2 0.294 0.440 0.398 0.402 -1.49 -0.481 0.467 -0.943 -0.339 0.388 0
3 -1.33 -0.502 -1.18 -1.01 0.263 -1.14 -0.862 -1.28 -1.43 0.304 0
4 -0.134 -0.417 -0.0180 -0.0139 -0.903 -1.39 -0.696 0.480 -0.507 0.136 0
5 0.208 0.0974 0.148 0.0692 -1.24 -1.06 0.217 -0.190 -0.844 -0.0323 0
이제 전처리는 끝났고 랜덤포레스트를 진행해야하는데 그전에 랜덤포레스트는 분류나무와 회귀나무가 있습니다. 이번에는 회귀나무 모델을 사용해보도록하겠습니다. 2개의 패키지를 사용해볼건데 parship, ranger 패키지를 사용해보겠습니다.
두 훈련결과를 보면 R squared 값이 비슷하게 나온걸 알 수 있다. 참고로 랜덤포레스트는 분석을 돌릴때마다 값이 변하는데 그게 싫으신 분은 set.seed를 이용하면 난수가 고정되서 분석이 됩니다. set.seed는 일회성이기 때문에 같은 결과를 보고 싶을때는 매번 set.seed를 입력하고 분석를 돌려야 결과가 같게나옵니다.
> #랜덤 포레스트는 회귀나무와 분류나무가 있으며 이번에는 회귀나무 모델로 진행 parship 패키지사용
> cya_rf <- rand_forest(trees=100,mode="regression") %>%
+ set_engine("randomForest") %>%
+ fit(vote~w+l+war+era+ip+bb+hr+k9+kbb+babip,data=cya_training)
> cya_rf
parsnip model object
Fit time: 91ms
Call:
randomForest(x = maybe_data_frame(x), y = y, ntree = ~100)
Type of random forest: regression
Number of trees: 100
No. of variables tried at each split: 3
Mean of squared residuals: 230.2437
% Var explained: 82.47
library(ranger)
> #set.seed는 결과를 한번 돌릴때마다 다시 입력해줘야한다 즉, 일회성
> set.seed(2222)
> cya_rg <- rand_forest(trees=100, mode='regression') %>%
+ set_engine('ranger') %>%
+ fit(vote~w+l+war+era+ip+bb+hr+k9+kbb+babip,data=cya_training)
> cya_rg
parsnip model object
Fit time: 20ms
Ranger result
Call:
ranger::ranger(x = maybe_data_frame(x), y = y, num.trees = ~100, num.threads = 1, verbose = FALSE, seed = sample.int(10^5, 1))
Type: Regression
Number of trees: 100
Sample size: 526
Number of independent variables: 10
Mtry: 3
Target node size: 5
Variable importance mode: none
Splitrule: variance
OOB prediction error (MSE): 219.1043
R squared (OOB): 0.8334832
이제 테스트용 데이터를 사용하여 예측을 해보면 결과는 다음과 같이나옵니다. 각 행마다 예측결과를 보고 싶으면 1번방법을 사용하시면 되고 전체적인 예측결과를 보고싶으면 2번방법을 사용하시면됩니다. r^2값이 80.2% 나왔는데 꽤 쓸만한 예측이라고 볼수 있을거같습니다. 하지만 이건 어디까지나 vote를 예측한거지 수상여부를 예측한게 아닙니다.
> #테스터 데이터 사용// 여기서는 수상결과를 예측하는게 아니라 vote를 예측하는것
> cya_rf %>% predict(cya_testing) %>%
+ bind_cols(cya_testing) %>%
+ head(10) #1번
# A tibble: 10 x 12
.pred w l war era ip bb hr k9 kbb babip vote
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2.86e+ 1 -1.42 -0.931 -1.27 -0.928 0.0966 -0.892 -1.11 -1.11 -1.35 0.220 1
2 2.65e- 2 -0.477 -0.0740 -0.850 -0.512 -0.320 -0.316 -1.28 0.312 0.0825 0.0518 0
3 6.21e+ 0 -0.734 -0.845 -1.02 -1.18 -0.153 0.0952 -0.945 -0.776 -0.00176 -0.705 0
4 -1.54e-14 0.551 0.526 0.315 0.318 -1.40 0.342 0.384 0.228 0.420 -1.38 0
5 8.00e- 3 0.465 -0.160 -0.101 -0.346 -1.15 0.260 -0.613 0.145 0.335 -1.46 0
6 1.30e- 1 0.636 0.697 -0.683 0.651 -0.237 -0.727 0.0513 -1.28 -0.929 0.724 0
7 -1.51e-14 0.294 0.783 -0.184 0.402 -1.07 -0.892 0.134 0.145 -0.339 0.136 0
8 7.52e+ 0 -1.42 -1.53 0.148 -0.679 -1.24 0.0130 -0.115 0.0611 0.167 0.0518 2
9 -1.58e-14 0.122 0.526 0.0652 0.318 -1.15 -0.974 0.550 -0.608 -0.676 -0.452 0
10 7.26e+ 1 -0.0488 -0.760 -1.60 -1.59 0.763 -1.22 -1.61 -1.53 -1.52 -0.705 207
> cya_rf %>% predict(cya_testing) %>%
+ bind_cols(cya_testing) %>%
+ metrics(truth=vote,estimate=.pred) %>%
+ head(10) #2번
# A tibble: 3 x 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 rmse standard 18.3
2 rsq standard 0.802
3 mae standard 6.75
이제 데이터셋을 나누지 않고 2018년이전의 데이터를 교육하고 예측을 하여 수상여부를 예측해보겠습니다.
> ##2018년 이전전체데이터로 수상결과 예측하기
> cya_recipe2 <- cya_pre %>%
+ recipe(vote~w+l+war+era+fip+ip+k+bb+hr+k9+bb9+kbb+babip) %>%
+ step_corr(all_predictors()) %>%
+ step_center(all_predictors(),-all_outcomes()) %>%
+ step_scale(all_predictors(),-all_outcomes()) %>%
+ prep()
> cya_recipe2
Data Recipe
Inputs:
role #variables
outcome 1
predictor 13
Training data contained 751 data points and no missing data.
Operations:
Correlation filter removed fip, bb9, k [trained]
Centering for w, l, war, era, ip, bb, hr, k9, kbb, babip [trained]
Scaling for w, l, war, era, ip, bb, hr, k9, kbb, babip [trained]
> cya_testing_pre <- cya_recipe2 %>% bake(cya_pre)
> cya_training_pre <- cya_recipe2 %>% juice() #train test data가 같습니다.
#학습과정
> cya_rf2 <- rand_forest(trees=100, mode="regression") %>%
+ set_engine('randomForest',locallmp=TRUE) %>%
+ fit(vote~w+l+war+era+ip+bb+hr+k9+kbb+babip,data=cya_training_pre)
#예측결과 (행마다 비교해보기)
> cya_rf2 %>%
+ predict(cya_testing_pre) %>%
+ bind_cols(cya_pre)
# A tibble: 751 x 20
.pred season name team lg cy vote w l war era fip ip k bb hr k9 bb9 kbb babip
<dbl> <dbl> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 5.88e- 1 2018 Marco Go~ Mari~ AL X 0 12 13 9 17 8 19 23 1 3 20 4 5 26
2 2.80e- 2 2018 Dylan Bu~ Orio~ AL X 0 23 25 25 25 25 2 13 14 26 9 15 16 25
3 1.00e+ 1 2018 Carlos C~ Indi~ AL X 0 4 14 6 8 4 23 3 6 10 5 6 3 24
4 8.60e+ 0 2018 Luis Sev~ Yank~ AL X 1 3 9 5 9 5 21 7 9 7 7 7 4 23
5 -4.02e-14 2018 Mike Lea~ Mari~ AL X 0 18 15 20 20 18 9 26 3 12 26 3 14 22
위의결과에서는 사용하지 않는 변수도 있고 간단하게 보기위해서 select를 사용하여 특정컬럼만 추출하고 예측값을 랭크로 순위를 매긴다음 예상값이 1등이거나 cy변수에 O이 들어 있는 행만 추출한 결과는 다음과 같습니다. 설명하기가 어려워서 행마다 설명을 하겠습니다.
> cya_rf2 %>% # 훈련된 cya_rf2 data에
+ predict(cya_testing_pre) %>% #cya_testing_pre데이터를 이용하여 예측하는데
+ bind_cols(cya_pre) %>% # 행마다 예측결과와 비교해보고싶다.
+ group_by(season, lg) %>% # season, lg단위로 그룹지어서
+ mutate(rank=rank(-.pred)) %>% # 예측값이 큰게 1위를 하는 변수를 추가해서
+ select(season, lg, team, name, cy, rank) %>% #season, lg, team, name, cy, rank변수만 추출하는데
+ filter(rank==1 | cy=='O') %>% #랭크가 1이거나 cy가 O인 행만 추출할것이며
+ arrange(season,lg,cy) %>% #1차적으로 season오름차순정렬하고 2차로 lg오름차순 3차로 cy오름차순으로 정렬해서
+ View() #보여줘
위의 결과를 보시면 대부분 예측한변수(rank)가 1위인 팀이 수상을 한걸알수있으며 7,8,16,17행이 예측이 빗나간걸 알수있으며 2위를 한팀이 수상한경우도 있고 1위를 했지만 수상을 못한 경우도 있는걸 확인할수있습니다.
이제 마지막으로 변수중에 어떤변수가 가장 예측에 많은 영향이 있고 어떤변수가 가장 예측에 적은 영향도를 미쳤는지 확인해보겠습니다
install.packages("randomForestExplainer")
> library(randomForestExplainer)
> measure_importance(cya_rf2$fit)
[1] "Warning: your forest does not contain information on local importance so 'mse_increase' measure cannot be extracted. To add it regrow the forest with the option localImp = TRUE and run this function again."
variable mean_min_depth no_of_nodes node_purity_increase no_of_trees times_a_root p_value
1 babip 3.14 1677 44127.11 100 0 6.884425e-01
2 bb 3.43 1594 23816.43 100 0 9.958445e-01
3 era 1.23 1840 365789.09 100 34 1.378600e-04
4 hr 3.40 1614 23634.98 100 0 9.829408e-01
5 ip 2.43 1730 48125.48 100 10 1.939300e-01
6 k9 2.84 1638 35025.86 100 4 9.327501e-01
7 kbb 2.66 1576 51411.38 100 9 9.990622e-01
8 l 2.89 1615 47653.74 100 7 9.818118e-01
9 w 1.40 1879 229001.16 100 21 2.027143e-06
10 war 1.67 1795 173821.47 100 15 6.061266e-03
결과로 mean_min_depth, no_of_nodes, node_purity_increase, no_of_trees, times_a_root, p_value가 나왔으며 node_purity_increase가 높으면 많은 영향을 끼친다고는 하는데 각각 뭔뜻인지는 모르겠습니다.... 아직공부를 하는 과정이므로 공부해오겠습니다~~
randomForestExplainer에 대한 설명은 다음사이트를 참고하시면 되겠습니다.
야구는 전혀 모르는 상황에서 분석을 시행했는데 몇몇 변수들은 뭘 의미하는지도 모르겠지만 상당히 어렵네요.. 역시 분석은 계속 공부해야하는 부분인거같습니다.
kuduz.tistory.com/1202 이분 블로그를 참고했습니다
www.rdocumentation.org/packages/randomForestExplainer/versions/0.10.1
'R' 카테고리의 다른 글
PCA(주성분 분석) with iris (0) | 2021.04.07 |
---|---|
R 버전 확인 및 변경하기 (다운그레이드,업그레이드) (0) | 2021.04.07 |
ggplot2를 이용한 시각화 ② (회귀선표시, 회귀분석, 범례표시) (0) | 2021.04.05 |
ggplot2를 이용한 시각화 ① (두변수가연속형인경우, 그룹별 표시하기) (0) | 2021.04.05 |
DPLYR를 이용한 Cars93처리 ② (0) | 2021.04.05 |