1. Business Understanding

Industri perbankan menghadapi risiko kerugian akibat nasabah yang gagal melakukan pembayaran kewajiban kartu kredit. Kondisi ini dapat meningkatkan risiko kredit dan mempengaruhi stabilitas keuangan perusahaan. Oleh karena itu, diperlukan suatu model prediksi yang mampu mengidentifikasi nasabah yang berpotensi mengalami gagal bayar (default) pada periode berikutnya.

Penelitian ini bertujuan untuk membangun model klasifikasi menggunakan metode Decision Tree untuk memprediksi status default pembayaran kartu kredit pada bulan berikutnya berdasarkan karakteristik demografi, riwayat pembayaran, tagihan, dan jumlah pembayaran nasabah.

Pertanyaan yang ingin dijawab dalam analisis ini adalah:

Faktor-faktor apa yang berhubungan dengan risiko default pembayaran? Seberapa baik model Decision Tree dalam mengklasifikasikan nasabah yang berpotensi mengalami default? Bagaimana model dapat digunakan untuk membantu pengambilan keputusan kredit?

2. Data Understanding

Dataset yang digunakan adalah Default of Credit Card Clients Dataset yang diperoleh dari UCI Machine Learning Repository. Dataset ini berisi informasi mengenai pelanggan kartu kredit di Taiwan yang digunakan untuk memprediksi kemungkinan terjadinya gagal bayar pada bulan berikutnya.

Dataset terdiri atas:

30.000 observasi 23 variabel prediktor 1 variabel target (DEFAULT)

Variabel target terdiri atas:

0 = Tidak Default 1 = Default

library(readxl)
library(dplyr)
library(ggplot2)
library(rpart)
library(rpart.plot)
library(caret)
library(pROC)
data_raw <- read_excel("default of credit card clients.xls", skip = 1)

