# ============================================================
# LANGKAH 1 — LOAD PACKAGE
# ============================================================

library(readxl)
## Warning: package 'readxl' was built under R version 4.4.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(rpart)
## Warning: package 'rpart' was built under R version 4.4.3
library(rpart.plot)
## Warning: package 'rpart.plot' was built under R version 4.4.3
library(caret)
## Warning: package 'caret' was built under R version 4.4.3
## Loading required package: ggplot2
## Warning: package 'ggplot2' was built under R version 4.4.3
## Loading required package: lattice
## Warning: package 'lattice' was built under R version 4.4.3
## New names:
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...6`
## • `` -> `...7`
## • `` -> `...8`
## • `` -> `...9`
## • `` -> `...10`
## • `` -> `...11`
## • `` -> `...12`
## • `` -> `...13`
## • `` -> `...14`
## • `` -> `...15`
## • `` -> `...16`
## • `` -> `...17`
## • `` -> `...18`
## Jumlah data awal : 897
# ============================================================
# LANGKAH 3 — CEK NAMA KOLOM
# ============================================================

names(data_awal)
##  [1] "2023"  "...2"  "...3"  "...4"  "...5"  "...6"  "...7"  "...8"  "...9" 
## [10] "...10" "...11" "...12" "...13" "...14" "...15" "...16" "...17" "...18"
# ============================================================
# LANGKAH 4 — CEK STRUKTUR DATA
# ============================================================

str(data_awal)
## tibble [897 × 18] (S3: tbl_df/tbl/data.frame)
##  $ 2023 : chr [1:897] "JANUARI" "No" "1" "2" ...
##  $ ...2 : chr [1:897] NA "Nama Sales" "DHEVY" "MUS MAULANA" ...
##  $ ...3 : chr [1:897] NA "Nama Debitur" "RESTU BUDIONO" "ABDUL CHAFID" ...
##  $ ...4 : chr [1:897] NA "Tanggal Masuk Berkas" "44921" "44922" ...
##  $ ...5 : chr [1:897] NA "Tanggal di Analisis" "44921" "44922" ...
##  $ ...6 : chr [1:897] NA "Segmen FIX/ Non Fix" "FIX INCOME" "FIX INCOME" ...
##  $ ...7 : chr [1:897] NA "Produk" "BNI GRIYA" "BNI GRIYA" ...
##  $ ...8 : chr [1:897] NA "Maximum Permohonan" "450000000" "1000000000" ...
##  $ ...9 : chr [1:897] NA "Permohonan Jangka Waktu" "120" "180" ...
##  $ ...10: chr [1:897] NA "Instansi Bekrja" "NES GLOBAL" "TOP UP" ...
##  $ ...11: chr [1:897] NA "Analis" "ERIKA" "KIRMAN" ...
##  $ ...12: chr [1:897] NA "TGL USUL/TGL KELUAR" "44928" "44929" ...
##  $ ...13: chr [1:897] NA "Nominal Keputusan" "425000000" "1000000000" ...
##  $ ...14: chr [1:897] NA "Jangka Waktu Keputusan" "140" "120" ...
##  $ ...15: chr [1:897] NA "Hasil Keputusan" "USUL" "BACK TO SALES" ...
##  $ ...16: chr [1:897] NA "Keterangan/ Alasan" "LANJUT KE PBP" "LAINNYA" ...
##  $ ...17: chr [1:897] NA "SLA" "5" "5" ...
##  $ ...18: chr [1:897] NA "Usulan" "OKE LANJUT" "PENGHASILAN BLM LAYAK DIAKUI" ...
# ============================================================
# LANGKAH 5 — CEK ISI DATA
# ============================================================

head(data_awal, 5)
## # A tibble: 5 × 18
##   `2023` ...2  ...3  ...4  ...5  ...6  ...7  ...8  ...9  ...10 ...11 ...12 ...13
##   <chr>  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 JANUA… <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA> 
## 2 No     Nama… Nama… Tang… Tang… Segm… Prod… Maxi… Perm… Inst… Anal… TGL … Nomi…
## 3 1      DHEVY REST… 44921 44921 FIX … BNI … 4500… 120   NES … ERIKA 44928 4250…
## 4 2      MUS … ABDU… 44922 44922 FIX … BNI … 1000… 180   TOP … KIRM… 44929 1000…
## 5 3      ERLI… EDDY… 44923 44923 FIX … BNI … 4000… 120   TOP … ERIKA 44929 3510…
## # ℹ 5 more variables: ...14 <chr>, ...15 <chr>, ...16 <chr>, ...17 <chr>,
## #   ...18 <chr>
# ============================================================
# LANGKAH 6 — AMBIL HEADER ASLI
# ============================================================

header <- as.character(data_awal[2, ])

print(header)
##  [1] "No"                      "Nama Sales"             
##  [3] "Nama Debitur"            "Tanggal Masuk Berkas"   
##  [5] "Tanggal di Analisis"     "Segmen FIX/ Non Fix"    
##  [7] "Produk"                  "Maximum Permohonan"     
##  [9] "Permohonan Jangka Waktu" "Instansi Bekrja"        
## [11] "Analis"                  "TGL USUL/TGL KELUAR"    
## [13] "Nominal Keputusan"       "Jangka Waktu Keputusan" 
## [15] "Hasil Keputusan"         "Keterangan/ Alasan"     
## [17] "SLA"                     "Usulan"
# ============================================================
# LANGKAH 7 — BUANG BARIS JUDUL DAN HEADER
# ============================================================

data_model <- data_awal[-c(1, 2), ]

cat("Jumlah data :", nrow(data_model), "\n")
## Jumlah data : 895
# ============================================================
# LANGKAH 8 — PASANG NAMA KOLOM
# ============================================================

names(data_model) <- make.names(header, unique = TRUE)

print(names(data_model))
##  [1] "No"                      "Nama.Sales"             
##  [3] "Nama.Debitur"            "Tanggal.Masuk.Berkas"   
##  [5] "Tanggal.di.Analisis"     "Segmen.FIX..Non.Fix"    
##  [7] "Produk"                  "Maximum.Permohonan"     
##  [9] "Permohonan.Jangka.Waktu" "Instansi.Bekrja"        
## [11] "Analis"                  "TGL.USUL.TGL.KELUAR"    
## [13] "Nominal.Keputusan"       "Jangka.Waktu.Keputusan" 
## [15] "Hasil.Keputusan"         "Keterangan..Alasan"     
## [17] "SLA"                     "Usulan"
# ============================================================
# LANGKAH 9 — CEK HASIL KEPUTUSAN
# ============================================================

cat("=== Hasil Keputusan ===\n")
## === Hasil Keputusan ===
print(table(data_model$Hasil.Keputusan, useNA = "ifany"))
## 
##   BACK TO SALES  CANCEL BY BANK Hasil Keputusan          REJECT            USUL 
##             123              70              11               1             668 
##            <NA> 
##              22
# ============================================================
# LANGKAH 10 — BENTUK TARGET KEPUTUSAN
# ============================================================

data_model$Keputusan <- ifelse(
  data_model$Hasil.Keputusan == "USUL",
  "USUL",
  "NON_USUL"
)

data_model$Keputusan <- factor(
  data_model$Keputusan,
  levels = c("NON_USUL", "USUL")
)
# ============================================================
# LANGKAH 11 — CEK DISTRIBUSI TARGET
# ============================================================

cat("Jumlah data :", nrow(data_model), "\n")
## Jumlah data : 895
cat("\nDistribusi Keputusan:\n")
## 
## Distribusi Keputusan:
print(table(data_model$Keputusan))
## 
## NON_USUL     USUL 
##      205      668
cat("\nProporsi Keputusan:\n")
## 
## Proporsi Keputusan:
print(round(prop.table(table(data_model$Keputusan)) * 100, 2))
## 
## NON_USUL     USUL 
##    23.48    76.52
# ============================================================
# LANGKAH 12 — CEK NA PADA TARGET
# ============================================================

cat("Jumlah NA Keputusan :", sum(is.na(data_model$Keputusan)), "\n")
## Jumlah NA Keputusan : 22
# ============================================================
# LANGKAH 13 — HAPUS DATA NA TARGET
# ============================================================

data_model <- data_model %>%
  filter(!is.na(Keputusan))

cat("Jumlah data setelah hapus NA target :", nrow(data_model), "\n")
## Jumlah data setelah hapus NA target : 873
# ============================================================
# LANGKAH 14 — CEK ULANG DISTRIBUSI TARGET
# ============================================================

cat("=== DISTRIBUSI TARGET ===\n")
## === DISTRIBUSI TARGET ===
print(table(data_model$Keputusan))
## 
## NON_USUL     USUL 
##      205      668
cat("\n=== PROPORSI TARGET ===\n")
## 
## === PROPORSI TARGET ===
print(round(prop.table(table(data_model$Keputusan)) * 100, 2))
## 
## NON_USUL     USUL 
##    23.48    76.52
cat("\n=== CEK NA ===\n")
## 
## === CEK NA ===
cat("NA :", sum(is.na(data_model$Keputusan)), "\n")
## NA : 0
# ============================================================
# LANGKAH 12 — HAPUS DATA TANPA HASIL KEPUTUSAN
# ============================================================

data_model <- data_model %>%
  filter(!is.na(Hasil.Keputusan))

cat("Jumlah data setelah hapus NA target :", nrow(data_model), "\n")
## Jumlah data setelah hapus NA target : 873
cat("\nDistribusi Keputusan:\n")
## 
## Distribusi Keputusan:
print(table(data_model$Keputusan))
## 
## NON_USUL     USUL 
##      205      668
cat("\nProporsi Keputusan:\n")
## 
## Proporsi Keputusan:
print(round(prop.table(table(data_model$Keputusan)) * 100, 2))
## 
## NON_USUL     USUL 
##    23.48    76.52
# ============================================================
# LANGKAH 13 — RAPIKAN NAMA VARIABEL
# ============================================================

data_model <- data_model %>%
  rename(
    Segmen = Segmen.FIX..Non.Fix,
    Produk = Produk,
    Maksimum_Permohonan = Maximum.Permohonan,
    Jangka_Waktu = Permohonan.Jangka.Waktu,
    Instansi = Instansi.Bekrja,
    Nominal_Keputusan = Nominal.Keputusan,
    Jangka_Waktu_Keputusan = Jangka.Waktu.Keputusan,
    SLA = SLA
  )

print(names(data_model))
##  [1] "No"                     "Nama.Sales"             "Nama.Debitur"          
##  [4] "Tanggal.Masuk.Berkas"   "Tanggal.di.Analisis"    "Segmen"                
##  [7] "Produk"                 "Maksimum_Permohonan"    "Jangka_Waktu"          
## [10] "Instansi"               "Analis"                 "TGL.USUL.TGL.KELUAR"   
## [13] "Nominal_Keputusan"      "Jangka_Waktu_Keputusan" "Hasil.Keputusan"       
## [16] "Keterangan..Alasan"     "SLA"                    "Usulan"                
## [19] "Keputusan"
# ============================================================
# LANGKAH 14 — CEK TIPE DATA
# ============================================================

str(
  data_model %>%
    select(
      Keputusan,
      Segmen,
      Produk,
      Maksimum_Permohonan,
      Jangka_Waktu,
      Instansi,
      Nominal_Keputusan,
      Jangka_Waktu_Keputusan,
      SLA
    )
)
## tibble [873 × 9] (S3: tbl_df/tbl/data.frame)
##  $ Keputusan             : Factor w/ 2 levels "NON_USUL","USUL": 2 1 2 2 1 1 1 2 1 1 ...
##  $ Segmen                : chr [1:873] "FIX INCOME" "FIX INCOME" "FIX INCOME" "FIX INCOME" ...
##  $ Produk                : chr [1:873] "BNI GRIYA" "BNI GRIYA" "BNI GRIYA" "BNI GRIYA" ...
##  $ Maksimum_Permohonan   : chr [1:873] "450000000" "1000000000" "400000000" "525000000" ...
##  $ Jangka_Waktu          : chr [1:873] "120" "180" "120" "120" ...
##  $ Instansi              : chr [1:873] "NES GLOBAL" "TOP UP" "TOP UP" "TOP UP" ...
##  $ Nominal_Keputusan     : chr [1:873] "425000000" "1000000000" "351000000" "525000000" ...
##  $ Jangka_Waktu_Keputusan: chr [1:873] "140" "120" "120" "180" ...
##  $ SLA                   : chr [1:873] "5" "5" "6" "0" ...
# ============================================================
# LANGKAH 15 — KONVERSI VARIABEL NUMERIK
# ============================================================

data_model <- data_model %>%
  mutate(
    Maksimum_Permohonan = as.numeric(Maksimum_Permohonan),
    Jangka_Waktu = as.numeric(Jangka_Waktu),
    Nominal_Keputusan = as.numeric(Nominal_Keputusan),
    Jangka_Waktu_Keputusan = as.numeric(Jangka_Waktu_Keputusan),
    SLA = as.numeric(SLA)
  )
## Warning: There were 5 warnings in `mutate()`.
## The first warning was:
## ℹ In argument: `Maksimum_Permohonan = as.numeric(Maksimum_Permohonan)`.
## Caused by warning:
## ! NAs introduced by coercion
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 4 remaining warnings.
# CEK
str(
  data_model %>%
    select(
      Maksimum_Permohonan,
      Jangka_Waktu,
      Nominal_Keputusan,
      Jangka_Waktu_Keputusan,
      SLA
    )
)
## tibble [873 × 5] (S3: tbl_df/tbl/data.frame)
##  $ Maksimum_Permohonan   : num [1:873] 4.50e+08 1.00e+09 4.00e+08 5.25e+08 8.29e+08 ...
##  $ Jangka_Waktu          : num [1:873] 120 180 120 120 120 120 180 180 36 36 ...
##  $ Nominal_Keputusan     : num [1:873] 4.25e+08 1.00e+09 3.51e+08 5.25e+08 8.29e+08 ...
##  $ Jangka_Waktu_Keputusan: num [1:873] 140 120 120 180 180 120 180 140 36 36 ...
##  $ SLA                   : num [1:873] 5 5 6 0 4 1 3 1 1 2 ...
# ============================================================
# LANGKAH 16 — CEK NA VARIABEL NUMERIK
# ============================================================

cat("NA Maksimum_Permohonan       :", sum(is.na(data_model$Maksimum_Permohonan)), "\n")
## NA Maksimum_Permohonan       : 11
cat("NA Jangka_Waktu              :", sum(is.na(data_model$Jangka_Waktu)), "\n")
## NA Jangka_Waktu              : 11
cat("NA Nominal_Keputusan         :", sum(is.na(data_model$Nominal_Keputusan)), "\n")
## NA Nominal_Keputusan         : 11
cat("NA Jangka_Waktu_Keputusan    :", sum(is.na(data_model$Jangka_Waktu_Keputusan)), "\n")
## NA Jangka_Waktu_Keputusan    : 11
cat("NA SLA                       :", sum(is.na(data_model$SLA)), "\n")
## NA SLA                       : 11
# ============================================================
# LANGKAH 17 — CEK BARIS YANG MENGANDUNG NA
# ============================================================

data_model %>%
  filter(
    is.na(Maksimum_Permohonan) |
    is.na(Jangka_Waktu) |
    is.na(Nominal_Keputusan) |
    is.na(Jangka_Waktu_Keputusan) |
    is.na(SLA)
  ) %>%
  select(
    No,
    Nama.Debitur,
    Segmen,
    Produk,
    Maksimum_Permohonan,
    Jangka_Waktu,
    Instansi,
    Nominal_Keputusan,
    Jangka_Waktu_Keputusan,
    SLA,
    Hasil.Keputusan,
    Keputusan
  )
## # A tibble: 11 × 12
##    No    Nama.Debitur Segmen    Produk Maksimum_Permohonan Jangka_Waktu Instansi
##    <chr> <chr>        <chr>     <chr>                <dbl>        <dbl> <chr>   
##  1 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
##  2 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
##  3 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
##  4 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
##  5 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
##  6 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
##  7 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
##  8 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
##  9 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
## 10 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
## 11 No    Nama Debitur Segmen F… Produk                  NA           NA Instans…
## # ℹ 5 more variables: Nominal_Keputusan <dbl>, Jangka_Waktu_Keputusan <dbl>,
## #   SLA <dbl>, Hasil.Keputusan <chr>, Keputusan <fct>
# ============================================================
# LANGKAH 18 — HAPUS BARIS HEADER YANG NYASAR
# ============================================================

data_model <- data_model %>%
  filter(No != "No")

cat("Jumlah data setelah menghapus header nyasar :", nrow(data_model), "\n")
## Jumlah data setelah menghapus header nyasar : 862
# ============================================================
# LANGKAH 19 — CEK ULANG NA
# ============================================================

cat("NA Maksimum_Permohonan    :", sum(is.na(data_model$Maksimum_Permohonan)), "\n")
## NA Maksimum_Permohonan    : 0
cat("NA Jangka_Waktu           :", sum(is.na(data_model$Jangka_Waktu)), "\n")
## NA Jangka_Waktu           : 0
cat("NA Nominal_Keputusan      :", sum(is.na(data_model$Nominal_Keputusan)), "\n")
## NA Nominal_Keputusan      : 0
cat("NA Jangka_Waktu_Keputusan :", sum(is.na(data_model$Jangka_Waktu_Keputusan)), "\n")
## NA Jangka_Waktu_Keputusan : 0
cat("NA SLA                    :", sum(is.na(data_model$SLA)), "\n")
## NA SLA                    : 0
# ============================================================
# LANGKAH 20 — CEK DATA SETELAH PEMBERSIHAN
# ============================================================

cat("Jumlah data :", nrow(data_model), "\n")
## Jumlah data : 862
cat("\nDistribusi Keputusan:\n")
## 
## Distribusi Keputusan:
print(table(data_model$Keputusan))
## 
## NON_USUL     USUL 
##      194      668
cat("\nProporsi Keputusan:\n")
## 
## Proporsi Keputusan:
print(round(prop.table(table(data_model$Keputusan)) * 100, 2))
## 
## NON_USUL     USUL 
##    22.51    77.49
# ============================================================
# LANGKAH 21 — CEK NA PREDIKTOR MODEL
# ============================================================

cat("=== CEK NA PREDIKTOR ===\n")
## === CEK NA PREDIKTOR ===
print(
  sapply(
    data_model %>%
      select(
        Keputusan,
        Segmen,
        Produk,
        Maksimum_Permohonan,
        Jangka_Waktu,
        Nominal_Keputusan,
        Jangka_Waktu_Keputusan,
        SLA
      ),
    function(x) sum(is.na(x))
  )
)
##              Keputusan                 Segmen                 Produk 
##                      0                      0                      0 
##    Maksimum_Permohonan           Jangka_Waktu      Nominal_Keputusan 
##                      0                      0                      0 
## Jangka_Waktu_Keputusan                    SLA 
##                      0                      0
# ============================================================
# LANGKAH 22 — CEK KATEGORI PREDIKTOR
# ============================================================

cat("=== SEGMEN ===\n")
## === SEGMEN ===
print(table(data_model$Segmen, useNA = "ifany"))
## 
##     FIX INCOME NON FIX INCOME 
##            828             34
cat("\n=== PRODUK ===\n")
## 
## === PRODUK ===
print(table(data_model$Produk, useNA = "ifany"))
## 
##          BNI FLEKSI  BNI FLEKSI PENSIUN           BNI GRIYA BNI GRIYA MULTIGUNA 
##                 528                   3                 320                  11
# ============================================================
# LANGKAH 23 — CEK VARIABEL NUMERIK
# ============================================================

summary(
  data_model %>%
    select(
      Maksimum_Permohonan,
      Jangka_Waktu,
      Nominal_Keputusan,
      Jangka_Waktu_Keputusan,
      SLA
    )
)
##  Maksimum_Permohonan  Jangka_Waktu   Nominal_Keputusan   Jangka_Waktu_Keputusan
##  Min.   :1.000e+07   Min.   : 12.0   Min.   :1.000e+07   Min.   : 12.00        
##  1st Qu.:8.000e+07   1st Qu.: 48.0   1st Qu.:7.500e+07   1st Qu.: 48.00        
##  Median :1.800e+08   Median : 72.0   Median :1.712e+08   Median : 75.00        
##  Mean   :3.512e+08   Mean   : 98.2   Mean   :3.487e+08   Mean   : 99.98        
##  3rd Qu.:4.000e+08   3rd Qu.:144.0   3rd Qu.:4.000e+08   3rd Qu.:177.00        
##  Max.   :6.000e+09   Max.   :240.0   Max.   :6.000e+09   Max.   :300.00        
##       SLA        
##  Min.   :-3.000  
##  1st Qu.: 1.000  
##  Median : 2.000  
##  Mean   : 2.799  
##  3rd Qu.: 4.000  
##  Max.   :25.000
# ============================================================
# LANGKAH 24 — CEK DUPLIKASI
# ============================================================

cat("Jumlah baris duplikat :", sum(duplicated(data_model)), "\n")
## Jumlah baris duplikat : 0
# ============================================================
# LANGKAH 25 — TRAIN-TEST SPLIT 80:20
# ============================================================

library(caret)

set.seed(123)

index_train <- createDataPartition(
  data_model$Keputusan,
  p = 0.80,
  list = FALSE
)

train_data <- data_model[index_train, ]
test_data  <- data_model[-index_train, ]

cat("Jumlah data total :", nrow(data_model), "\n")
## Jumlah data total : 862
cat("Jumlah data train :", nrow(train_data), "\n")
## Jumlah data train : 691
cat("Jumlah data test  :", nrow(test_data), "\n")
## Jumlah data test  : 171
cat("\nProporsi target TRAIN:\n")
## 
## Proporsi target TRAIN:
print(round(
  prop.table(table(train_data$Keputusan)) * 100,
  2
))
## 
## NON_USUL     USUL 
##    22.58    77.42
cat("\nProporsi target TEST:\n")
## 
## Proporsi target TEST:
print(round(
  prop.table(table(test_data$Keputusan)) * 100,
  2
))
## 
## NON_USUL     USUL 
##    22.22    77.78
# ============================================================
# LANGKAH 26 — FIT CART PADA DATA TRAIN
# ============================================================

library(rpart)

model_cart <- rpart(
  Keputusan ~
    Segmen +
    Produk +
    Maksimum_Permohonan +
    Jangka_Waktu +
    Nominal_Keputusan +
    Jangka_Waktu_Keputusan +
    SLA,
  data = train_data,
  method = "class",
  control = rpart.control(
    cp = 0.001,
    minsplit = 20,
    minbucket = 10,
    maxdepth = 5
  )
)

print(model_cart)
## n= 691 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 691 156 USUL (0.2257598 0.7742402)  
##    2) Segmen=NON FIX INCOME 28  13 USUL (0.4642857 0.5357143)  
##      4) Maksimum_Permohonan< 1.45e+09 18   8 NON_USUL (0.5555556 0.4444444) *
##      5) Maksimum_Permohonan>=1.45e+09 10   3 USUL (0.3000000 0.7000000) *
##    3) Segmen=FIX INCOME 663 143 USUL (0.2156863 0.7843137)  
##      6) Jangka_Waktu_Keputusan< 39 167  46 USUL (0.2754491 0.7245509)  
##       12) Nominal_Keputusan>=6.75e+07 47  18 USUL (0.3829787 0.6170213)  
##         24) Maksimum_Permohonan< 9.5e+07 14   6 NON_USUL (0.5714286 0.4285714) *
##         25) Maksimum_Permohonan>=9.5e+07 33  10 USUL (0.3030303 0.6969697) *
##       13) Nominal_Keputusan< 6.75e+07 120  28 USUL (0.2333333 0.7666667) *
##      7) Jangka_Waktu_Keputusan>=39 496  97 USUL (0.1955645 0.8044355)  
##       14) Nominal_Keputusan>=4.1528e+08 123  33 USUL (0.2682927 0.7317073)  
##         28) SLA>=2.5 56  22 USUL (0.3928571 0.6071429)  
##           56) Jangka_Waktu_Keputusan< 150 23   9 NON_USUL (0.6086957 0.3913043) *
##           57) Jangka_Waktu_Keputusan>=150 33   8 USUL (0.2424242 0.7575758) *
##         29) SLA< 2.5 67  11 USUL (0.1641791 0.8358209) *
##       15) Nominal_Keputusan< 4.1528e+08 373  64 USUL (0.1715818 0.8284182) *
# ============================================================
# LANGKAH 27 — CEK HASIL CART
# ============================================================

cat("=== CP TABLE ===\n")
## === CP TABLE ===
print(model_cart$cptable)
##            CP nsplit rel error   xerror       xstd
## 1 0.007478632      0 1.0000000 1.000000 0.07044912
## 2 0.006410256      6 0.9551282 1.044872 0.07153970
## 3 0.001000000      8 0.9423077 1.076923 0.07228395
cat("\n=== VARIABLE IMPORTANCE ===\n")
## 
## === VARIABLE IMPORTANCE ===
print(model_cart$variable.importance)
##      Nominal_Keputusan    Maksimum_Permohonan Jangka_Waktu_Keputusan 
##              7.3014764              7.0055717              5.5411224 
##                    SLA                 Segmen           Jangka_Waktu 
##              3.4324167              3.3206553              3.1688214 
##                 Produk 
##              0.5634858
cat("\n=== JUMLAH SPLIT ===\n")
## 
## === JUMLAH SPLIT ===
cat(nrow(model_cart$splits), "\n")
## 64
# ============================================================
# LANGKAH 28 — CP 1-SE RULE
# ============================================================

cp_table <- model_cart$cptable

min_xerror <- min(cp_table[, "xerror"])

se_min <- cp_table[
  which.min(cp_table[, "xerror"]),
  "xstd"
]

cp_1se <- cp_table[
  which(
    cp_table[, "xerror"] <= min_xerror + se_min
  )[1],
  "CP"
]

cat("Minimum xerror :", min_xerror, "\n")
## Minimum xerror : 1
cat("SE minimum     :", se_min, "\n")
## SE minimum     : 0.07044912
cat("CP 1-SE        :", cp_1se, "\n")
## CP 1-SE        : 0.007478632
# ============================================================
# LANGKAH 29 — PRUNING CART
# ============================================================

model_cart_pruned <- prune(
  model_cart,
  cp = cp_1se
)

print(model_cart_pruned)
## n= 691 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
## 1) root 691 156 USUL (0.2257598 0.7742402) *
cat("\nJumlah split setelah pruning :",
    nrow(model_cart_pruned$splits), "\n")
## 
## Jumlah split setelah pruning : 0
# ============================================================
# LANGKAH 28 — COBA CP YANG LEBIH KECIL
# ============================================================

cp_test <- 0.006

model_cart_pruned <- prune(
  model_cart,
  cp = cp_test
)

print(model_cart_pruned)
## n= 691 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 691 156 USUL (0.2257598 0.7742402)  
##    2) Segmen=NON FIX INCOME 28  13 USUL (0.4642857 0.5357143)  
##      4) Maksimum_Permohonan< 1.45e+09 18   8 NON_USUL (0.5555556 0.4444444) *
##      5) Maksimum_Permohonan>=1.45e+09 10   3 USUL (0.3000000 0.7000000) *
##    3) Segmen=FIX INCOME 663 143 USUL (0.2156863 0.7843137)  
##      6) Jangka_Waktu_Keputusan< 39 167  46 USUL (0.2754491 0.7245509)  
##       12) Nominal_Keputusan>=6.75e+07 47  18 USUL (0.3829787 0.6170213)  
##         24) Maksimum_Permohonan< 9.5e+07 14   6 NON_USUL (0.5714286 0.4285714) *
##         25) Maksimum_Permohonan>=9.5e+07 33  10 USUL (0.3030303 0.6969697) *
##       13) Nominal_Keputusan< 6.75e+07 120  28 USUL (0.2333333 0.7666667) *
##      7) Jangka_Waktu_Keputusan>=39 496  97 USUL (0.1955645 0.8044355)  
##       14) Nominal_Keputusan>=4.1528e+08 123  33 USUL (0.2682927 0.7317073)  
##         28) SLA>=2.5 56  22 USUL (0.3928571 0.6071429)  
##           56) Jangka_Waktu_Keputusan< 150 23   9 NON_USUL (0.6086957 0.3913043) *
##           57) Jangka_Waktu_Keputusan>=150 33   8 USUL (0.2424242 0.7575758) *
##         29) SLA< 2.5 67  11 USUL (0.1641791 0.8358209) *
##       15) Nominal_Keputusan< 4.1528e+08 373  64 USUL (0.1715818 0.8284182) *
cat("\nJumlah split setelah pruning :",
    nrow(model_cart_pruned$splits), "\n")
## 
## Jumlah split setelah pruning : 64
# ============================================================
# LANGKAH 29 — PREDIKSI CART PADA DATA TEST
# ============================================================

pred_test <- predict(
  model_cart_pruned,
  newdata = test_data,
  type = "class"
)

cat("=== DISTRIBUSI PREDIKSI TEST ===\n")
## === DISTRIBUSI PREDIKSI TEST ===
print(table(pred_test))
## pred_test
## NON_USUL     USUL 
##       14      157
cat("\n=== DISTRIBUSI AKTUAL TEST ===\n")
## 
## === DISTRIBUSI AKTUAL TEST ===
print(table(test_data$Keputusan))
## 
## NON_USUL     USUL 
##       38      133
cat("\n=== CONFUSION MATRIX ===\n")
## 
## === CONFUSION MATRIX ===
library(caret)

cm_cart <- confusionMatrix(
  pred_test,
  test_data$Keputusan,
  positive = "USUL"
)

print(cm_cart)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction NON_USUL USUL
##   NON_USUL        3   11
##   USUL           35  122
##                                          
##                Accuracy : 0.731          
##                  95% CI : (0.658, 0.7958)
##     No Information Rate : 0.7778         
##     P-Value [Acc > NIR] : 0.938460       
##                                          
##                   Kappa : -0.0049        
##                                          
##  Mcnemar's Test P-Value : 0.000696       
##                                          
##             Sensitivity : 0.91729        
##             Specificity : 0.07895        
##          Pos Pred Value : 0.77707        
##          Neg Pred Value : 0.21429        
##              Prevalence : 0.77778        
##          Detection Rate : 0.71345        
##    Detection Prevalence : 0.91813        
##       Balanced Accuracy : 0.49812        
##                                          
##        'Positive' Class : USUL           
## 

SMOTE

library(smotefamily)
## Warning: package 'smotefamily' was built under R version 4.4.3
# ============================================================
# LANGKAH 29 — CEK PACKAGE SMOTE
# ============================================================

library(smotefamily)

cat("Package smotefamily berhasil dipanggil.\n")
## Package smotefamily berhasil dipanggil.
# ============================================================
# LANGKAH 30 — SIAPKAN DATA TRAIN UNTUK SMOTE
# ============================================================

library(dplyr)
library(smotefamily)

# Salin data train
train_smote <- train_data %>%
  select(
    Keputusan,
    Segmen,
    Produk,
    Maksimum_Permohonan,
    Jangka_Waktu,
    Nominal_Keputusan,
    Jangka_Waktu_Keputusan,
    SLA
  )

# Ubah variabel kategorik menjadi factor
train_smote$Segmen <- as.factor(train_smote$Segmen)
train_smote$Produk <- as.factor(train_smote$Produk)

# Cek struktur
str(train_smote)
## tibble [691 × 8] (S3: tbl_df/tbl/data.frame)
##  $ Keputusan             : Factor w/ 2 levels "NON_USUL","USUL": 1 2 1 1 2 1 1 2 2 2 ...
##  $ Segmen                : Factor w/ 2 levels "FIX INCOME","NON FIX INCOME": 1 1 1 1 1 1 1 1 1 1 ...
##  $ Produk                : Factor w/ 4 levels "BNI FLEKSI","BNI FLEKSI PENSIUN",..: 3 3 3 3 3 1 1 3 3 3 ...
##  $ Maksimum_Permohonan   : num [1:691] 1.00e+09 4.00e+08 3.10e+08 4.16e+08 5.97e+08 ...
##  $ Jangka_Waktu          : num [1:691] 180 120 120 180 180 36 36 180 180 180 ...
##  $ Nominal_Keputusan     : num [1:691] 1.00e+09 3.51e+08 3.10e+08 4.16e+08 5.97e+08 ...
##  $ Jangka_Waktu_Keputusan: num [1:691] 120 120 120 180 140 36 36 120 240 300 ...
##  $ SLA                   : num [1:691] 5 6 1 3 1 1 2 3 1 0 ...
# Cek distribusi target sebelum SMOTE
cat("\n=== TARGET SEBELUM SMOTE ===\n")
## 
## === TARGET SEBELUM SMOTE ===
print(table(train_smote$Keputusan))
## 
## NON_USUL     USUL 
##      156      535
print(
  round(
    prop.table(table(train_smote$Keputusan)) * 100,
    2
  )
)
## 
## NON_USUL     USUL 
##    22.58    77.42
# ============================================================
# LANGKAH 31 — CEK FUNGSI SMOTE
# ============================================================

library(smotefamily)

print(exists("SMOTE"))
## [1] TRUE
print(args(SMOTE))
## function (X, target, K = 5, dup_size = 0) 
## NULL
# ============================================================
# LANGKAH 31 — DUMMY ENCODING DATA TRAIN
# ============================================================

library(dplyr)

train_smote_dummy <- train_smote %>%
  mutate(
    Segmen = as.factor(Segmen),
    Produk = as.factor(Produk)
  )

# Buat dummy variable
dummy_model <- model.matrix(
  Keputusan ~ Segmen + Produk - 1,
  data = train_smote_dummy
)

# Gabungkan dummy + variabel numerik
X_train_smote <- cbind(
  dummy_model,
  train_smote_dummy %>%
    select(
      Maksimum_Permohonan,
      Jangka_Waktu,
      Nominal_Keputusan,
      Jangka_Waktu_Keputusan,
      SLA
    )
)

# Target
y_train_smote <- train_smote_dummy$Keputusan

cat("=== DIMENSI X ===\n")
## === DIMENSI X ===
print(dim(X_train_smote))
## [1] 691  10
cat("\n=== NAMA VARIABEL ===\n")
## 
## === NAMA VARIABEL ===
print(colnames(X_train_smote))
##  [1] "SegmenFIX INCOME"          "SegmenNON FIX INCOME"     
##  [3] "ProdukBNI FLEKSI PENSIUN"  "ProdukBNI GRIYA"          
##  [5] "ProdukBNI GRIYA MULTIGUNA" "Maksimum_Permohonan"      
##  [7] "Jangka_Waktu"              "Nominal_Keputusan"        
##  [9] "Jangka_Waktu_Keputusan"    "SLA"
cat("\n=== TARGET ===\n")
## 
## === TARGET ===
print(table(y_train_smote))
## y_train_smote
## NON_USUL     USUL 
##      156      535
# ============================================================
# LANGKAH 32 — SMOTE
# ============================================================

set.seed(123)

smote_result <- SMOTE(
  X = X_train_smote,
  target = y_train_smote,
  K = 5,
  dup_size = 3
)

cat("=== SMOTE BERHASIL ===\n")
## === SMOTE BERHASIL ===
cat("\nJumlah data hasil SMOTE :", nrow(smote_result$data), "\n")
## 
## Jumlah data hasil SMOTE : 1159
cat("\nNama kolom hasil SMOTE:\n")
## 
## Nama kolom hasil SMOTE:
print(names(smote_result$data))
##  [1] "SegmenFIX INCOME"          "SegmenNON FIX INCOME"     
##  [3] "ProdukBNI FLEKSI PENSIUN"  "ProdukBNI GRIYA"          
##  [5] "ProdukBNI GRIYA MULTIGUNA" "Maksimum_Permohonan"      
##  [7] "Jangka_Waktu"              "Nominal_Keputusan"        
##  [9] "Jangka_Waktu_Keputusan"    "SLA"                      
## [11] "class"
cat("\nDistribusi target setelah SMOTE:\n")
## 
## Distribusi target setelah SMOTE:
print(table(smote_result$data$class))
## 
## NON_USUL     USUL 
##      624      535
cat("\nProporsi target setelah SMOTE:\n")
## 
## Proporsi target setelah SMOTE:
print(
  round(
    prop.table(table(smote_result$data$class)) * 100,
    2
  )
)
## 
## NON_USUL     USUL 
##    53.84    46.16
# ============================================================
# LANGKAH 33 — DATA TRAIN HASIL SMOTE
# ============================================================

train_smote_final <- smote_result$data

# Ubah nama target menjadi Keputusan
names(train_smote_final)[names(train_smote_final) == "class"] <- "Keputusan"

# Jadikan target factor
train_smote_final$Keputusan <- factor(
  train_smote_final$Keputusan,
  levels = c("NON_USUL", "USUL")
)

cat("=== STRUKTUR DATA SMOTE ===\n")
## === STRUKTUR DATA SMOTE ===
str(train_smote_final)
## 'data.frame':    1159 obs. of  11 variables:
##  $ SegmenFIX INCOME         : num  1 1 1 1 1 1 1 1 1 1 ...
##  $ SegmenNON FIX INCOME     : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ ProdukBNI FLEKSI PENSIUN : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ ProdukBNI GRIYA          : num  0 0 0 0 0 1 0 1 0 0 ...
##  $ ProdukBNI GRIYA MULTIGUNA: num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Maksimum_Permohonan      : num  5.0e+08 7.0e+07 6.0e+07 1.1e+08 1.5e+08 9.5e+08 2.5e+08 1.4e+08 5.0e+07 5.0e+08 ...
##  $ Jangka_Waktu             : num  96 60 48 36 36 120 188 120 60 72 ...
##  $ Nominal_Keputusan        : num  5.0e+08 7.0e+07 6.0e+07 1.5e+08 1.5e+08 9.5e+08 2.5e+08 1.4e+08 5.0e+07 5.0e+08 ...
##  $ Jangka_Waktu_Keputusan   : num  96 60 48 36 36 120 188 120 60 72 ...
##  $ SLA                      : num  3 0 2 1 0 6 2 5 1 3 ...
##  $ Keputusan                : Factor w/ 2 levels "NON_USUL","USUL": 1 1 1 1 1 1 1 1 1 1 ...
cat("\n=== DIMENSI DATA ===\n")
## 
## === DIMENSI DATA ===
print(dim(train_smote_final))
## [1] 1159   11
cat("\n=== DISTRIBUSI TARGET ===\n")
## 
## === DISTRIBUSI TARGET ===
print(table(train_smote_final$Keputusan))
## 
## NON_USUL     USUL 
##      624      535
cat("\n=== PROPORSI TARGET ===\n")
## 
## === PROPORSI TARGET ===
print(
  round(
    prop.table(table(train_smote_final$Keputusan)) * 100,
    2
  )
)
## 
## NON_USUL     USUL 
##    53.84    46.16
# ============================================================
# LANGKAH 34 — FIT CART PADA DATA HASIL SMOTE
# ============================================================

library(rpart)

model_cart_smote <- rpart(
  Keputusan ~
    `SegmenFIX INCOME` +
    `SegmenNON FIX INCOME` +
    `ProdukBNI FLEKSI PENSIUN` +
    `ProdukBNI GRIYA` +
    `ProdukBNI GRIYA MULTIGUNA` +
    Maksimum_Permohonan +
    Jangka_Waktu +
    Nominal_Keputusan +
    Jangka_Waktu_Keputusan +
    SLA,
  data = train_smote_final,
  method = "class",
  control = rpart.control(
    cp = 0.001,
    minsplit = 20,
    minbucket = 10,
    maxdepth = 5
  )
)

print(model_cart_smote)
## n= 1159 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 1159 535 NON_USUL (0.5383952 0.4616048)  
##    2) SegmenFIX INCOME< 0.9997023 95  15 NON_USUL (0.8421053 0.1578947)  
##      4) SegmenFIX INCOME>=0.004714953 58   0 NON_USUL (1.0000000 0.0000000) *
##      5) SegmenFIX INCOME< 0.004714953 37  15 NON_USUL (0.5945946 0.4054054)  
##       10) Maksimum_Permohonan< 1.572274e+09 27   8 NON_USUL (0.7037037 0.2962963) *
##       11) Maksimum_Permohonan>=1.572274e+09 10   3 USUL (0.3000000 0.7000000) *
##    3) SegmenFIX INCOME>=0.9997023 1064 520 NON_USUL (0.5112782 0.4887218)  
##      6) Jangka_Waktu< 179.4978 888 399 NON_USUL (0.5506757 0.4493243)  
##       12) SLA>=1.002053 636 246 NON_USUL (0.6132075 0.3867925)  
##         24) SLA< 1.99192 52   0 NON_USUL (1.0000000 0.0000000) *
##         25) SLA>=1.99192 584 246 NON_USUL (0.5787671 0.4212329)  
##           50) SLA>=2.003915 435 154 NON_USUL (0.6459770 0.3540230) *
##           51) SLA< 2.003915 149  57 USUL (0.3825503 0.6174497) *
##       13) SLA< 1.002053 252  99 USUL (0.3928571 0.6071429) *
##      7) Jangka_Waktu>=179.4978 176  55 USUL (0.3125000 0.6875000)  
##       14) ProdukBNI GRIYA< 0.8853115 32  13 NON_USUL (0.5937500 0.4062500)  
##         28) ProdukBNI GRIYA>=0.004482841 10   0 NON_USUL (1.0000000 0.0000000) *
##         29) ProdukBNI GRIYA< 0.004482841 22   9 USUL (0.4090909 0.5909091)  
##           58) Maksimum_Permohonan>=4.25e+08 11   5 NON_USUL (0.5454545 0.4545455) *
##           59) Maksimum_Permohonan< 4.25e+08 11   3 USUL (0.2727273 0.7272727) *
##       15) ProdukBNI GRIYA>=0.8853115 144  36 USUL (0.2500000 0.7500000) *
# ============================================================
# LANGKAH 35 — CEK CP TABLE CART-SMOTE
# ============================================================

cat("=== CP TABLE CART-SMOTE ===\n")
## === CP TABLE CART-SMOTE ===
print(model_cart_smote$cptable)
##            CP nsplit rel error    xerror       xstd
## 1 0.061682243      0 1.0000000 1.0000000 0.03172296
## 2 0.032710280      3 0.7757009 0.7775701 0.03052429
## 3 0.011214953      5 0.7102804 0.7532710 0.03030525
## 4 0.007476636      6 0.6990654 0.7570093 0.03034015
## 5 0.003738318      7 0.6915888 0.7383178 0.03016128
## 6 0.001869159      9 0.6841121 0.7364486 0.03014278
## 7 0.001000000     10 0.6822430 0.7308411 0.03008663
# ============================================================
# LANGKAH 36 — FIT CART-SMOTE DENGAN CP = 0
# ============================================================

model_cart_smote_full <- rpart(
  Keputusan ~
    `SegmenFIX INCOME` +
    `SegmenNON FIX INCOME` +
    `ProdukBNI FLEKSI PENSIUN` +
    `ProdukBNI GRIYA` +
    `ProdukBNI GRIYA MULTIGUNA` +
    Maksimum_Permohonan +
    Jangka_Waktu +
    Nominal_Keputusan +
    Jangka_Waktu_Keputusan +
    SLA,
  data = train_smote_final,
  method = "class",
  control = rpart.control(
    cp = 0,
    minsplit = 20,
    minbucket = 10,
    maxdepth = 5
  )
)

cat("=== CP TABLE CART-SMOTE FULL ===\n")
## === CP TABLE CART-SMOTE FULL ===
print(model_cart_smote_full$cptable)
##             CP nsplit rel error    xerror       xstd
## 1 0.0616822430      0 1.0000000 1.0000000 0.03172296
## 2 0.0327102804      3 0.7757009 0.7719626 0.03047537
## 3 0.0112149533      5 0.7102804 0.7271028 0.03004863
## 4 0.0074766355      6 0.6990654 0.6822430 0.02955706
## 5 0.0037383178      7 0.6915888 0.6803738 0.02953512
## 6 0.0018691589      9 0.6841121 0.6803738 0.02953512
## 7 0.0009345794     10 0.6822430 0.6710280 0.02942366
## 8 0.0000000000     12 0.6803738 0.6710280 0.02942366
# ============================================================
# LANGKAH 37 — PRUNING CART-SMOTE
# ============================================================

cp_optimal_smote <- 0.0009345794

model_cart_smote_pruned <- prune(
  model_cart_smote_full,
  cp = cp_optimal_smote
)

cat("=== CART-SMOTE SETELAH PRUNING ===\n")
## === CART-SMOTE SETELAH PRUNING ===
print(model_cart_smote_pruned)
## n= 1159 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 1159 535 NON_USUL (0.53839517 0.46160483)  
##    2) SegmenFIX INCOME< 0.9997023 95  15 NON_USUL (0.84210526 0.15789474)  
##      4) SegmenFIX INCOME>=0.004714953 58   0 NON_USUL (1.00000000 0.00000000) *
##      5) SegmenFIX INCOME< 0.004714953 37  15 NON_USUL (0.59459459 0.40540541)  
##       10) Maksimum_Permohonan< 1.572274e+09 27   8 NON_USUL (0.70370370 0.29629630) *
##       11) Maksimum_Permohonan>=1.572274e+09 10   3 USUL (0.30000000 0.70000000) *
##    3) SegmenFIX INCOME>=0.9997023 1064 520 NON_USUL (0.51127820 0.48872180)  
##      6) Jangka_Waktu< 179.4978 888 399 NON_USUL (0.55067568 0.44932432)  
##       12) SLA>=1.002053 636 246 NON_USUL (0.61320755 0.38679245)  
##         24) SLA< 1.99192 52   0 NON_USUL (1.00000000 0.00000000) *
##         25) SLA>=1.99192 584 246 NON_USUL (0.57876712 0.42123288)  
##           50) SLA>=2.003915 435 154 NON_USUL (0.64597701 0.35402299) *
##           51) SLA< 2.003915 149  57 USUL (0.38255034 0.61744966) *
##       13) SLA< 1.002053 252  99 USUL (0.39285714 0.60714286) *
##      7) Jangka_Waktu>=179.4978 176  55 USUL (0.31250000 0.68750000)  
##       14) ProdukBNI GRIYA< 0.8853115 32  13 NON_USUL (0.59375000 0.40625000)  
##         28) ProdukBNI GRIYA>=0.004482841 10   0 NON_USUL (1.00000000 0.00000000) *
##         29) ProdukBNI GRIYA< 0.004482841 22   9 USUL (0.40909091 0.59090909)  
##           58) Maksimum_Permohonan>=4.25e+08 11   5 NON_USUL (0.54545455 0.45454545) *
##           59) Maksimum_Permohonan< 4.25e+08 11   3 USUL (0.27272727 0.72727273) *
##       15) ProdukBNI GRIYA>=0.8853115 144  36 USUL (0.25000000 0.75000000)  
##         30) Nominal_Keputusan>=2.82984e+08 107  34 USUL (0.31775701 0.68224299)  
##           60) Maksimum_Permohonan< 4.356475e+08 45  22 NON_USUL (0.51111111 0.48888889) *
##           61) Maksimum_Permohonan>=4.356475e+08 62  11 USUL (0.17741935 0.82258065) *
##         31) Nominal_Keputusan< 2.82984e+08 37   2 USUL (0.05405405 0.94594595) *
cat("\nJumlah split setelah pruning :",
    nrow(model_cart_smote_pruned$splits), "\n")
## 
## Jumlah split setelah pruning : 86
# ============================================================
# LANGKAH 38 — VARIABLE IMPORTANCE CART-SMOTE
# ============================================================

cat("=== VARIABLE IMPORTANCE CART-SMOTE ===\n")
## === VARIABLE IMPORTANCE CART-SMOTE ===
importance_smote <- model_cart_smote_pruned$variable.importance

print(importance_smote)
##                       SLA          SegmenFIX INCOME      SegmenNON FIX INCOME 
##                 51.417915                 26.515669                 26.515669 
##              Jangka_Waktu       Maksimum_Permohonan         Nominal_Keputusan 
##                 18.969465                 16.420693                 14.810142 
##    Jangka_Waktu_Keputusan           ProdukBNI GRIYA ProdukBNI GRIYA MULTIGUNA 
##                 12.100397                 10.988636                  0.996578
cat("\n=== VARIABLE IMPORTANCE (%) ===\n")
## 
## === VARIABLE IMPORTANCE (%) ===
importance_percent <- round(
  importance_smote / sum(importance_smote) * 100,
  2
)

print(importance_percent)
##                       SLA          SegmenFIX INCOME      SegmenNON FIX INCOME 
##                     28.77                     14.84                     14.84 
##              Jangka_Waktu       Maksimum_Permohonan         Nominal_Keputusan 
##                     10.61                      9.19                      8.29 
##    Jangka_Waktu_Keputusan           ProdukBNI GRIYA ProdukBNI GRIYA MULTIGUNA 
##                      6.77                      6.15                      0.56
# ============================================================
# LANGKAH 39A — BUAT DUMMY TEST SESUAI DATA SMOTE
# ============================================================

test_smote <- test_data %>%
  mutate(
    Segmen = factor(
      Segmen,
      levels = c("FIX INCOME", "NON FIX INCOME")
    ),
    Produk = factor(
      Produk,
      levels = c(
        "BNI FLEKSI",
        "BNI FLEKSI PENSIUN",
        "BNI GRIYA",
        "BNI GRIYA MULTIGUNA"
      )
    )
  ) %>%
  mutate(
    `SegmenFIX INCOME` =
      ifelse(Segmen == "FIX INCOME", 1, 0),

    `SegmenNON FIX INCOME` =
      ifelse(Segmen == "NON FIX INCOME", 1, 0),

    `ProdukBNI FLEKSI PENSIUN` =
      ifelse(Produk == "BNI FLEKSI PENSIUN", 1, 0),

    `ProdukBNI GRIYA` =
      ifelse(Produk == "BNI GRIYA", 1, 0),

    `ProdukBNI GRIYA MULTIGUNA` =
      ifelse(Produk == "BNI GRIYA MULTIGUNA", 1, 0)
  )

cat("=== CEK KOLOM TEST ===\n")
## === CEK KOLOM TEST ===
print(
  names(
    test_smote %>%
      select(
        `SegmenFIX INCOME`,
        `SegmenNON FIX INCOME`,
        `ProdukBNI FLEKSI PENSIUN`,
        `ProdukBNI GRIYA`,
        `ProdukBNI GRIYA MULTIGUNA`,
        Maksimum_Permohonan,
        Jangka_Waktu,
        Nominal_Keputusan,
        Jangka_Waktu_Keputusan,
        SLA
      )
  )
)
##  [1] "SegmenFIX INCOME"          "SegmenNON FIX INCOME"     
##  [3] "ProdukBNI FLEKSI PENSIUN"  "ProdukBNI GRIYA"          
##  [5] "ProdukBNI GRIYA MULTIGUNA" "Maksimum_Permohonan"      
##  [7] "Jangka_Waktu"              "Nominal_Keputusan"        
##  [9] "Jangka_Waktu_Keputusan"    "SLA"
# ============================================================
# LANGKAH 39B — PREDIKSI CART-SMOTE PADA TEST ASLI
# ============================================================

pred_test_smote <- predict(
  model_cart_smote_pruned,
  newdata = test_smote,
  type = "class"
)

cat("=== PREDIKSI CART-SMOTE SELESAI ===\n")
## === PREDIKSI CART-SMOTE SELESAI ===
cat("\nJumlah prediksi:\n")
## 
## Jumlah prediksi:
print(table(pred_test_smote))
## pred_test_smote
## NON_USUL     USUL 
##       64      107
cat("\nProporsi prediksi (%):\n")
## 
## Proporsi prediksi (%):
print(
  round(
    prop.table(table(pred_test_smote)) * 100,
    2
  )
)
## pred_test_smote
## NON_USUL     USUL 
##    37.43    62.57
# ============================================================
# LANGKAH 40 — CONFUSION MATRIX CART-SMOTE
# ============================================================

library(caret)

cm_cart_smote <- confusionMatrix(
  data = pred_test_smote,
  reference = test_data$Keputusan,
  positive = "USUL"
)

cat("=== CONFUSION MATRIX CART-SMOTE ===\n")
## === CONFUSION MATRIX CART-SMOTE ===
print(cm_cart_smote)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction NON_USUL USUL
##   NON_USUL       17   47
##   USUL           21   86
##                                           
##                Accuracy : 0.6023          
##                  95% CI : (0.5248, 0.6763)
##     No Information Rate : 0.7778          
##     P-Value [Acc > NIR] : 1.000000        
##                                           
##                   Kappa : 0.0755          
##                                           
##  Mcnemar's Test P-Value : 0.002432        
##                                           
##             Sensitivity : 0.6466          
##             Specificity : 0.4474          
##          Pos Pred Value : 0.8037          
##          Neg Pred Value : 0.2656          
##              Prevalence : 0.7778          
##          Detection Rate : 0.5029          
##    Detection Prevalence : 0.6257          
##       Balanced Accuracy : 0.5470          
##                                           
##        'Positive' Class : USUL            
##