Decision Tree based on Caret

Machine Learning

R code using Caret Package for Decision Tree

Yeongeun Jeon , Jeongwook Lee , Jung In Seo
09-26-2020

다양한 의사결정나무모형을 실습하기 위해서 사용될 예제 데이터는 “Universal Bank_Main”로 유니버셜 은행의 고객들에 대한 데이터(출처 : Data Mining for Business Intelligence, Shmueli et al. 2010)이다. 데이터는 총 2500개이며, 변수의 갯수는 13개이다. 여기에서, TargetPerson.Loan이다.


의사결정나무를 분석할 수 있는 패키지는 여러가지가 있다.

이 중 실습에서는 “caret”을 이용하여 “rpart”와 “C5.0” 방법으로 의사결정나무 분석을 한다.


1. 데이터 불러오기

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,~

2. 데이터 분할

# Partition (Traning Data : Test Data = 7:3)

pacman::p_load("caret")

y      <- UB$Personal.Loan                            # Target
 
set.seed(200)
ind    <- createDataPartition(y, p=0.7, list=T)       # Training Data를 70%로 추출

UB.trd <- UB[ind$Resample1,]                          # Traning Data


UB.ted <- UB[-ind$Resample1,]                         # Test Data

3. Method “rpart”


3-1. 모형 적합

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일 때 적합된 모형이다.

3-2. Tree 그림


3-2-1. “Plot”

plot(rtree.grid.caret$finalModel)
text(rtree.grid.caret$finalModel, use.n=TRUE, all=TRUE, cex=0.8)

3-2-2. “fancyRpartPlot”

pacman::p_load("rattle")
fancyRpartPlot(rtree.grid.caret$finalModel)    # 가독성 좋은 그래프래프

3-2-3. “visTree”

pacman::p_load("visNetwork","sparkline")  # 네트워크 기반 그래프

visTree(rtree.grid.caret$finalModel)  

3-3. 모형 평가

# 적합된 모형으로 Test Data의 클래스 예측NArtree.grid.caret.pred <- predict(rtree.grid.caret, newdata=UB.ted)   # predict(트리모형, Test Data) 

3-3-1. ConfusionMatrix

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             
                                          

3-3-2. ROC 곡선


1) Package “pROC”

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)


2) Package “Epi”

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)


3) Package “ROCR”

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


3-3-3. 향상 차트


1) Package “ROCR”

rtree.perf       <- performance(rtree.pred, "lift", "rpp")       # Lift Chart
plot(rtree.perf, main="lift curve", colorize=T, lwd=2)  
detach(package:ROCR)

2) Package “lift”

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)

4. Method “C5.0”


4-1. 모형 적합

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.0model, 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일 때 적합된 모형이다.

4-2. Tree 그림

caret에서는 C5.0에 대해서 최종 모델에 대한 plot을 그릴 수 없다. 그래서 최적의 Tune Parameter를 가지고 Package C50에서 다시 모형 적합 후 그림을 그릴 수 있다. 이 때 caret의 최종모형에 대한 예측과 C50의 모형에 대한 예측이 같은지 확인하는 것이 필요하다.

pacman::p_load("C50", "MASS","partykit")  

set.seed(100)
c5model = C5.0(x = UB.trd[,-9], y = UB.trd$Personal.Loan,
                       trials = ctree.grid.caret$bestTune$trials, rules = ctree.grid.caret$bestTune$model == "rules",
                       control = C5.0Control(winnow = ctree.grid.caret$bestTune$winnow))
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)

4-3. 모형 평가

# 적합된 모형으로 Test Data의 클래스 예측NActree.grid.caret.pred <- predict(ctree.grid.caret, newdata=UB.ted)   # predict(트리모형, Test Data) 

4-3-1. ConfusionMatrix

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             
                                          

4-3-2. ROC 곡선


1) Package “pROC”

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)


2) Package “Epi”

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)


3) Package “ROCR”

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

4-3-3. 향상 차트


1) Package “ROCR”

ctree.perf       <- performance(ctree.pred, "lift", "rpp")       # Lift Chart
plot(ctree.perf, main="lift curve", colorize=T, lwd=2)  
detach(package:ROCR)


2) Package “lift”

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)

5. 모형 비교

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)

Reuse

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 ...".