# Melihat struktur awal
str(data_raw)
## tibble [30,000 × 25] (S3: tbl_df/tbl/data.frame)
##  $ ID                        : num [1:30000] 1 2 3 4 5 6 7 8 9 10 ...
##  $ LIMIT_BAL                 : num [1:30000] 20000 120000 90000 50000 50000 50000 500000 100000 140000 20000 ...
##  $ SEX                       : num [1:30000] 2 2 2 2 1 1 1 2 2 1 ...
##  $ EDUCATION                 : num [1:30000] 2 2 2 2 2 1 1 2 3 3 ...
##  $ MARRIAGE                  : num [1:30000] 1 2 2 1 1 2 2 2 1 2 ...
##  $ AGE                       : num [1:30000] 24 26 34 37 57 37 29 23 28 35 ...
##  $ PAY_0                     : num [1:30000] 2 -1 0 0 -1 0 0 0 0 -2 ...
##  $ PAY_2                     : num [1:30000] 2 2 0 0 0 0 0 -1 0 -2 ...
##  $ PAY_3                     : num [1:30000] -1 0 0 0 -1 0 0 -1 2 -2 ...
##  $ PAY_4                     : num [1:30000] -1 0 0 0 0 0 0 0 0 -2 ...
##  $ PAY_5                     : num [1:30000] -2 0 0 0 0 0 0 0 0 -1 ...
##  $ PAY_6                     : num [1:30000] -2 2 0 0 0 0 0 -1 0 -1 ...
##  $ BILL_AMT1                 : num [1:30000] 3913 2682 29239 46990 8617 ...
##  $ BILL_AMT2                 : num [1:30000] 3102 1725 14027 48233 5670 ...
##  $ BILL_AMT3                 : num [1:30000] 689 2682 13559 49291 35835 ...
##  $ BILL_AMT4                 : num [1:30000] 0 3272 14331 28314 20940 ...
##  $ BILL_AMT5                 : num [1:30000] 0 3455 14948 28959 19146 ...
##  $ BILL_AMT6                 : num [1:30000] 0 3261 15549 29547 19131 ...
##  $ PAY_AMT1                  : num [1:30000] 0 0 1518 2000 2000 ...
##  $ PAY_AMT2                  : num [1:30000] 689 1000 1500 2019 36681 ...
##  $ PAY_AMT3                  : num [1:30000] 0 1000 1000 1200 10000 657 38000 0 432 0 ...
##  $ PAY_AMT4                  : num [1:30000] 0 1000 1000 1100 9000 ...
##  $ PAY_AMT5                  : num [1:30000] 0 0 1000 1069 689 ...
##  $ PAY_AMT6                  : num [1:30000] 0 2000 5000 1000 679 ...
##  $ default payment next month: num [1:30000] 1 1 0 0 0 0 0 0 0 0 ...
head(data_raw)
## # A tibble: 6 × 25
##      ID LIMIT_BAL   SEX EDUCATION MARRIAGE   AGE PAY_0 PAY_2 PAY_3 PAY_4 PAY_5
##   <dbl>     <dbl> <dbl>     <dbl>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1     1     20000     2         2        1    24     2     2    -1    -1    -2
## 2     2    120000     2         2        2    26    -1     2     0     0     0
## 3     3     90000     2         2        2    34     0     0     0     0     0
## 4     4     50000     2         2        1    37     0     0     0     0     0
## 5     5     50000     1         2        1    57    -1     0    -1     0     0
## 6     6     50000     1         1        2    37     0     0     0     0     0
## # ℹ 14 more variables: PAY_6 <dbl>, BILL_AMT1 <dbl>, BILL_AMT2 <dbl>,
## #   BILL_AMT3 <dbl>, BILL_AMT4 <dbl>, BILL_AMT5 <dbl>, BILL_AMT6 <dbl>,
## #   PAY_AMT1 <dbl>, PAY_AMT2 <dbl>, PAY_AMT3 <dbl>, PAY_AMT4 <dbl>,
## #   PAY_AMT5 <dbl>, PAY_AMT6 <dbl>, `default payment next month` <dbl>
summary(data_raw)
##        ID          LIMIT_BAL            SEX          EDUCATION    
##  Min.   :    1   Min.   :  10000   Min.   :1.000   Min.   :0.000  
##  1st Qu.: 7501   1st Qu.:  50000   1st Qu.:1.000   1st Qu.:1.000  
##  Median :15000   Median : 140000   Median :2.000   Median :2.000  
##  Mean   :15000   Mean   : 167484   Mean   :1.604   Mean   :1.853  
##  3rd Qu.:22500   3rd Qu.: 240000   3rd Qu.:2.000   3rd Qu.:2.000  
##  Max.   :30000   Max.   :1000000   Max.   :2.000   Max.   :6.000  
##     MARRIAGE          AGE            PAY_0             PAY_2        
##  Min.   :0.000   Min.   :21.00   Min.   :-2.0000   Min.   :-2.0000  
##  1st Qu.:1.000   1st Qu.:28.00   1st Qu.:-1.0000   1st Qu.:-1.0000  
##  Median :2.000   Median :34.00   Median : 0.0000   Median : 0.0000  
##  Mean   :1.552   Mean   :35.49   Mean   :-0.0167   Mean   :-0.1338  
##  3rd Qu.:2.000   3rd Qu.:41.00   3rd Qu.: 0.0000   3rd Qu.: 0.0000  
##  Max.   :3.000   Max.   :79.00   Max.   : 8.0000   Max.   : 8.0000  
##      PAY_3             PAY_4             PAY_5             PAY_6        
##  Min.   :-2.0000   Min.   :-2.0000   Min.   :-2.0000   Min.   :-2.0000  
##  1st Qu.:-1.0000   1st Qu.:-1.0000   1st Qu.:-1.0000   1st Qu.:-1.0000  
##  Median : 0.0000   Median : 0.0000   Median : 0.0000   Median : 0.0000  
##  Mean   :-0.1662   Mean   :-0.2207   Mean   :-0.2662   Mean   :-0.2911  
##  3rd Qu.: 0.0000   3rd Qu.: 0.0000   3rd Qu.: 0.0000   3rd Qu.: 0.0000  
##  Max.   : 8.0000   Max.   : 8.0000   Max.   : 8.0000   Max.   : 8.0000  
##    BILL_AMT1         BILL_AMT2        BILL_AMT3         BILL_AMT4      
##  Min.   :-165580   Min.   :-69777   Min.   :-157264   Min.   :-170000  
##  1st Qu.:   3559   1st Qu.:  2985   1st Qu.:   2666   1st Qu.:   2327  
##  Median :  22382   Median : 21200   Median :  20089   Median :  19052  
##  Mean   :  51223   Mean   : 49179   Mean   :  47013   Mean   :  43263  
##  3rd Qu.:  67091   3rd Qu.: 64006   3rd Qu.:  60165   3rd Qu.:  54506  
##  Max.   : 964511   Max.   :983931   Max.   :1664089   Max.   : 891586  
##    BILL_AMT5        BILL_AMT6          PAY_AMT1         PAY_AMT2      
##  Min.   :-81334   Min.   :-339603   Min.   :     0   Min.   :      0  
##  1st Qu.:  1763   1st Qu.:   1256   1st Qu.:  1000   1st Qu.:    833  
##  Median : 18105   Median :  17071   Median :  2100   Median :   2009  
##  Mean   : 40311   Mean   :  38872   Mean   :  5664   Mean   :   5921  
##  3rd Qu.: 50191   3rd Qu.:  49198   3rd Qu.:  5006   3rd Qu.:   5000  
##  Max.   :927171   Max.   : 961664   Max.   :873552   Max.   :1684259  
##     PAY_AMT3         PAY_AMT4         PAY_AMT5           PAY_AMT6       
##  Min.   :     0   Min.   :     0   Min.   :     0.0   Min.   :     0.0  
##  1st Qu.:   390   1st Qu.:   296   1st Qu.:   252.5   1st Qu.:   117.8  
##  Median :  1800   Median :  1500   Median :  1500.0   Median :  1500.0  
##  Mean   :  5226   Mean   :  4826   Mean   :  4799.4   Mean   :  5215.5  
##  3rd Qu.:  4505   3rd Qu.:  4013   3rd Qu.:  4031.5   3rd Qu.:  4000.0  
##  Max.   :896040   Max.   :621000   Max.   :426529.0   Max.   :528666.0  
##  default payment next month
##  Min.   :0.0000            
##  1st Qu.:0.0000            
##  Median :0.0000            
##  Mean   :0.2212            
##  3rd Qu.:0.0000            
##  Max.   :1.0000

