Sebuah lembaga perbankan di portugal sedang membuat layanan marketing baru berupa kampanye penjualan deposito berjangka dari panggilan telepon. Lembaga tersebut telah memiliki sejumlah data terkait yang berisi hasil penjualan dari telepon. sebagai seorang data analis kita diminta untuk membuat model yang dapat memprediksi klien yang berpotensi berlangganan deposito berjangka (variabel y).
Berikut adalah tabel penjelasan variabel dari dataset :
| No. | Nama Variabel | Tipe Data | Kategori/Keterangan |
|---|---|---|---|
| 1 | Age | Numerik | Umur |
| 2 | Job | Kategori | Tipe Pekerjaan (admin, blue-collar, entrepreneur, housemaid, management, retired, self-employed, services, student, technician, unemployed, unknown) |
| 3 | Marital | Kategori | Status Perkawinan (divorced, married, single, unknown) |
| 4 | Education | Kategori | Pendidikan (basic.4y, basic.6y, basic.9y, high.school, illiterate, professional.course, university.degree, unknown) |
| 5 | Default | Kategori | Status Kredit Default (no, yes, unknown) |
| 6 | Housing | Kategori | Pemilikan Rumah (no, yes, unknown) |
| 7 | Loan | Kategori | Pinjaman Pribadi (no, yes, unknown) |
| 8 | Contact | Kategori | Jenis Komunikasi Kontak (cellular, telephone) |
| 9 | Month | Kategori | Bulan Terakhir Kontak (jan, feb, mar, …, nov, dec) |
| 10 | Day_of_week | Kategori | Hari Terakhir Kontak dalam Seminggu (mon, tue, wed, thu, fri) |
| 11 | Duration | Numerik | Durasi Terakhir Kontak, dalam detik |
| 12 | Campaign | Numerik | Jumlah Kontak yang Dilakukan selama Kampanye ini dan untuk Klien ini |
| 13 | Pdays | Numerik | Jumlah hari yang telah berlalu setelah klien terakhir dihubungi dari kampanye sebelumnya (999 berarti klien tidak pernah dihubungi sebelumnya) |
| 14 | Previous | Numerik | Jumlah kontak yang dilakukan sebelum kampanye ini dan untuk klien ini |
| 15 | Poutcome | Kategori | Hasil dari kampanye pemasaran sebelumnya (failure, nonexistent, success) |
| 16 | Emp.var.rate | Numerik | Tingkat variasi pekerjaan - indikator perempat tahun |
| 17 | Cons.price.idx | Numerik | Indeks harga konsumen - indikator bulanan |
| 18 | Cons.conf.idx | Numerik | Indeks kepercayaan konsumen - indikator bulanan |
| 19 | Euribor3m | Numerik | Tingkat euribor 3 bulan - indikator harian |
| 20 | Nr.employed | Numerik | Jumlah karyawan - indikator perempat tahun |
| 21 | y | Kategori | Apakah klien telah berlangganan deposito berjangka? (yes, no) |
library(mlbench)
library(caTools)
library(caret)
library(e1071)
library(rpart)
library(randomForest)
library(dplyr)
library(partykit)
library(naivebayes)#> Rows: 41,188
#> Columns: 21
#> $ age <int> 56, 57, 37, 40, 56, 45, 59, 41, 24, 25, 41, 25, 29, 57,…
#> $ job <chr> "housemaid", "services", "services", "admin.", "service…
#> $ marital <chr> "married", "married", "married", "married", "married", …
#> $ education <chr> "basic.4y", "high.school", "high.school", "basic.6y", "…
#> $ default <chr> "no", "unknown", "no", "no", "no", "unknown", "no", "un…
#> $ housing <chr> "no", "no", "yes", "no", "no", "no", "no", "no", "yes",…
#> $ loan <chr> "no", "no", "no", "no", "yes", "no", "no", "no", "no", …
#> $ contact <chr> "telephone", "telephone", "telephone", "telephone", "te…
#> $ month <chr> "may", "may", "may", "may", "may", "may", "may", "may",…
#> $ day_of_week <chr> "mon", "mon", "mon", "mon", "mon", "mon", "mon", "mon",…
#> $ duration <int> 261, 149, 226, 151, 307, 198, 139, 217, 380, 50, 55, 22…
#> $ campaign <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
#> $ pdays <int> 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, …
#> $ previous <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
#> $ poutcome <chr> "nonexistent", "nonexistent", "nonexistent", "nonexiste…
#> $ emp.var.rate <dbl> 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, …
#> $ cons.price.idx <dbl> 93.994, 93.994, 93.994, 93.994, 93.994, 93.994, 93.994,…
#> $ cons.conf.idx <dbl> -36.4, -36.4, -36.4, -36.4, -36.4, -36.4, -36.4, -36.4,…
#> $ euribor3m <dbl> 4.857, 4.857, 4.857, 4.857, 4.857, 4.857, 4.857, 4.857,…
#> $ nr.employed <dbl> 5191, 5191, 5191, 5191, 5191, 5191, 5191, 5191, 5191, 5…
#> $ y <chr> "no", "no", "no", "no", "no", "no", "no", "no", "no", "…
terlihat sejumlah variabel belum sesuai tipe datanya, yaitu job, martial, education, default, housing, loan contact, month, day_of_week, poutcome, y.
#ubah tipe data.
df <- df %>% mutate_at(.vars=c('job', 'marital', "education", "default", "housing", "loan", "contact", "month", "day_of_week", "poutcome", 'y'), as.factor)#> age job marital education default
#> 0 0 0 0 0
#> housing loan contact month day_of_week
#> 0 0 0 0 0
#> duration campaign pdays previous poutcome
#> 0 0 0 0 0
#> emp.var.rate cons.price.idx cons.conf.idx euribor3m nr.employed
#> 0 0 0 0 0
#> y
#> 0
Tidak Terdapat missing values
untuk mendapatkan model yang baik kita perlu memiliki data yang
seimbang pada kelas target, sehingga model tidak hanya handal dalam
menilai salah satu kelas. Pada kasus kita variabel target adalah apakah
nasabah mau untuk berlangganan deposito berjangka atau tidak, yang dalam
hal ini diwakili oleh kolom y pada dataset.
#>
#> no yes
#> 0.8873458 0.1126542
Terlihat bahwa dataset kita imbalance dimana proporsi untuk target
yes jauh lebih besar dari target no.
Data splitting dilakukan dengan tujuan memisahkan dataset menjadi dua bagian: data pelatihan (train), data pengujian (test) dengan perbandingan 70%:30%. Proses pemilihan data secara acak dilakukan dengan menggunakan seed bernilai 123. Penggunaan seed ini penting untuk memastikan bahwa pengacakan data akan menghasilkan hasil yang konsisten. Setiap kali kita menggunakan nilai seed yang sama (misalnya, 123), pengacakan akan menghasilkan hasil yang sama, tetapi jika kita menggunakan nilai seed yang berbeda, hasil pengacakan akan berbeda, yang pada akhirnya dapat memengaruhi akurasi dan hasil analisis.
karena dataset kita cukup besar, maka akan dilakukan downsampling karena adanya imbalance pada variabel target.
RNGkind(sample.kind = "Rounding")
set.seed(123)
train_data <- downSample(x = train_data %>% select(-y),
y = train_data$y,
yname = "y")
prop.table(table(train_data$y))#>
#> no yes
#> 0.5 0.5
Kekurangan random forest adalah beban komputasinya yang amat besar dan lama.
Hal ini dapat dikurangi dengan menseleksi prediktor sehingga tidak
terlalu banyak. Bila ditemukan jumlah kolom yang amat banyak, kita dapat
menghapus kolom yang memiliki variansi mendekati nol (kurang informatif)
dengan nearZeroVar() dari package caret.
# feature selection menggunakan nearZeroVar
zero_var <- nearZeroVar(df) #fungsi untuk mencari kolom yang zero variance
# JANGAN DI-RUN 2X!!
df_rf <- df %>% select(-zero_var)set.seed(123) # Seed for reproducibility
train_indices <- createDataPartition(df_rf$y, p = 0.7, list = FALSE)
train_data_rf <- df_rf[train_indices, ]
test_data_rf <- df_rf[-train_indices, ]RNGkind(sample.kind = "Rounding")
set.seed(123)
train_data_rf <- downSample(x = train_data_rf %>% select(-y),
y = train_data_rf$y,
yname = "y")
prop.table(table(train_data_rf$y))#>
#> no yes
#> 0.5 0.5
# set.seed(123)
# ctrl <- trainControl(method = "repeatedcv", # repeated k-fold cross validation, cv = kfold
# number = 5,
# repeats=3) # k-fold
# model_rf <- caret::train(y ~ ., # formula model
# data = train_data_rf, # data yg digunakan
# method = "rf", # random forest
# trControl = ctrl,
# importance = T)
# rf_imp <- varImp(model_rf, scale = FALSE)
# saveRDS(model_rf, "model_rf.RDS")#>
#> Call:
#> randomForest(x = x, y = y, mtry = param$mtry, importance = ..1)
#> Type of random forest: classification
#> Number of trees: 500
#> No. of variables tried at each split: 27
#>
#> OOB estimate of error rate: 10.93%
#> Confusion matrix:
#> no yes class.error
#> no 2738 510 0.15701970
#> yes 200 3048 0.06157635
Pada tahap Bootstrap sampling, terdapat data yang tidak digunakan dalam pembuatan model, ini yang disebut sebagai data Out-of-Bag (OOB). Model Random Forest akan menggunakan data OOB sebagai data untuk melakukan evaluasi dengan cara menghitung error (serupa dengan data test). Error inilah yang disebut OOB Error. Dalam kasus klasifikasi, OOB error merupakan persentase data OOB yang misklasifikasi
Nilai OOB Error pada model model_rf sebesar 10.93%.
Dengan kata lain, akurasi model pada data OOB adalah 89.07%
prediksi_rf <- predict(object = model_rf,newdata = test_data_rf)
cm_rf <- confusionMatrix(data = prediksi_rf,reference = test_data_rf$y)$byClass
cm_rf#> Sensitivity Specificity Pos Pred Value
#> 0.8389274 0.9310345 0.9896708
#> Neg Pred Value Precision Recall
#> 0.4232528 0.9896708 0.8389274
#> F1 Prevalence Detection Rate
#> 0.9080857 0.8873422 0.7444157
#> Detection Prevalence Balanced Accuracy
#> 0.7521852 0.8849809
hasil akurasi model RF untuk data test adalah 89.07% sementara untuk data train adalah 88.46%, maka model dapat disimpulkan tidak overfitting.
prediksi_nb <- predict(object = model_nb,newdata = test_data, type = 'class')
cm_nb <- confusionMatrix(data = prediksi_nb,reference = test_data$y)$byClass
cm_nb#> Sensitivity Specificity Pos Pred Value
#> 0.8277089 0.7614943 0.9647071
#> Neg Pred Value Precision Recall
#> 0.3594439 0.9647071 0.8277089
#> F1 Prevalence Detection Rate
#> 0.8909725 0.8873422 0.7344610
#> Detection Prevalence Balanced Accuracy
#> 0.7613305 0.7946016
prediksi_dt <- predict(object = model_dt,
newdata = test_data,
type = "response")
cm_dt <- confusionMatrix(data = prediksi_dt,
reference = test_data$y)$byClass #data actual
cm_dt#> Sensitivity Specificity Pos Pred Value
#> 0.8041773 0.9554598 0.9930172
#> Neg Pred Value Precision Recall
#> 0.3825137 0.9930172 0.8041773
#> F1 Prevalence Detection Rate
#> 0.8886761 0.8873422 0.7135804
#> Detection Prevalence Balanced Accuracy
#> 0.7185983 0.8798185
Karena kita ingin mengetahui seberapa akurat model kita dalam memprediksi apakah seorang pelanggan akan berlangganan deposito berjangka, kita sebaiknya mempertimbangkan presisi (precision) sebagai metrik utama:
Presisi (Precision) adalah metrik evaluasi dalam analisis klasifikasi yang mengukur sejauh mana prediksi positif yang dibuat oleh model adalah benar positif. Lebih tepatnya, presisi mengukur berapa persen dari prediksi positif yang benar-benar benar.
Rumus untuk menghitung presisi adalah:
\[Presisi = \frac{TP}{TP + FP}\]
Di mana: - TP (True Positive) adalah jumlah data yang benar-benar positif dan telah diprediksi dengan benar sebagai positif oleh model. - FP (False Positive) adalah jumlah data yang sebenarnya negatif tetapi salah diprediksi sebagai positif oleh model.
Dalam konteks ini, presisi akan memberikan gambaran tentang seberapa baik model dalam mengidentifikasi pelanggan yang benar-benar akan berlangganan deposito berjangka. Semakin tinggi nilai presisi, semakin baik model dalam menghindari kesalahan memprediksi pelanggan yang sebenarnya tidak akan berlangganan.
Dalam studi kasus ini, kita akan fokus pada penyesuaian dua parameter, yaitu mtry dan ntree, yang memiliki pengaruh berikut pada model random forest kita. Meskipun ada banyak parameter lain, dua parameter ini mungkin memiliki dampak terbesar pada akurasi akhir Anda.
Berikut penjelasan kedua parameter tersebut :
Metode pertama yang dapat digunakan untuk tuning model RF adalah Grid Search. Setiap sumbu dari grid adalah parameter algoritma, dan titik-titik dalam grid adalah kombinasi khusus dari parameter-parameter tersebut. Karena kita hanya menyesuaikan satu parameter, maka pencarian grid ini merupakan pencarian linear melalui vektor nilai-nilai kandidat untuk parameter tersebut.
# Tuning model RF dengan grid search
# control <- trainControl(method="repeatedcv",
# number=10,
# repeats=3,
# search="grid") # 10 fold cross validation, repeadted 3 kali.
# set.seed(123)
# tunegrid <- expand.grid(.mtry=c(1:15))
# rf_gridsearch <- train(y~., data=train_data_rf, method="rf", metric='accuracy', tuneGrid=tunegrid, trControl=control)
# print(rf_gridsearch)
# plot(rf_gridsearch)
# saveRDS(rf_gridsearch, "rf_gridsearch.RDS")Dengan tambahan tanda ‘#’ di depan setiap baris, kode ini sekarang adalah komentar dan tidak akan dieksekusi saat dijalankan.
#>
#> Call:
#> randomForest(x = x, y = y, mtry = param$mtry)
#> Type of random forest: classification
#> Number of trees: 500
#> No. of variables tried at each split: 13
#>
#> OOB estimate of error rate: 10.87%
#> Confusion matrix:
#> no yes class.error
#> no 2736 512 0.15763547
#> yes 194 3054 0.05972906
# Prediksi dan evaluasi RF Tune Grid
prediksi_rf_tune <- predict(object = rf_gridsearch,newdata = test_data_rf)
cm_rf_tune_grid <- confusionMatrix(data = prediksi_rf_tune,reference = test_data_rf$y)$byClass
cm_rf_tune_grid#> Sensitivity Specificity Pos Pred Value
#> 0.8375593 0.9324713 0.9898674
#> Neg Pred Value Precision Recall
#> 0.4215654 0.9898674 0.8375593
#> F1 Prevalence Detection Rate
#> 0.9073662 0.8873422 0.7432017
#> Detection Prevalence Balanced Accuracy
#> 0.7508093 0.8850153
Kita dapat melihat bahwa nilai yang paling akurat untuk mtry adalah 13, dengan tingkat akurasi data train sebesar 83,78% serta akurasi data test sebesar 88.50%
untuk itu kita akan mencoba model tuning kedua dengan random search. Random search adalah mencoba nilai-nilai acak dalam suatu rentang. Ini bisa berguna jika kita tidak yakin dengan nilai yang mungkin cocok dan kita ingin mengatasi bias yang mungkin kita miliki dalam menentukan parameter. Mari coba pencarian acak untuk mtry menggunakan paket caret:
# control <- trainControl(method="repeatedcv", number=10, repeats=3, search="random")
# set.seed(123)
# mtry <- 7
# rf_random <- train(y~., data=train_data_rf, method="rf", metric="accuracy", tuneLength=15, trControl=control)
# print(rf_random)
# plot(rf_random)
# saveRDS(rf_random, "rf_random.RDS")#>
#> Call:
#> randomForest(x = x, y = y, mtry = param$mtry)
#> Type of random forest: classification
#> Number of trees: 500
#> No. of variables tried at each split: 24
#>
#> OOB estimate of error rate: 10.9%
#> Confusion matrix:
#> no yes class.error
#> no 2740 508 0.15640394
#> yes 200 3048 0.06157635
# Prediksi dan evaluasi RF Tune
prediksi_rf_tune_random <- predict(object = rf_random,newdata = test_data_rf)
cm_rf_tune_random <- confusionMatrix(data = prediksi_rf_tune_random,reference = test_data_rf$y)$byClass
cm_rf_tune_random#> Sensitivity Specificity Pos Pred Value
#> 0.8377417 0.9317529 0.9897629
#> Neg Pred Value Precision Recall
#> 0.4216515 0.9897629 0.8377417
#> F1 Prevalence Detection Rate
#> 0.9074294 0.8873422 0.7433635
#> Detection Prevalence Balanced Accuracy
#> 0.7510521 0.8847473
Berikut tabel perbandingan dari ketiga model RF kita :
comparison_table_rf <- data.frame(
Model = c("Model RF", "Model RF Grid", "Model RF Random"),
Accuracy = c(cm_rf["Balanced Accuracy"], cm_rf_tune_grid["Balanced Accuracy"], cm_rf_tune_random["Balanced Accuracy"]),
Sensitivity = c(cm_rf["Sensitivity"], cm_rf_tune_grid["Sensitivity"], cm_rf_tune_random["Sensitivity"]),
Specificity = c(cm_rf["Specificity"], cm_rf_tune_grid["Specificity"], cm_rf_tune_random["Specificity"]),
Precision = c(cm_rf["Pos Pred Value"], cm_rf_tune_grid["Pos Pred Value"], cm_rf_tune_random["Pos Pred Value"]),
F1_Score = c(cm_rf["F1"], cm_rf_tune_grid["F1"], cm_rf_tune_random["F1"])
)
comparison_table_rfPerbandingan ketiga model RF menunjukan hasil yang hampir sama nilai nya, namun dapat terlihat bahwa model RF Random memiliki precision yang sedikit lebih unggul dari kedua model lainnya. untuk itu model RF yang akan digunakan adalah RF Random.
Kita dapat menyesuaikan beberapa parameter hiper yang dimiliki oleh model Naïve Bayes.
usekernel memungkinkan kita untuk menggunakan
estimasi kerapatan kernel (kernel density estimate) untuk variabel
kontinu daripada estimasi kerapatan Gaussian.adjust memungkinkan kita untuk menyesuaikan
lebar bandwidth dari estimasi kerapatan kernel (nilai yang lebih besar
menghasilkan estimasi kerapatan yang lebih fleksibel).fL memungkinkan kita untuk menggunakan
smoothing Laplace.kita juga akan menggabungkan beberapa tahap preprocessing fitur (normalisasi dengan Box Cox, standarisasi dengan center-scaling, dan reduksi dengan PCA).
Proses preprocessing diharapkan ini membantu model Naïve Bayes untuk menghasilkan hasil yang lebih baik dengan mengolah data input sebelum digunakan dalam proses pelatihan model. Hal ini sering digunakan untuk meningkatkan performa model dan mengurangi masalah overfitting.
# train_control <- trainControl(
# method = "cv",
# number = 10
# )
#
# # set up tuning grid
# search_grid <- expand.grid(
# usekernel = c(TRUE, FALSE),
# fL = 0:5,
# adjust = seq(0, 5, by = 1)
# )
#
# # train model
# nb.m2 <- train(
# x = train_data %>% select(-y),
# y = train_data$y,
# method = "nb",
# trControl = train_control,
# tuneGrid = search_grid,
# preProc = c("BoxCox", "center", "scale", "pca")
# )
#
# # top 5 modesl
# nb.m2$results %>%
# top_n(5, wt = Accuracy) %>%
# arrange(desc(Accuracy))
#
# saveRDS(nb.m2, "model_nb_tune.RDS")nb.m2 <- readRDS("model_nb_tune.RDS")
pred_nb_tune<-predict(nb.m2,test_data)
cm_nb_tune <- confusionMatrix(pred_nb_tune,test_data$y)$byClasscomparison_table_nb <- data.frame(
Model = c("Model NB", "Model NB Tune"),
Accuracy = c(cm_nb["Balanced Accuracy"], cm_nb_tune["Balanced Accuracy"]),
Sensitivity = c(cm_nb["Sensitivity"], cm_nb_tune["Sensitivity"]),
Specificity = c(cm_nb["Specificity"], cm_nb_tune["Specificity"]),
Precision = c(cm_nb["Pos Pred Value"], cm_nb_tune["Pos Pred Value"]),
F1_Score = c(cm_nb["F1"], cm_nb_tune["F1"])
)
comparison_table_nbHasil tuning model NB tidak menunjukan peningkatan performa yang signifikan, ini mungkin terjadi karena prediktor kita yang mayoritas variabel kategorik. untuk itu kita akan memilih model NB tampa tuning.
Kekurangan dari Decision Tree adalah kecenderungannya untuk overfitting. Hal ini terjadi karena Decision tree mampu membagi-bagi data hingga amat detail (bahkan hingga dalam leaf node hanya terdapat 1 observasi). Pada keadaan ini, decision tree justru menghafal pola di data train, dan membuat aturan yang terlalu kompleks, bukan mempelajari pola tersebut. Alhasil model menjadi kurang general untuk diaplikasikan ke data yang bukan data train, sehingga cenderung overfit. Untuk mengatasinya, decision tree perlu tahu kapan ia berhenti membuat cabang sehingga pohon yang dihasilkan tidak terlalu kompleks. Pemotongan/pencegahan cabang pohon disebut Pruning. Parameter yang dapat digunakan untuk pruning model DT adalah :
mincriterion: 1- tingkat signifikan minsplit: syarat jumlah observasi sebelum membuat cabang minbucket: syarat jumlah observasi setelah membuat cabang
model_dt_tune <- ctree(formula = y~.,
data = train_data,
control = ctree_control(mincriterion = 0.90,
minsplit = 30,
minbucket = 9))prediksi_dt_tune <- predict(object = model_dt_tune,
newdata = test_data,
type = "response")
cm_dt_tune <- confusionMatrix(data = prediksi_dt_tune,
reference = test_data$y)$byClass #data actual
cm_dt_tune#> Sensitivity Specificity Pos Pred Value
#> 0.8027180 0.9561782 0.9931167
#> Neg Pred Value Precision Recall
#> 0.3809388 0.9931167 0.8027180
#> F1 Prevalence Detection Rate
#> 0.8878241 0.8873422 0.7122855
#> Detection Prevalence Balanced Accuracy
#> 0.7172224 0.8794481
comparison_table_dt <- data.frame(
Model = c("Model DT", "Model DT Tune"),
Accuracy = c(cm_dt["Balanced Accuracy"], cm_dt_tune["Balanced Accuracy"]),
Sensitivity = c(cm_dt["Sensitivity"], cm_dt_tune["Sensitivity"]),
Specificity = c(cm_dt["Specificity"], cm_dt_tune["Specificity"]),
Precision = c(cm_dt["Pos Pred Value"], cm_dt_tune["Pos Pred Value"]),
F1_Score = c(cm_dt["F1"], cm_dt_tune["F1"])
)
comparison_table_dtPerbandingan kedua model DT kita menunjukan performa yang hampir sama. meskipun model DT dengan tuning sedikit lebih unggul pada metrik akurasi, namun terlihat model DT tampa tuning sedikit lebih unggul pada metrik precision, maka kita akan menggunakan model DT tampa tuning
Langkah terakhir adalah membandingkan ketiga model yang telah kita buat. seperti yang telah dijelaskan diatas bahwa pada case bisnis kita akan lebih memperhatikan metrik Precision.
comparison_table <- data.frame(
Model = c("Model RF", "Model NB", "Model DT"),
Accuracy = c(cm_rf_tune_random["Balanced Accuracy"], cm_nb["Balanced Accuracy"], cm_dt["Balanced Accuracy"]),
Sensitivity = c(cm_rf_tune_random["Sensitivity"], cm_nb["Sensitivity"], cm_dt["Sensitivity"]),
Specificity = c(cm_rf_tune_random["Specificity"], cm_nb["Specificity"], cm_dt["Specificity"]),
Precision = c(cm_rf_tune_random["Pos Pred Value"], cm_nb["Pos Pred Value"], cm_dt["Pos Pred Value"]),
F1_Score = c(cm_rf_tune_random["F1"], cm_nb["F1"], cm_dt["F1"])
)
comparison_tableBerdasarkan hasil perbandingan ketiga model kita didapatkan model RF unggul pada matrik Precision dengan 98.98%. selain itu model RF juga unggul di semua matrik lainnya. Untuk itu kita dapat menggunakan model RF untuk membuat prediksi hingga 98.98% dari prediksi positif yang benar-benar benar. Dengan demikian kita telah menjawab masalah dalam percobaan ini yaitu membuat model yang dapat memprediksi klien yang berpotensi berlangganan deposito berjangka.