Dataset yang digunakan adalah dataset tentang kampanye pemasaran panggilan telepon langsung, yang bertujuan untuk mempromosikan deposito di antara pelanggan yang sudah ada, oleh lembaga perbankan Portugis. Kita akan menganalisis kampanye pemasaran terakhir yang dilakukan bank dan mengidentifikasi pola yang akan membantu dalam menemukan kesimpulan untuk mengembangkan strategi masa depan. Salah satunya ialah dengan memprediksi apakah seorang calon pelanggan akan setuju mengajukan program deposit atau tidak dengan kampanye yang telah dilakukan.
library(dplyr)
library(tidyverse)
library(e1071)
library(rsample)
library(caret)Read Data
bank <- read.csv("bank-full.csv", sep = ";")
head(bank)#> age job marital education default balance housing loan contact day
#> 1 58 management married tertiary no 2143 yes no unknown 5
#> 2 44 technician single secondary no 29 yes no unknown 5
#> 3 33 entrepreneur married secondary no 2 yes yes unknown 5
#> 4 47 blue-collar married unknown no 1506 yes no unknown 5
#> 5 33 unknown single unknown no 1 no no unknown 5
#> 6 35 management married tertiary no 231 yes no unknown 5
#> month duration campaign pdays previous poutcome y
#> 1 may 261 1 -1 0 unknown no
#> 2 may 151 1 -1 0 unknown no
#> 3 may 76 1 -1 0 unknown no
#> 4 may 92 1 -1 0 unknown no
#> 5 may 198 1 -1 0 unknown no
#> 6 may 139 1 -1 0 unknown no
Deskripsi kolom:
age: umurjob: kategori pekerjaanmarital: status menikaheducation: tingkat pendidikandefault: apakah memiliki kredit gagal bayar
(default)?balance: uang yang tersimpan dalam rekeninghousing: apakah memiliki kredit rumah?loan: apakah memiliki kredit pribadi?contact: metode kontak/telefonday: day-of-month dari kontak terakhirmonth: bulan dari kontak terakhirduration: durasi kontak pada campaign inicampaign: jumlah kontak yang dilakukan pada campaign
inipdays: jumlah hari berlalu setelah kontak dari campaign
sebelumnyaprevious: jumlah kontak yang dilakukan pada campaign
sebelumnyapoutcome: outcome dari campaign sebelumnyay: outcome dari campaign ini (target variable)Ada beberapa kolom yang perlu dihilangkan, yaitu :
month -> tidak penting, karena hanya
menunjukkan apakah calon nasabah tersebut pernah ditawarkan atau
tidakcontact -> tidak penting, karena hanya
menunjukan alat yang digunakan dalam melakukan penawranpdays -> - tidak penting, karena intuisinya
sama saja dengan month, untuk mengetahui apakah calon
nasabah tersebut pernah di hubungi atau tidak. Pada kolom ini juga
terdapat banyak nilai negatif, dan nilai negatif bisa kita indikasikan
sebagai nilai yang salah diinputday -> tidak penting, intuisinya sama dengan
kolom monthduration -> Data durasi baru kita dapatkan
setelah penelfonan terjadi, dan pada data kedepannya kolom tersebut
tidak kita milikipoutcome -> karena berisikan informasi
unknown yang cukup banyakbank_clean <- bank %>%
select(-c(month, contact, pdays, day, duration, poutcome))
colnames(bank_clean) #> [1] "age" "job" "marital" "education" "default" "balance"
#> [7] "housing" "loan" "campaign" "previous" "y"
Kolom yang tidak perlu sudah berhasil dikeluarkan. Sekarang kita cek tipe data apakah sudah sesuai.
bank_clean %>%
glimpse()#> Rows: 45,211
#> Columns: 11
#> $ age <int> 58, 44, 33, 47, 33, 35, 28, 42, 58, 43, 41, 29, 53, 58, 57, …
#> $ job <chr> "management", "technician", "entrepreneur", "blue-collar", "…
#> $ marital <chr> "married", "single", "married", "married", "single", "marrie…
#> $ education <chr> "tertiary", "secondary", "secondary", "unknown", "unknown", …
#> $ default <chr> "no", "no", "no", "no", "no", "no", "no", "yes", "no", "no",…
#> $ balance <int> 2143, 29, 2, 1506, 1, 231, 447, 2, 121, 593, 270, 390, 6, 71…
#> $ housing <chr> "yes", "yes", "yes", "yes", "no", "yes", "yes", "yes", "yes"…
#> $ loan <chr> "no", "no", "yes", "no", "no", "no", "yes", "no", "no", "no"…
#> $ campaign <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
#> $ previous <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ y <chr> "no", "no", "no", "no", "no", "no", "no", "no", "no", "no", …
Terlihat ada beberapa feature yang bertipe data character. Maka dari itu, kita akan diubah menjadi tipe data factor.
bank_clean <- bank_clean %>%
mutate_if(is.character, as.factor)
glimpse(bank_clean)#> Rows: 45,211
#> Columns: 11
#> $ age <int> 58, 44, 33, 47, 33, 35, 28, 42, 58, 43, 41, 29, 53, 58, 57, …
#> $ job <fct> management, technician, entrepreneur, blue-collar, unknown, …
#> $ marital <fct> married, single, married, married, single, married, single, …
#> $ education <fct> tertiary, secondary, secondary, unknown, unknown, tertiary, …
#> $ default <fct> no, no, no, no, no, no, no, yes, no, no, no, no, no, no, no,…
#> $ balance <int> 2143, 29, 2, 1506, 1, 231, 447, 2, 121, 593, 270, 390, 6, 71…
#> $ housing <fct> yes, yes, yes, yes, no, yes, yes, yes, yes, yes, yes, yes, y…
#> $ loan <fct> no, no, yes, no, no, no, yes, no, no, no, no, no, no, no, no…
#> $ campaign <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
#> $ previous <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ y <fct> no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, …
Cek apakah terdapat missing value pada data.
bank_clean %>%
anyNA#> [1] FALSE
Tidak terdapat missing value pada data. Maka dapat dilanjutkan ke proses berikutnya.
set.seed(417)
split <- initial_split(data = bank_clean, prop = 0.8, strata = "y")
data_train <- training(split)
data_test <- testing(split)prop.table(table(data_train$y))#>
#> no yes
#> 0.8830181 0.1169819
Terlihat bahwa proporsi kelas target belum seimbang. Maka dilakukan downsample, yaitu mengurangi kelas mayoritas hingga seimbang dengan minoritas.
RNGkind(sample.kind = "Rounding")
set.seed(100)
down_train <- downSample(x = data_train %>% select(-y),
y = data_train$y,
yname = "y")prop.table(table(down_train$y))#>
#> no yes
#> 0.5 0.5
Data sudah seimbang. Maka dapat dilakukan proses analisis selanjutnya.
Naive Bayes memanfaatkan kejadian dependent events, namun menerapkan
asumsi Naive pada hubungan antar prediktornya:
1. Hubungan prediktor dengan target variabel saling dependen
2. Hubungan antar prediktor saling independen
model_naive <- naiveBayes(y ~ .,
data = down_train)Setelah membuat model, kita lakukan prediksi.
pred_naive <- predict(object = model_naive,
newdata = data_test,
type = "class")Evaluasi Model
conf_matrix_naive <- table(pred_naive, data_test$y)
conf_matrix_naive#>
#> pred_naive no yes
#> no 3379 224
#> yes 4606 834
Hasil ConfusionMatrix menunjukkan bahwa klasifikasi Naive Bayes memperkirakan dengan benar 3379 orang tidak setuju mengajukan program deposit dan 224 prediksi salah. Demikian pula, model memprediksi dengan benar 834 orang setuju mengajukan program deposit dan 4606 prediksi salah. Bagimana dengan tingkat recall/sensitivity?? Mari kita lihat dibawah ini.
confusionMatrix(data = pred_naive,
reference = data_test$y,
positive = "yes")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction no yes
#> no 3379 224
#> yes 4606 834
#>
#> Accuracy : 0.4659
#> 95% CI : (0.4556, 0.4762)
#> No Information Rate : 0.883
#> P-Value [Acc > NIR] : 1
#>
#> Kappa : 0.0756
#>
#> Mcnemar's Test P-Value : <2e-16
#>
#> Sensitivity : 0.78828
#> Specificity : 0.42317
#> Pos Pred Value : 0.15331
#> Neg Pred Value : 0.93783
#> Prevalence : 0.11700
#> Detection Rate : 0.09223
#> Detection Prevalence : 0.60157
#> Balanced Accuracy : 0.60572
#>
#> 'Positive' Class : yes
#>
Dari output model klasifikasi Naive Bayes, terlihat bahwa tingkat akurasi sebesar 67.8% dengan nilai recall/sensitivitynya sebesar 59.07%.
Selanjutnya, mari kita uji menggunakan model Decision Tree.
Decision Tree merupakan tree-based model yang cukup sederhana dengan performa yang robust/powerful untuk prediksi. Decision Tree menghasilkan visualisasi berupa pohon keputusan yang dapat diinterpretasi dengan mudah.
library(partykit)
model_dtree <- ctree(formula = y ~.,
data = down_train)
plot(model_dtree, type = "simple")
Setelah membuat model, kita lakukan prediksi.
train_pred_dtree <- predict(object = model_dtree,
newdata = down_train,
type = "response")
test_pred_dtree <- predict(object = model_dtree,
newdata = data_test,
type = "response")Evaluasi Model
Pada evaluasi model kali ini, kita akan membandingkan peforma pada data
train dan data test.
# Confusion Matrix: data train
confusionMatrix(data = train_pred_dtree,
reference = down_train$y,
positive = "yes")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction no yes
#> no 3157 1817
#> yes 1074 2414
#>
#> Accuracy : 0.6584
#> 95% CI : (0.6481, 0.6685)
#> No Information Rate : 0.5
#> P-Value [Acc > NIR] : < 2.2e-16
#>
#> Kappa : 0.3167
#>
#> Mcnemar's Test P-Value : < 2.2e-16
#>
#> Sensitivity : 0.5706
#> Specificity : 0.7462
#> Pos Pred Value : 0.6921
#> Neg Pred Value : 0.6347
#> Prevalence : 0.5000
#> Detection Rate : 0.2853
#> Detection Prevalence : 0.4122
#> Balanced Accuracy : 0.6584
#>
#> 'Positive' Class : yes
#>
# Confusion Matrix: data test
confusionMatrix(data = test_pred_dtree,
reference = data_test$y,
positive = "yes")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction no yes
#> no 5838 467
#> yes 2147 591
#>
#> Accuracy : 0.7109
#> 95% CI : (0.7015, 0.7203)
#> No Information Rate : 0.883
#> P-Value [Acc > NIR] : 1
#>
#> Kappa : 0.1716
#>
#> Mcnemar's Test P-Value : <2e-16
#>
#> Sensitivity : 0.55860
#> Specificity : 0.73112
#> Pos Pred Value : 0.21585
#> Neg Pred Value : 0.92593
#> Prevalence : 0.11700
#> Detection Rate : 0.06535
#> Detection Prevalence : 0.30278
#> Balanced Accuracy : 0.64486
#>
#> 'Positive' Class : yes
#>
Kita akan menggunakan metrics recall/sensitivity sebab lebih
memilih benar diprediksi positif, dari yang
reality-nya (aktualnya) positif. Atau dengan kata lain,
tidak mau ada positif yang tidak terprediksi positif.
Dapat kita lihat performa metricsnya :
Perbedaannya sebesar 1.2%. Dalam hal ini, performa model konsisten di
data train maupun data test.
Karena nilai recallnya masih belum cukup tinggi, maka akan
dilakukan tunning model menggunakan tree
prunning.
model_dtree_tuned <- ctree(formula = y ~.,
data = down_train,
control = ctree_control(mincriterion = 0.97,
minsplit = 30,
minbucket = 50))
plot(model_dtree_tuned, type = "simple")# prediksi ke data train
train_pred_tuned <- predict(object = model_dtree_tuned,
newdata = down_train,
type = "response")
# prediksi ke data test
test_pred_tuned <- predict(object = model_dtree_tuned,
newdata = data_test,
type = "response")confusionMatrix(data = train_pred_tuned,
reference = down_train$y,
positive = "yes")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction no yes
#> no 3072 1746
#> yes 1159 2485
#>
#> Accuracy : 0.6567
#> 95% CI : (0.6465, 0.6668)
#> No Information Rate : 0.5
#> P-Value [Acc > NIR] : < 2.2e-16
#>
#> Kappa : 0.3134
#>
#> Mcnemar's Test P-Value : < 2.2e-16
#>
#> Sensitivity : 0.5873
#> Specificity : 0.7261
#> Pos Pred Value : 0.6819
#> Neg Pred Value : 0.6376
#> Prevalence : 0.5000
#> Detection Rate : 0.2937
#> Detection Prevalence : 0.4306
#> Balanced Accuracy : 0.6567
#>
#> 'Positive' Class : yes
#>
confusionMatrix(data = test_pred_tuned,
reference = data_test$y,
positive = "yes")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction no yes
#> no 5650 446
#> yes 2335 612
#>
#> Accuracy : 0.6925
#> 95% CI : (0.6828, 0.702)
#> No Information Rate : 0.883
#> P-Value [Acc > NIR] : 1
#>
#> Kappa : 0.1612
#>
#> Mcnemar's Test P-Value : <2e-16
#>
#> Sensitivity : 0.57845
#> Specificity : 0.70758
#> Pos Pred Value : 0.20767
#> Neg Pred Value : 0.92684
#> Prevalence : 0.11700
#> Detection Rate : 0.06768
#> Detection Prevalence : 0.32589
#> Balanced Accuracy : 0.64301
#>
#> 'Positive' Class : yes
#>
Setelah dilakukan proses tree pruning, diperoleh nilai recall pada data train sebesar 0.5873 sedangkan pada data testnya sebesar 0.57845. Pada tuning model ini, nilai recallnya tidak mengalami kenaikan yang signifikan. Maka dari itu, akan dicoba model berikutnya yaitu Random Forest.
Random Forest adalah salah satu jenis Ensemble Method yang terdiri dari banyak Decision Tree. Masing-masing Decision Tree memiliki karakteristik masing-masing dan tidak saling berhubungan. Random Forest memanfaatkan konsep Bagging (Bootstrap and Aggregation) dalam pembuatannya.
Cross Validation
library(rsample)
RNGkind(sample.kind = "Rounding")
set.seed(100)
index_bank <- sample(nrow(bank_clean), nrow(bank_clean)*0.8)
train_bank <- bank_clean[index_bank,]
test_bank <- bank_clean[-index_bank,]K-Fold Cross Validation
Biasanya kita melakukan cross validation dengan membagi data menjadi
data train dan test. K-Fold Cross Validation membagi data
sebanyak k bagian sama banyak, sehingga tiap bagian sempat
dijadikan data train dan data test.
RNGkind(sample.kind = "Rounding")
set.seed(100)
ctrl <- trainControl(method = "repeatedcv",
number = 5, # k-fold
repeats = 3) # repetisiModel Random Forest
Kekurangan dari Random Forest adalah membutuhkan waktu komputasi yang cukup lama. Maka dari itu kita akan membuat modelnya terlebih dahulu, kemudian menyimpannya ke dalam format .rds
# model_rf <- train(y ~.,
# data = train_bank,
# method = "rf",
# trcontrol = ctrl)
#
# saveRDS(model_rf, "model_rf.RDS")Read Model
model_bank_rf <- readRDS("model_rf.RDS")
model_bank_rf#> Random Forest
#>
#> 36168 samples
#> 10 predictor
#> 2 classes: 'no', 'yes'
#>
#> No pre-processing
#> Resampling: Bootstrapped (25 reps)
#> Summary of sample sizes: 36168, 36168, 36168, 36168, 36168, 36168, ...
#> Resampling results across tuning parameters:
#>
#> mtry Accuracy Kappa
#> 2 0.8831878 0.0002680743
#> 12 0.8774059 0.1737377125
#> 23 0.8734002 0.1770089073
#>
#> Accuracy was used to select the optimal model using the largest value.
#> The final value used for the model was mtry = 2.
Interpretasi pada summary model di atas :
Dilakukan beberapa kali percobaan mtry (jumlah prediktor
random yang digunakan saat splitting node).
Secara default akan dicoba sebanyak 3 nilai mtry, yaitu
:
mtry = 2mtry sebanyak jumlah predictor (numerik +
dummy variable). Pada kasus ini, terdapat 4 predictor numerik dan 19
dummy variable dari predictor kategorikalmtry minimal dan maksimal, yaitu 12Model yang dipilih adalah mtry = 2 dengan nilai Accuracy
tertinggi ketika diujikan ke data hasil bootstrap sampling (atau data
in-sample, bisa dianggap sebagai data train seperti pada pembuatan
model).
Out-of-Bag (OOB) Error
Pada tahap Bootstrap Sampling, terdapat data yang tidak digunakan dalam pembuatan model, ini yang disebut sebagai Out-of-Bag (OOB) data. OOB data dapat diibaratkan unseen data/data test. Model random forest akan otomatis menghitung OOB error untuk mengetahui performa random forest di OOB data atau diibaratkan sebagai unseen data.
Untuk mengetahui OOB error:
library(randomForest)
model_bank_rf$finalModel#>
#> Call:
#> randomForest(x = x, y = y, mtry = param$mtry, trcontrol = ..1)
#> Type of random forest: classification
#> Number of trees: 500
#> No. of variables tried at each split: 2
#>
#> OOB estimate of error rate: 11.71%
#> Confusion matrix:
#> no yes class.error
#> no 31932 0 0
#> yes 4236 0 1
# accuracy: 1-error
100-11.71#> [1] 88.29
Nilai OOB Error pada model model_bank_rf sebesar 11.71%.
Dengan kata lain, akurasi model pada data OOB adalah 88.29%
Interpretasi
Pada machine learning model, terdapat trade-off antara sisi interpretability dan performance. Performance Random Forest dapat diunggulkan dibandingkan model yang lain, namun tidak terlalu dapat diinterpretasi karena banyak faktor random yang terlibat. Namun setidaknya kita dapat melihat predictor apa saja yang paling penting dalam pembuatan Random Forest melalui variable importancenya.
varImp(model_bank_rf)#> rf variable importance
#>
#> only 20 most important variables shown (out of 23)
#>
#> Overall
#> age 100.000
#> previous 93.909
#> balance 62.762
#> housingyes 59.821
#> campaign 27.240
#> jobretired 12.437
#> loanyes 11.710
#> jobstudent 9.895
#> jobblue-collar 9.874
#> educationtertiary 9.656
#> maritalsingle 7.329
#> maritalmarried 6.398
#> educationsecondary 4.320
#> jobmanagement 3.281
#> jobunemployed 2.692
#> jobtechnician 2.271
#> jobservices 1.542
#> jobentrepreneur 1.315
#> educationunknown 1.277
#> jobhousemaid 1.118
Dari hasil data diatas terlihat bahwa variabel age
memiliki pengaruh paling tinggi terhadap hasil.
Beberapa variabel seperti age,
previous, balance, housing, dan
campaign sangat mempengaruhi seseorang setuju mengajukan
program deposit atau tidak.
Berdasarkan urutan tingkat akurasi dan nilai recall, model klasifikasi Random Forest adalah model terbaik. Dengan tingkat akurasi 88.29%. Namun kita masih bisa mengubah nilai cutoff dari tiap model dan melakukan validasi ulang terhadap nilai k-fold khususnya pada model random forest.