2.1. Variabel Target

Variabel target adalah:

  • default payment next month
  • nilai 1 = default
  • nilai 0 = tidak default

2.2. Ringkasan Singkat Data

# Mengubah nama kolom agar lebih mudah dipanggil
names(data_raw) <- c(
  "ID", "LIMIT_BAL", "SEX", "EDUCATION", "MARRIAGE", "AGE",
  "PAY_0", "PAY_2", "PAY_3", "PAY_4", "PAY_5", "PAY_6",
  "BILL_AMT1", "BILL_AMT2", "BILL_AMT3", "BILL_AMT4", "BILL_AMT5", "BILL_AMT6",
  "PAY_AMT1", "PAY_AMT2", "PAY_AMT3", "PAY_AMT4", "PAY_AMT5", "PAY_AMT6",
  "DEFAULT"
)

# Ubah target menjadi factor
data_raw$DEFAULT <- as.factor(data_raw$DEFAULT)

# Cek proporsi kelas
table(data_raw$DEFAULT)
## 
##     0     1 
## 23364  6636
prop.table(table(data_raw$DEFAULT))
## 
##      0      1 
## 0.7788 0.2212

Hasil distribusi kelas menunjukkan bahwa 77,88% nasabah tidak mengalami default pembayaran, sedangkan 22,12% nasabah mengalami default. Dengan demikian, kelas tidak default merupakan kelas mayoritas dalam dataset.

2.3. Visualisasi sederhana

# Distribusi target
ggplot(data_raw, aes(x = DEFAULT)) +
  geom_bar() +
  labs(
    title = "Distribusi Default Pembayaran",
    x = "Default",
    y = "Jumlah"
  )

Perbedaan jumlah antara kelas default dan tidak default menunjukkan bahwa data didominasi oleh kelas tidak default. Informasi ini penting untuk diperhatikan dalam proses pembangunan dan evaluasi model klasifikasi.

3. Data Preparation / Preprocessing

Pada tahap ini dilakukan beberapa proses preprocessing untuk meningkatkan kualitas data sebelum digunakan dalam pemodelan.

Langkah-langkah yang dilakukan meliputi:

Menghapus variabel ID karena hanya berfungsi sebagai identitas unik dan tidak memiliki informasi prediktif. Mengubah variabel kategorik menjadi tipe factor agar dapat dikenali oleh algoritma klasifikasi. Membagi data menjadi data latih (training data) dan data uji (testing data).

