R code using Caret Package for Decision Tree
다양한 의사결정나무모형을 실습하기 위해서 사용될 예제 데이터는 “Universal Bank_Main”로 유니버셜 은행의 고객들에 대한 데이터(출처 : Data Mining for Business Intelligence, Shmueli et al. 2010)이다. 데이터는 총 2500개이며, 변수의 갯수는 13개이다. 여기에서, Target은 Person.Loan
이다.
의사결정나무를 분석할 수 있는 패키지는 여러가지가 있다.
이 중 실습에서는 “caret”을 이용하여 “rpart”와 “C5.0” 방법으로 의사결정나무 분석을 한다.
pacman::p_load("data.table", "dplyr")
UB <- fread(paste(getwd(),"Universal Bank_Main.csv", sep="/")) %>% # 데이터 불러오기
data.frame() %>% # Data frame 변환환
mutate(Personal.Loan = ifelse(Personal.Loan==1, "yes","no")) %>% # Character for classification
select(-1) # ID변수 제거거
# select columns
cols <- c("Family", "Education", "Personal.Loan", "Securities.Account",
"CD.Account", "Online", "CreditCard")
UB <- UB %>%
mutate_at(cols, as.factor) # 범주형 변수 변환
glimpse(UB) # 데이터 구조
Rows: 2,500
Columns: 13
$ Age <int> 25, 45, 39, 35, 35, 37, 53, 50, 35, 34, 6~
$ Experience <int> 1, 19, 15, 9, 8, 13, 27, 24, 10, 9, 39, 5~
$ Income <int> 49, 34, 11, 100, 45, 29, 72, 22, 81, 180,~
$ ZIP.Code <int> 91107, 90089, 94720, 94112, 91330, 92121,~
$ Family <fct> 4, 3, 1, 1, 4, 4, 2, 1, 3, 1, 4, 3, 2, 4,~
$ CCAvg <dbl> 1.6, 1.5, 1.0, 2.7, 1.0, 0.4, 1.5, 0.3, 0~
$ Education <fct> 1, 1, 1, 2, 2, 2, 2, 3, 2, 3, 3, 2, 3, 2,~
$ Mortgage <int> 0, 0, 0, 0, 0, 155, 0, 0, 104, 0, 0, 0, 0~
$ Personal.Loan <fct> no, no, no, no, no, no, no, no, no, yes, ~
$ Securities.Account <fct> 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,~
$ CD.Account <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,~
$ Online <fct> 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1,~
$ CreditCard <fct> 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,~
Turn Parameter인 CP
의 그리드를 조정하기 위해서 그리드를 조정해주지 않았을 때 결과를 참고하였다. 아래는 그리드를 조정해주지 않았을 때 기본적인 결과이다. (caret
에서 자동적으로 xval = 0
이며, 최적의 cp를 이용하여 최종 모형을 적합하기 때문에 가지치기를 할 필요가 없다.)
fitControl <- trainControl(method="cv", number=5) # 5-fold-cross validation
set.seed(100) # seed 고정 for cross validation
rtree.caret <- train(Personal.Loan~., data=UB.trd, method="rpart", trControl = fitControl)
rtree.caret
CART
1751 samples
12 predictor
2 classes: 'no', 'yes'
No pre-processing
Resampling: Cross-Validated (5 fold)
Summary of sample sizes: 1401, 1401, 1400, 1401, 1401
Resampling results across tuning parameters:
cp Accuracy Kappa
0.0500000 0.9697387 0.8284624
0.1000000 0.9611673 0.7713973
0.1666667 0.9485975 0.6144540
Accuracy was used to select the optimal model using the
largest value.
The final value used for the model was cp = 0.05.
모수가 하나이기 때문에 Grid Search
방법으로 3개의 CP
값을 얻고 각 CP
로 고정한 후 5-Fold-Cross Validation 방법으로 정확도를 얻었다. CP
가0.05일 때 정확도가 가장 높은 것을 알 수 있다. 아래는 CP
가 0.05일 때 가장 좋으므로, CP
의 탐색 범위를 조절한 코드이다.
customGrid <- expand.grid(cp = seq(0.01,0.07, by=0.001)) # cp의 탐색 범위
set.seed(100) # seed 고정 for cross validation
rtree.grid.caret <- train(Personal.Loan~., data=UB.trd, tuneGrid = customGrid,
method="rpart", trControl = fitControl)
rtree.grid.caret
CART
1751 samples
12 predictor
2 classes: 'no', 'yes'
No pre-processing
Resampling: Cross-Validated (5 fold)
Summary of sample sizes: 1401, 1401, 1400, 1401, 1401
Resampling results across tuning parameters:
cp Accuracy Kappa
0.010 0.9760179 0.8639124
0.011 0.9760179 0.8639124
0.012 0.9760179 0.8639124
0.013 0.9760179 0.8639124
0.014 0.9760179 0.8623885
0.015 0.9760179 0.8623885
0.016 0.9760179 0.8623885
0.017 0.9760179 0.8623885
0.018 0.9765893 0.8653539
0.019 0.9765893 0.8653539
0.020 0.9765893 0.8653539
0.021 0.9760179 0.8629073
0.022 0.9760179 0.8629073
0.023 0.9760179 0.8629073
0.024 0.9760179 0.8629073
0.025 0.9760179 0.8629073
0.026 0.9760179 0.8629073
0.027 0.9760179 0.8629073
0.028 0.9754481 0.8614672
0.029 0.9754481 0.8614672
0.030 0.9754481 0.8614672
0.031 0.9754481 0.8614672
0.032 0.9754481 0.8614672
0.033 0.9754481 0.8614672
0.034 0.9754481 0.8614672
0.035 0.9737387 0.8493612
0.036 0.9737387 0.8493612
0.037 0.9737387 0.8493612
0.038 0.9737387 0.8493612
0.039 0.9737387 0.8493612
0.040 0.9737387 0.8493612
0.041 0.9737387 0.8493612
0.042 0.9725958 0.8410719
0.043 0.9725958 0.8410719
0.044 0.9725958 0.8410719
0.045 0.9725958 0.8410719
0.046 0.9725958 0.8410719
0.047 0.9725958 0.8410719
0.048 0.9725958 0.8410719
0.049 0.9697387 0.8284624
0.050 0.9697387 0.8284624
0.051 0.9697387 0.8284624
0.052 0.9697387 0.8284624
0.053 0.9697387 0.8284624
0.054 0.9697387 0.8284624
0.055 0.9697387 0.8284624
0.056 0.9691673 0.8246504
0.057 0.9691673 0.8246504
0.058 0.9691673 0.8246504
0.059 0.9691673 0.8246504
0.060 0.9691673 0.8246504
0.061 0.9691673 0.8246504
0.062 0.9691673 0.8246504
0.063 0.9691673 0.8246504
0.064 0.9691673 0.8246504
0.065 0.9691673 0.8246504
0.066 0.9691673 0.8246504
0.067 0.9691673 0.8246504
0.068 0.9691673 0.8246504
0.069 0.9691673 0.8246504
0.070 0.9691673 0.8246504
Accuracy was used to select the optimal model using the
largest value.
The final value used for the model was cp = 0.02.
plot(rtree.grid.caret)
cp
값이 0.02일 때 정확도가 가장 높다는 것을 알 수 있으며, 최종 모형은 cp
= 0.02일 때 적합된 모형이다.
pacman::p_load("rattle")
fancyRpartPlot(rtree.grid.caret$finalModel) # 가독성 좋은 그래프래프
pacman::p_load("visNetwork","sparkline") # 네트워크 기반 그래프
visTree(rtree.grid.caret$finalModel)
# 적합된 모형으로 Test Data의 클래스 예측NArtree.grid.caret.pred <- predict(rtree.grid.caret, newdata=UB.ted) # predict(트리모형, Test Data)
confusionMatrix(rtree.grid.caret.pred, UB.ted$Personal.Loan, positive = "yes") # ConfusionMatrix(예측 클래스, 실제 클래스, positive="관심 클래스") 클래스")
Confusion Matrix and Statistics
Reference
Prediction no yes
no 665 15
yes 8 61
Accuracy : 0.9693
95% CI : (0.9543, 0.9804)
No Information Rate : 0.8985
P-Value [Acc > NIR] : 1.268e-13
Kappa : 0.8244
Mcnemar's Test P-Value : 0.2109
Sensitivity : 0.80263
Specificity : 0.98811
Pos Pred Value : 0.88406
Neg Pred Value : 0.97794
Prevalence : 0.10147
Detection Rate : 0.08144
Detection Prevalence : 0.09212
Balanced Accuracy : 0.89537
'Positive' Class : yes
pacman::p_load("pROC")
test.rtree.prob <- predict(rtree.grid.caret, newdata = UB.ted, type="prob") # Training Data로 적합시킨 모형에 대한 Test Data의 각 클래스에 대한 예측 확률
test.rtree.prob <- test.rtree.prob[,2] # "yes"에 대한 예측 확률
ac <- UB.ted$Personal.Loan # 범주형을 숫자형으로 변환할 때 문자형으로 변환한 뒤 숫자형으로 변환해야함NApp <- as.numeric(test.rtree.prob) # "yes"에 대한 예측 확률
tree.roc <- roc(ac, pp, plot = T, col = "red") # roc(실제 클래스, 예측 확률)률)
auc <- round(auc(tree.roc),3)
legend("bottomright",legend=auc, bty="n")
detach(package:pROC)
pacman::p_load("devtools", "Epi")
# install_version("etm", version = "1.1", repos = "http://cran.us.r-project.org")
ROC(pp, ac, plot="ROC") # ROC(예측 확률, 실제 클래스) / 최적의 cutoff value 예측 가능
detach(package:Epi)
pacman::p_load("ROCR")
rtree.pred <- prediction(test.rtree.prob, UB.ted$Personal.Loan) # prediction(예측 확률, 실제 클레스)
rtree.perf <- performance(rtree.pred, "tpr", "fpr") # performance(, "민감도", "1-특이도")")
plot(rtree.perf, col="red") # ROC Curve
perf.auc <- performance(rtree.pred, "auc") # AUC
auc <- attributes(perf.auc)$y.values
legend("bottomright", legend=auc, bty="n")
rtree.perf <- performance(rtree.pred, "lift", "rpp") # Lift Chart
plot(rtree.perf, main="lift curve", colorize=T, lwd=2)
detach(package:ROCR)
pacman::p_load("lift")
ac.numeric <- ifelse(UB.ted$Personal.Loan=="yes",1,0) # 실제 클래스를 수치형으로 변환 변환
plotLift(test.rtree.prob, ac.numeric, cumulative = T, n.buckets = 24) # plotLift(예측 확률, 실제 클래스)스)
TopDecileLift(test.rtree.prob, ac.numeric) # Top 10% 향상도 출력
[1] 8.147
detach(package:lift)
Turn Parameter인 trials
의 그리드를 조정하기 위해서 그리드를 조정해주지 않았을 때 결과를 참고하였다. 아래는 그리드를 조정해주지 않았을 때 기본적인 결과이다.
fitControl <- trainControl(method="cv", number=5) # 5-fold-cross validation
set.seed(100) # seed 고정 for cross validation
ctree.caret <- train(x = UB.trd[,-9], y = UB.trd$Personal.Loan, data=UB.trd, # x= , y= 로 명시! 아니면 Package "C50" 했을 때와 예측이 다름
method="C5.0", trControl = fitControl)
ctree.caret
C5.0
1751 samples
12 predictor
2 classes: 'no', 'yes'
No pre-processing
Resampling: Cross-Validated (5 fold)
Summary of sample sizes: 1401, 1401, 1400, 1401, 1401
Resampling results across tuning parameters:
model winnow trials Accuracy Kappa
rules FALSE 1 0.9817306 0.8961760
rules FALSE 10 0.9845845 0.9141485
rules FALSE 20 0.9828702 0.9043359
rules TRUE 1 0.9771656 0.8721187
rules TRUE 10 0.9783036 0.8746518
rules TRUE 20 0.9788751 0.8792319
tree FALSE 1 0.9783020 0.8788430
tree FALSE 10 0.9805845 0.8923516
tree FALSE 20 0.9845828 0.9146147
tree TRUE 1 0.9771656 0.8734177
tree TRUE 10 0.9788751 0.8781789
tree TRUE 20 0.9783053 0.8773222
Accuracy was used to select the optimal model using the
largest value.
The final values used for the model were trials = 10, model =
rules and winnow = FALSE.
caret
에서 C5.0
은 model
, winnow
, trials
Parameter를 가진다. - model
: Model Type으로 rules
는 규칙 기반, tree
는 의사결정나무 - winnow
: TRUE
이면 유용하지 않은 예측 변수는 제외하고 모형 적합 (예측 변수가 많을 떄 유용) - trials
: 부스팅 횟수
trials
= 5일 때 가장 정확도가 높으므로, 탐색 범위를 그 근방에서 조정해준다.
customGrid <- expand.grid(model = c("tree"),
winnow = FALSE,
trials = seq(5,20,1)) # 모수 탐색 범위
set.seed(100) # seed 고정 for cross validation
ctree.grid.caret <- train(x = UB.trd[,-9], y = UB.trd$Personal.Loan, data=UB.trd, tuneGrid = customGrid,
method="C5.0", trControl = fitControl)
ctree.grid.caret
C5.0
1751 samples
12 predictor
2 classes: 'no', 'yes'
No pre-processing
Resampling: Cross-Validated (5 fold)
Summary of sample sizes: 1401, 1401, 1400, 1401, 1401
Resampling results across tuning parameters:
trials Accuracy Kappa
5 0.9834416 0.9100873
6 0.9805845 0.8911922
7 0.9828702 0.9063286
8 0.9834416 0.9082698
9 0.9840130 0.9117356
10 0.9805845 0.8923516
11 0.9834416 0.9094845
12 0.9834416 0.9088279
13 0.9834400 0.9088936
14 0.9840114 0.9113861
15 0.9834400 0.9077827
16 0.9845828 0.9144347
17 0.9851543 0.9178158
18 0.9845828 0.9144347
19 0.9857257 0.9207135
20 0.9845828 0.9146147
Tuning parameter 'model' was held constant at a value of tree
Tuning parameter 'winnow' was held constant at a value of FALSE
Accuracy was used to select the optimal model using the
largest value.
The final values used for the model were trials = 19, model =
tree and winnow = FALSE.
plot(ctree.grid.caret)
trials
이 19일 때 정확도가 가장 높다는 것을 알 수 있으며, 최종 모형은 trials
= 19일 때 적합된 모형이다.
caret
에서는 C5.0
에 대해서 최종 모델에 대한 plot을 그릴 수 없다. 그래서 최적의 Tune Parameter를 가지고 Package C50
에서 다시 모형 적합 후 그림을 그릴 수 있다. 이 때 caret
의 최종모형에 대한 예측과 C50
의 모형에 대한 예측이 같은지 확인하는 것이 필요하다.
c5model.pred <- predict(c5model, newdata=UB.ted, type="prob") # C50에 대한 예측 확률
ctree.grid.caret.pred <- predict(ctree.grid.caret, newdata=UB.ted,type="prob") # Caret에 대한 예측 확률
sum(c5model.pred[,1]==ctree.grid.caret.pred[,1]) == dim(UB.ted)[1] # 예측 확률이 모두 같은 지 확인NA
[1] TRUE
c5model.pred <- predict(c5model, newdata=UB.ted) # C50에 대한 예측 클래스 스
ctree.grid.caret.pred <- predict(ctree.grid.caret, newdata=UB.ted) # Caret에 대한 예측 클래스래스
sum(c5model.pred==ctree.grid.caret.pred) == dim(UB.ted)[1] # 예측 확률이 모두 같은 지 확인NA
[1] TRUE
plot(c5model)
# 적합된 모형으로 Test Data의 클래스 예측NActree.grid.caret.pred <- predict(ctree.grid.caret, newdata=UB.ted) # predict(트리모형, Test Data)
confusionMatrix(ctree.grid.caret.pred, UB.ted$Personal.Loan, positive = "yes") # ConfusionMatrix(예측 클래스, 실제 클래스, positive="관심 클래스") 클래스")
Confusion Matrix and Statistics
Reference
Prediction no yes
no 669 10
yes 4 66
Accuracy : 0.9813
95% CI : (0.9688, 0.9897)
No Information Rate : 0.8985
P-Value [Acc > NIR] : <2e-16
Kappa : 0.8938
Mcnemar's Test P-Value : 0.1814
Sensitivity : 0.86842
Specificity : 0.99406
Pos Pred Value : 0.94286
Neg Pred Value : 0.98527
Prevalence : 0.10147
Detection Rate : 0.08812
Detection Prevalence : 0.09346
Balanced Accuracy : 0.93124
'Positive' Class : yes
pacman::p_load("pROC")
test.ctree.prob <- predict(ctree.grid.caret, newdata = UB.ted, type="prob") # Training Data로 적합시킨 모형에 대한 Test Data의 각 클래스에 대한 예측 확률
test.ctree.prob <- test.ctree.prob[,2] # "yes"에 대한 예측 확률
ac <- UB.ted$Personal.Loan # 범주형을 숫자형으로 변환할 때 문자형으로 변환한 뒤 숫자형으로 변환해야함NApp <- as.numeric(test.ctree.prob) # "yes"에 대한 예측 확률
tree.roc <- roc(ac, pp, plot = T, col = "red") # roc(실제 클래스, 예측 확률)률)
auc <- round(auc(tree.roc),3)
legend("bottomright",legend=auc, bty="n")
detach(package:pROC)
pacman::p_load("devtools", "Epi")
# install_version("etm", version = "1.1", repos = "http://cran.us.r-project.org")
ROC(pp, ac, plot="ROC") # ROC(예측 확률, 실제 클래스) / 최적의 cutoff value 예측 가능
detach(package:Epi)
pacman::p_load("ROCR")
ctree.pred <- prediction(test.ctree.prob, UB.ted$Personal.Loan) # prediction(예측 확률, 실제 클레스)
ctree.perf <- performance(ctree.pred, "tpr", "fpr") # performance(, "민감도", "1-특이도") )
plot(ctree.perf, col="red") # ROC Curve
perf.auc <- performance(ctree.pred, "auc") # AUC
auc <- attributes(perf.auc)$y.values
legend("bottomright", legend=auc, bty="n")
ctree.perf <- performance(ctree.pred, "lift", "rpp") # Lift Chart
plot(ctree.perf, main="lift curve", colorize=T, lwd=2)
detach(package:ROCR)
pacman::p_load("lift")
ac.numeric <- ifelse(UB.ted$Personal.Loan=="yes",1,0) # 실제 클래스를 수치형으로 변환 변환
plotLift(test.ctree.prob, ac.numeric, cumulative = T, n.buckets = 24) # plotLift(예측 확률, 실제 클래스)스)
TopDecileLift(test.ctree.prob, ac.numeric) # Top 10% 향상도 출력
[1] 8.935
detach(package:lift)
pacman::p_load("ROCR")
rtree.pred <- prediction(test.rtree.prob, UB.ted$Personal.Loan) # prediction(예측 확률, 실제 클레스)
rtree.perf <- performance(rtree.pred, "tpr", "fpr") # performance(, "민감도", "1-특이도")
plot(rtree.perf, col="blue") # ROC Curve
par(new=TRUE)
ctree.perf <- performance(ctree.pred, "tpr", "fpr") # performance(, "민감도", "1-특이도")
plot(ctree.perf, col="red") # ROC Curve
legend("bottomright", legend=c("rpart","C50"), col=c("blue", "red"), lty=c(1,1))
detach(package:ROCR)
Text and figures are licensed under Creative Commons Attribution CC BY 4.0. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".