Decision Tree using Package caret

Data Mining

Description for Decision Tree using Package caret

Yeongeun Jeon , Jung In Seo
2023-04-05

Tree-based Algorithm


실습 자료 : 유니버셜 은행의 고객 2,500명에 대한 자료(출처 : Data Mining for Business Intelligence, Shmueli et al. 2010)이며, 총 13개의 변수를 포함하고 있다. 이 자료에서 TargetPersonal Loan이다.




1. 데이터 불러오기

pacman::p_load("data.table", 
               "tidyverse", 
               "dplyr",
               "ggplot2", "GGally",
               "caret",
               "rattle", "rpart.plot",                                  # For fancyRpartPlot
               "visNetwork", "sparkline",                               # For visTree
               "doParallel", "parallel")                                # For 병렬 처리

registerDoParallel(cores=detectCores())                                 # 사용할 Core 개수 지정

UB <- fread("../Universal Bank_Main.csv")                               # 데이터 불러오기

UB %>%
  as_tibble
# A tibble: 2,500 × 14
      ID   Age Experience Income `ZIP Code` Family CCAvg Education
   <int> <int>      <int>  <int>      <int>  <int> <dbl>     <int>
 1     1    25          1     49      91107      4   1.6         1
 2     2    45         19     34      90089      3   1.5         1
 3     3    39         15     11      94720      1   1           1
 4     4    35          9    100      94112      1   2.7         2
 5     5    35          8     45      91330      4   1           2
 6     6    37         13     29      92121      4   0.4         2
 7     7    53         27     72      91711      2   1.5         2
 8     8    50         24     22      93943      1   0.3         3
 9     9    35         10     81      90089      3   0.6         2
10    10    34          9    180      93023      1   8.9         3
# ℹ 2,490 more rows
# ℹ 6 more variables: Mortgage <int>, `Personal Loan` <int>,
#   `Securities Account` <int>, `CD Account` <int>, Online <int>,
#   CreditCard <int>

2. 데이터 전처리

UB %<>%
  data.frame() %>%                                                      # Data Frame 형태로 변환 
  mutate(Personal.Loan = ifelse(Personal.Loan == 1, "yes", "no")) %>%   # Target을 문자형 변수로 변환
  select(-1)                                                            # ID 변수 제거

# Convert to Factor
fac.col <- c("Family", "Education", "Securities.Account", 
             "CD.Account", "Online", "CreditCard",
             # Target
             "Personal.Loan")