Pembagian data dilakukan dengan proporsi 80:20 untuk memastikan model dapat dievaluasi pada data yang belum pernah dilihat sebelumnya.

data <- data_raw %>%
  select(-ID) %>%
  mutate(
    SEX = as.factor(SEX),
    EDUCATION = as.factor(EDUCATION),
    MARRIAGE = as.factor(MARRIAGE),
    DEFAULT = as.factor(DEFAULT)
  )

# Split data
set.seed(123)
index <- createDataPartition(data$DEFAULT, p = 0.8, list = FALSE)
train_data <- data[index, ]
test_data  <- data[-index, ]

dim(train_data)
## [1] 24001    24
dim(test_data)
## [1] 5999   24

4. Modeling

Pada penelitian ini digunakan algoritma Decision Tree karena memiliki beberapa keunggulan, yaitu:

mudah dipahami dan diinterpretasikan, mampu menangani hubungan non-linear, dapat mengidentifikasi variabel yang paling berpengaruh terhadap target.

Model dibangun menggunakan paket rpart dengan metode klasifikasi.

dt_model <- rpart(
  DEFAULT ~ .,
  data = train_data,
  method = "class",
  control = rpart.control(cp = 0.01)
)

dt_model
## n= 24001 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
## 1) root 24001 5309 0 (0.7788009 0.2211991)  
##   2) PAY_0< 1.5 21499 3572 0 (0.8338527 0.1661473) *
##   3) PAY_0>=1.5 2502  765 1 (0.3057554 0.6942446) *

Hasil pohon keputusan menunjukkan bahwa data sebanyak 24.001 observasi dibagi berdasarkan variabel PAY_0. Pada node akar, kelas yang paling dominan adalah tidak default (0) dengan proporsi 77,88%. Pada cabang PAY_0 < 1,5, sebagian besar nasabah diklasifikasikan sebagai tidak default (0) dengan proporsi 83,39%. Sementara itu, pada cabang PAY_0 ≥ 1,5, nasabah cenderung diklasifikasikan sebagai default (1) dengan proporsi 69,42%. Hal ini menunjukkan bahwa variabel PAY_0 merupakan penentu utama dalam model.

4.1. Visualisasi Pohon Keputusan

rpart.plot(
  dt_model,
  type = 2,
  extra = 104,
  fallen.leaves = TRUE,
  main = "Decision Tree: Default Pembayaran"
)

Gambar pohon keputusan menunjukkan bahwa variabel yang menjadi pemisah utama adalah PAY_0. Jika PAY_0 < 2, maka nasabah cenderung diklasifikasikan sebagai tidak default (0). Sebaliknya, jika PAY_0 ≥ 2, maka nasabah cenderung diklasifikasikan sebagai default (1). Hal ini menunjukkan bahwa status pembayaran bulan sebelumnya menjadi faktor utama dalam memprediksi default pembayaran.

5. Evaluation

5.1. Prediksi pada data uji

pred_class <- predict(dt_model, test_data, type = "class")
pred_prob  <- predict(dt_model, test_data, type = "prob")[, 2]

5.2. Confusion Matrix

conf_mat <- confusionMatrix(pred_class, test_data$DEFAULT, positive = "1")
conf_mat
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 4484  887
##          1  188  440
##                                           
##                Accuracy : 0.8208          
##                  95% CI : (0.8109, 0.8304)
##     No Information Rate : 0.7788          
##     P-Value [Acc > NIR] : 5.474e-16       
##                                           
##                   Kappa : 0.359           
##                                           
##  Mcnemar's Test P-Value : < 2.2e-16       
##                                           
##             Sensitivity : 0.33157         
##             Specificity : 0.95976         
##          Pos Pred Value : 0.70064         
##          Neg Pred Value : 0.83485         
##              Prevalence : 0.22120         
##          Detection Rate : 0.07335         
##    Detection Prevalence : 0.10468         
##       Balanced Accuracy : 0.64567         
##                                           
##        'Positive' Class : 1               
## 

Berdasarkan confusion matrix, model menghasilkan akurasi sebesar 82,08%. Nilai ini menunjukkan bahwa model cukup baik dalam mengklasifikasikan data secara keseluruhan. Namun, sensitivity sebesar 33,16% menunjukkan bahwa model masih kurang baik dalam mendeteksi nasabah yang mengalami default. Sementara itu, specificity sebesar 95,98% menunjukkan bahwa model sangat baik dalam mengidentifikasi nasabah yang tidak default. Hal ini berarti model cenderung lebih tepat dalam memprediksi kelas tidak default dibandingkan kelas default.

5.3. AUC ROC

roc_obj <- roc(response = test_data$DEFAULT, predictor = pred_prob, levels = c("0", "1"), direction = "<")
auc(roc_obj)
## Area under the curve: 0.6457
plot(roc_obj, main = "ROC Curve - Decision Tree")

Kurva ROC berada di atas garis diagonal, sehingga model memiliki kemampuan klasifikasi. Namun, karena kurva belum jauh dari diagonal, performa model masih tergolong sedang.

5.4. Interpretasi hasil

accuracy <- conf_mat$overall["Accuracy"]
sensitivity <- conf_mat$byClass["Sensitivity"]
specificity <- conf_mat$byClass["Specificity"]
precision <- conf_mat$byClass["Precision"]
recall <- conf_mat$byClass["Recall"]

evaluation_result <- data.frame(
  Metrik = c("Accuracy", "Sensitivity", "Specificity", "Precision", "Recall", "AUC"),
  Nilai = c(
    as.numeric(accuracy),
    as.numeric(sensitivity),
    as.numeric(specificity),
    as.numeric(precision),
    as.numeric(recall),
    as.numeric(auc(roc_obj))
  )
)

evaluation_result
##        Metrik     Nilai
## 1    Accuracy 0.8208035
## 2 Sensitivity 0.3315750
## 3 Specificity 0.9597603
## 4   Precision 0.7006369
## 5      Recall 0.3315750
## 6         AUC 0.6456676

Model menghasilkan accuracy 82,08% dengan precision 70,06%, specificity 95,98%, dan AUC 0,646. Namun, sensitivity/recall yang hanya 33,16% menunjukkan bahwa model masih kurang baik dalam mendeteksi nasabah default.

6. Prediction on New Data

Contoh prediksi untuk satu nasabah baru.

new_client <- data.frame(
  LIMIT_BAL = 20000,
  SEX = factor(2, levels = levels(data$SEX)),
  EDUCATION = factor(2, levels = levels(data$EDUCATION)),
  MARRIAGE = factor(1, levels = levels(data$MARRIAGE)),
  AGE = 28,
  PAY_0 = 0,
  PAY_2 = 0,
  PAY_3 = 0,
  PAY_4 = 0,
  PAY_5 = 0,
  PAY_6 = 0,
  BILL_AMT1 = 15000,
  BILL_AMT2 = 14500,
  BILL_AMT3 = 14000,
  BILL_AMT4 = 13500,
  BILL_AMT5 = 13000,
  BILL_AMT6 = 12500,
  PAY_AMT1 = 2000,
  PAY_AMT2 = 2000,
  PAY_AMT3 = 1500,
  PAY_AMT4 = 1500,
  PAY_AMT5 = 1000,
  PAY_AMT6 = 1000
)

new_pred_class <- predict(dt_model, new_client, type = "class")
new_pred_prob <- predict(dt_model, new_client, type = "prob")

new_pred_class
## 1 
## 0 
## Levels: 0 1
new_pred_prob
##           0         1
## 1 0.8338527 0.1661473

7. Conclusion

Berdasarkan analisis menggunakan tahapan CRISP-DM, model Decision Tree berhasil dibangun untuk memprediksi default pembayaran kartu kredit. Hasil evaluasi menunjukkan bahwa model memiliki accuracy sebesar 82,08% dengan AUC sebesar 0,646, sehingga kemampuan klasifikasinya tergolong sedang. Model lebih baik dalam mengidentifikasi nasabah yang tidak default dibandingkan nasabah yang default, terlihat dari specificity yang tinggi namun sensitivity yang masih rendah. Hasil prediksi pada data baru juga menunjukkan bahwa nasabah tersebut termasuk kategori tidak default dengan probabilitas 83,39%. Dengan demikian, model dapat digunakan sebagai alat bantu awal dalam mengidentifikasi risiko gagal bayar nasabah.