UB <- UB %>% 
  mutate_at(fac.col, 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,…

3. 데이터 탐색

ggpairs(UB,                                           
        columns = c("Age", "Experience", "Income",        # 수치형 예측 변수
                    "ZIP.Code", "CCAvg", "Mortgage"),                            
        aes(colour = Personal.Loan)) +                    # Target의 범주에 따라 색깔을 다르게 표현
  theme_bw()
ggpairs(UB,                                           
        columns = c("Age", "Experience", "Income",        # 수치형 예측 변수
                    "ZIP.Code", "CCAvg", "Mortgage"), 
        aes(colour = Personal.Loan), alpha = 0.8) +       # Target의 범주에 따라 색깔을 다르게 표현
  scale_colour_manual(values = c("#00798c", "#d1495b")) + # 특정 색깔 지정
  scale_fill_manual(values = c("#00798c", "#d1495b")) +   # 특정 색깔 지정
  theme_bw()
ggpairs(UB,                                           
        columns = c("Age", "Income",                      # 수치형 예측 변수
                    "Family", "Education"),               # 범주형 예측 변수
        aes(colour = Personal.Loan, alpha = 0.8)) +       # Target의 범주에 따라 색깔을 다르게 표현
  scale_colour_manual(values = c("#E69F00", "#56B4E9")) + # 특정 색깔 지정
  scale_fill_manual(values = c("#E69F00", "#56B4E9")) +   # 특정 색깔 지정
  theme_bw()


4. 데이터 분할

# Partition (Training Dataset : Test Dataset = 7:3)
y      <- UB$Personal.Loan                            # Target
 
set.seed(200)
ind    <- createDataPartition(y, p = 0.7, list = T)   # Index를 이용하여 7:3으로 분할
UB.trd <- UB[ind$Resample1,]                          # Training Dataset
UB.ted <- UB[-ind$Resample1,]                         # Test Dataset

5. 모형 훈련

Package "caret"은 통합 API를 통해 R로 기계 학습을 실행할 수 있는 매우 실용적인 방법을 제공한다. Package "caret"에서는 초모수의 최적의 조합을 찾는 방법으로 그리드 검색(Grid Search), 랜덤 검색(Random Search), 직접 탐색 범위 설정이 있다. 여기서는 초모수 cp (Complexity Parameter)의 최적값을 찾기 위해 그리드 검색을 수행하였고, 이를 기반으로 직접 탐색 범위를 설정하였다. 아래는 그리드 검색을 수행하였을 때 결과이다.

fitControl <- trainControl(method = "cv", number = 5,  # 5-Fold Cross Validation (5-Fold CV)
                           allowParallel = TRUE)       # 병렬 처리

set.seed(100)                                          # For CV
rtree.caret <- train(x = UB.trd[,-9],                  # Training Dataset including Only 예측 변수
                     y = UB.trd[,"Personal.Loan"],     # Training Dataset including Only Target
                     method = "rpart", 
                     trControl = fitControl)   

Caution! Package "caret"에서는 함수 rpart()의 옵션 xval = 0이며, cp의 최적값을 이용하여 최종 모형을 훈련하기 때문에 가지치기를 수행할 필요가 없다. 게다가, Package "caret"을 통해 "rpart"를 수행하는 경우, 함수 train(Target ~ 예측 변수, data)를 사용하면 범주형 예측 변수는 자동적으로 One-hot Encoding 변환이 된다. 범주형 예측 변수에 대해 One-hot Encoding 변환을 수행하고 싶지 않다면 함수 train(x = 예측 변수만 포함하는 데이터셋, y = Target만 포함하는 데이터셋)를 사용한다.

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.01944444  0.9771624  0.8718446
  0.15000000  0.9634546  0.7852939
  0.32500000  0.9205975  0.2898210

Accuracy was used to select the optimal model using the
 largest value.
The final value used for the model was cp = 0.01944444.
plot(rtree.caret)                                      # Plot

Result! 랜덤하게 결정된 3개의 cp 값에 대한 정확도를 보여주며, cp = 0.01944444일 때 정확도가 가장 높은 것을 알 수 있다. 따라서 그리드 검색을 통해 찾은 최적의 초모수 값 0.01944444 근처의 값들을 탐색 범위로 설정하여 훈련을 다시 수행할 수 있다.

customGrid <- expand.grid(cp = seq(0.01, 0.07, by = 0.001))   # cp의 탐색 범위 

set.seed(100)                                                 # For CV
rtree.grid.caret <- train(x = UB.trd[,-9],                    # Training Dataset including Only 예측 변수
                          y = UB.trd[,"Personal.Loan"],       # Training Dataset including Only Target
                          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.9771624  0.8735627
  0.011  0.9771624  0.8735627
  0.012  0.9771624  0.8735627
  0.013  0.9771624  0.8735627
  0.014  0.9771624  0.8720389
  0.015  0.9771624  0.8720389
  0.016  0.9771624  0.8720389
  0.017  0.9771624  0.8720389
  0.018  0.9771624  0.8718446
  0.019  0.9771624  0.8718446
  0.020  0.9771624  0.8718446
  0.021  0.9771624  0.8718446
  0.022  0.9771624  0.8718446
  0.023  0.9771624  0.8718446
  0.024  0.9771624  0.8718446
  0.025  0.9771624  0.8718446
  0.026  0.9771624  0.8718446
  0.027  0.9771624  0.8718446
  0.028  0.9771624  0.8718446
  0.029  0.9771624  0.8718446
  0.030  0.9771624  0.8718446
  0.031  0.9771624  0.8718446
  0.032  0.9771624  0.8718446
  0.033  0.9771624  0.8718446
  0.034  0.9771624  0.8718446
  0.035  0.9771624  0.8718446
  0.036  0.9771624  0.8718446
  0.037  0.9771624  0.8718446
  0.038  0.9771624  0.8718446
  0.039  0.9771624  0.8718446
  0.040  0.9771624  0.8718446
  0.041  0.9771624  0.8718446
  0.042  0.9771624  0.8718446
  0.043  0.9771624  0.8718446
  0.044  0.9771624  0.8718446
  0.045  0.9771624  0.8718446
  0.046  0.9771624  0.8718446
  0.047  0.9771624  0.8718446
  0.048  0.9771624  0.8718446
  0.049  0.9771624  0.8718446
  0.050  0.9771624  0.8718446
  0.051  0.9771624  0.8718446
  0.052  0.9771624  0.8718446
  0.053  0.9771624  0.8718446
  0.054  0.9771624  0.8718446
  0.055  0.9771624  0.8718446
  0.056  0.9754481  0.8614672
  0.057  0.9754481  0.8614672
  0.058  0.9754481  0.8614672
  0.059  0.9754481  0.8614672
  0.060  0.9754481  0.8614672
  0.061  0.9754481  0.8614672
  0.062  0.9754481  0.8614672
  0.063  0.9754481  0.8614672
  0.064  0.9754481  0.8614672
  0.065  0.9754481  0.8614672
  0.066  0.9754481  0.8614672
  0.067  0.9754481  0.8614672
  0.068  0.9754481  0.8614672
  0.069  0.9754481  0.8614672
  0.070  0.9754481  0.8614672

Accuracy was used to select the optimal model using the
 largest value.
The final value used for the model was cp = 0.055.
plot(rtree.grid.caret)                                  # Plot
rtree.grid.caret$bestTune                               # cp의 최적값
      cp
46 0.055

Result! cp = 0.055일 때 정확도가 가장 높다는 것을 알 수 있으며, cp = 0.055를 가지는 모형을 최적의 훈련된 모형으로 선택한다.


6. Tree Plot

6-1. “fancyRpartPlot”

fancyRpartPlot(rtree.grid.caret$finalModel)    # Plot

6-2. “visTree”

visTree(rtree.grid.caret$finalModel)           # Network-based Plot 

7. 모형 평가

Caution! 모형 평가를 위해 Test Dataset에 대한 예측 class/확률 이 필요하며, 함수 predict()를 이용하여 생성한다.

# 예측 class 생성
rtree.grid.caret.pred <- predict(rtree.grid.caret, 
                                 newdata = UB.ted[,-9])   # Test Dataset including Only 예측 변수                      

rtree.grid.caret.pred %>%
  as_tibble
# A tibble: 749 × 1
   value
   <fct>
 1 no   
 2 no   
 3 no   
 4 no   
 5 no   
 6 no   
 7 no   
 8 no   
 9 no   
10 no   
# ℹ 739 more rows


7-1. ConfusionMatrix

CM   <- caret::confusionMatrix(rtree.grid.caret.pred, UB.ted$Personal.Loan, 
                               positive = "yes")          # confusionMatrix(예측 class, 실제 class, positive = "관심 class")
CM
Confusion Matrix and Statistics

          Reference
Prediction  no yes
       no  656  12
       yes  17  64
                                          
               Accuracy : 0.9613          
                 95% CI : (0.9449, 0.9739)
    No Information Rate : 0.8985          
    P-Value [Acc > NIR] : 1.228e-10       
                                          
                  Kappa : 0.7937          
                                          
 Mcnemar's Test P-Value : 0.4576          
                                          
            Sensitivity : 0.84211         
            Specificity : 0.97474         
         Pos Pred Value : 0.79012         
         Neg Pred Value : 0.98204         
             Prevalence : 0.10147         
         Detection Rate : 0.08545         
   Detection Prevalence : 0.10814         
      Balanced Accuracy : 0.90842         
                                          
       'Positive' Class : yes             
                                          


7-2. ROC 곡선

# 예측 확률 생성 
test.rtree.prob <- predict(rtree.grid.caret,
                           newdata = UB.ted[,-9],         # Test Dataset including Only 예측 변수 
                           type = "prob")                 # 예측 확률 생성 

test.rtree.prob %>%
  as_tibble
# A tibble: 749 × 2
      no    yes
   <dbl>  <dbl>
 1 0.985 0.0154
 2 0.985 0.0154
 3 0.985 0.0154
 4 0.985 0.0154
 5 0.985 0.0154
 6 0.985 0.0154
 7 0.985 0.0154
 8 0.985 0.0154
 9 0.985 0.0154
10 0.985 0.0154
# ℹ 739 more rows
test.rtree.prob <- test.rtree.prob[,2]                    # "Personal.Loan = yes"에 대한 예측 확률

ac  <- UB.ted$Personal.Loan                               # Test Dataset의 실제 class 
pp  <- as.numeric(test.rtree.prob)                        # 예측 확률을 수치형으로 변환

1) Package “pROC”

pacman::p_load("pROC")

rtree.roc  <- roc(ac, pp, plot = T, col = "gray")         # roc(실제 class, 예측 확률)
auc        <- round(auc(rtree.roc), 3)
legend("bottomright", legend = auc, bty = "n")

Caution! Package "pROC"를 통해 출력한 ROC 곡선은 다양한 함수를 이용해서 그래프를 수정할 수 있다.

# 함수 plot.roc() 이용
plot.roc(rtree.roc,   
         col="gray",                                      # Line Color
         print.auc = TRUE,                                # AUC 출력 여부
         print.auc.col = "red",                           # AUC 글씨 색깔
         print.thres = TRUE,                              # Cutoff Value 출력 여부
         print.thres.pch = 19,                            # Cutoff Value를 표시하는 도형 모양
         print.thres.col = "red",                         # Cutoff Value를 표시하는 도형의 색깔
         auc.polygon = TRUE,                              # 곡선 아래 면적에 대한 여부
         auc.polygon.col = "gray90")                      # 곡선 아래 면적의 색깔

# 함수 ggroc() 이용
ggroc(rtree.roc) +
annotate(geom = "text", x = 0.9, y = 1.0,
label = paste("AUC = ", auc),
size = 5,
color="red") +
theme_bw()

2) Package “Epi”

pacman::p_load("Epi")       
# install_version("etm", version = "1.1", repos = "http://cran.us.r-project.org")

ROC(pp, ac, plot = "ROC")                                 # ROC(예측 확률, 실제 class)  

3) Package “ROCR”

pacman::p_load("ROCR")

rtree.pred <- prediction(pp, ac)                          # prediction(예측 확률, 실제 class)  

rtree.perf <- performance(rtree.pred, "tpr", "fpr")       # performance(, "민감도", "1-특이도")                      
plot(rtree.perf, col = "gray")                            # ROC Curve

perf.auc   <- performance(rtree.pred, "auc")              # AUC
auc        <- attributes(perf.auc)$y.values
legend("bottomright", legend = auc, bty = "n")


7-3. 향상 차트

1) Package “ROCR”

rtree.perf <- performance(rtree.pred, "lift", "rpp")       # Lift Chart
plot(rtree.perf, main = "lift curve", 
     colorize = T,                                          # Coloring according to cutoff
     lwd = 2)  

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