library(tidyverse)
library(dplyr) 
library(ggplot2)
library(inspectdf)
library(GGally)
library(plotly)
library(e1071)
library(rsample)
library(randomForest)
library(DMwR)
library(ROCR)
library(caret)
library(partykit)

Background

Telemarketing adalah salah satu cara promosi yang masih dianggap efektif selain memasang iklan di media umum. Pengertian telemarketing yaitu memasarkan atau mensosialisasikan produk atau jasa melalui telepon. Biasanya, strategi pemasaran melalui telemarketing ini dipakai oleh beberapa perusahaan besar untuk menawarkan produknya. Menurut banyak ahli pemasaran, penawaran melalui telemarketing cenderung mudah diterima, karena sifatnya memang berbicara secara personal langsung kepada konsumen.

Bank merupakan salah satu lembaga yang menggunakan metode telmarketing untuk menjual product product yang ada di bank atau untuk menawarkan jasa. Bank sebagai lembaga financing sangat memperhatikan tingkat baiknya reputasi dan branding yang dimiliki, dan salah satu hal buruk yang dilakukan telemarketing dapat mengganggu reputasi itu sendiri. Jadi perlu dicari target mana yang akan membeli atau tudak membeli produk atau jasa yang akan ditawarkan oleh bank menggunakan metode telemarketing.

Analysis Method

Dalam kasus ini kita akan menggunakan machine learning untuk melihat suatu pola dan menprediksi target dari telemarketing. Model yang akan kita gunakan adalah random forest dan decision tree.

Kita akan membandingkan kedua hasil prediksi dari kedua model tersebut. Dua model ini masuk kedalam kategori supervised learning, berdasarkan fitur tersebut kita akan membandingkan pola klasifikasi dari model yang didapat dari data untuk mendapatkan prediksi akhir yang terbaik.

Data Preparation

Read Data

Disini kita akan read data telemarketing yang kita dapatkan dari UCI Machine Learning Repository, data tersebut terkait dengan kampanye pemasaran dari lembaga perbankan Portugis. Dan disini kita akan menyimpan dataset kedalam bank.

bank <- read.csv("data_input/bank.csv", sep=";")
head(bank)
glimpse(bank)
## Rows: 4,521
## Columns: 17
## $ age       <int> 30, 33, 35, 30, 59, 35, 36, 39, 41, 43, 39, 43, 36, 20, 3...
## $ job       <chr> "unemployed", "services", "management", "management", "bl...
## $ marital   <chr> "married", "married", "single", "married", "married", "si...
## $ education <chr> "primary", "secondary", "tertiary", "tertiary", "secondar...
## $ default   <chr> "no", "no", "no", "no", "no", "no", "no", "no", "no", "no...
## $ balance   <int> 1787, 4789, 1350, 1476, 0, 747, 307, 147, 221, -88, 9374,...
## $ housing   <chr> "no", "yes", "yes", "yes", "yes", "no", "yes", "yes", "ye...
## $ loan      <chr> "no", "yes", "no", "yes", "no", "no", "no", "no", "no", "...
## $ contact   <chr> "cellular", "cellular", "cellular", "unknown", "unknown",...
## $ day       <int> 19, 11, 16, 3, 5, 23, 14, 6, 14, 17, 20, 17, 13, 30, 29, ...
## $ month     <chr> "oct", "may", "apr", "jun", "may", "feb", "may", "may", "...
## $ duration  <int> 79, 220, 185, 199, 226, 141, 341, 151, 57, 313, 273, 113,...
## $ campaign  <int> 1, 1, 1, 4, 1, 2, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 5, 1, 1, ...
## $ pdays     <int> -1, 339, 330, -1, -1, 176, 330, -1, -1, 147, -1, -1, -1, ...
## $ previous  <int> 0, 4, 1, 0, 0, 3, 2, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 2, 0, ...
## $ poutcome  <chr> "unknown", "failure", "failure", "unknown", "unknown", "f...
## $ y         <chr> "no", "no", "no", "no", "no", "no", "no", "no", "no", "no...

Data Wrangling

Pertama kita akan mengecek apakah didalam dataset yang kita miliki terdapat missing values.

colSums(is.na(bank))
##       age       job   marital education   default   balance   housing      loan 
##         0         0         0         0         0         0         0         0 
##   contact       day     month  duration  campaign     pdays  previous  poutcome 
##         0         0         0         0         0         0         0         0 
##         y 
##         0

Data bank tidak memiliki missing values, jadi kita akan langsung merubah beberapa kolom varibel ke tipe data yang seharusnya. Dan kita akan mengganti kolom target kita dari y menjadi subscribe.

bank <- bank %>% 
  mutate(job = as.factor(job)) %>% 
  mutate(marital = as.factor(marital)) %>% 
  mutate(education = as.factor(education)) %>% 
  mutate(default = as.factor(default)) %>% 
  mutate(housing = as.factor(housing)) %>%
  mutate(month = as.factor(month)) %>% 
  mutate(loan = as.factor(loan)) %>% 
  mutate(contact = as.factor(contact)) %>% 
  mutate(poutcome = as.factor(poutcome)) %>% 
  mutate(subscribe = as.factor(y))

Setelah merubah variabel penting kedalam tipe data yang benar. Kita akan menampilkan beberapa variabel numeric dengan histogram untuk mendapatkan distribusinya.

numericCols <- unlist(lapply(bank, is.numeric))
show_plot(inspect_num(bank[,numericCols]))

# Cross Validation

Disini kita akan membagi dataset yang kita miliki menjadi dua subset yaitu data train dan data test. Dan kita memisahkan data dengan proporsi 80% untuk data train dan 20% untuk data test.

RNGkind(sample.kind = "Rounding")
## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(100)
intrain <- sample(nrow(bank), nrow(bank)*0.8)
bank_train <- bank[intrain,]
bank_test <- bank[-intrain,]

Setelah kita bagi data, alagkah baiknya kita cek terlebih dahulu apakah proporsi kelas target yang kita miliki sudah balance.

prop.table(table(bank_train$subscribe))
## 
##        no       yes 
## 0.8821903 0.1178097

Setelah kita cek ternyata terjadi imbalance dalam proporsi kelas target, agar model yang kita buat dapat memprediksi secara baik, kita akan melakukan metode upsampling karena data yang kita milik tidak terlalu banyak.

bank_train <- upSample(x = bank_train %>% select(-subscribe),
                         y = as.factor(bank_train$subscribe),
                         yname = "subscribe")

Modeling

Random Forest

Model pertama yang kita gunakan adalah model random forest.Random Forest melakukan prediksi dengan membuat banyak Decision Tree. Masing-masing Decision Tree memiliki karakteristik masing-masing dan tidak saling berhubungan satu sama lain. Random Forest kemudian melakukan prediksi untuk masing-masing decision tree, kemudian dari sekian banyak hasil prediksi tersebut dilakukan voting.

Traincontrol future selection yang menggunakan keuntungan dari random forest yaitu automatic feature selection: pemilihan prediktor secara otomatis dan random didalam pembuatan decision tree - method: traincontrol - number: 5 - repeats: r

set.seed(123)
control <- trainControl(method = "repeatedcv", number = 5, repeats = 4)

# pembuatan model random forest
bank_model <- train(subscribe ~ ., data = bank_train, method = "rf", trControl = control)
# simpan model
saveRDS(bank_model, "bank_forest.RDS")
bank_model

Setelah kita membuat model random forest kita akan menyimpan model tersebut kedalam bank_forest dan memanggil model tersebut untuk digunakan kedalam prediksi. Penting untung menyimpan model random forest agar kita tidak menjalankan model secara berulang dikarenakan kelamahan dari random forest adalah komputasinya yang lama.

bank_model_rf <- readRDS("bank_forest.RDS")
bank_model_rf
## Random Forest 
## 
## 6380 samples
##   16 predictor
##    2 classes: 'no', 'yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold, repeated 4 times) 
## Summary of sample sizes: 5104, 5104, 5104, 5104, 5104, 5104, ... 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##    2    0.8851097  0.7702194
##   22    0.9681034  0.9362069
##   42    0.9631270  0.9262539
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 22.

Kita lihat final model didalam model random forest yang kita buat

bank_model_rf$finalModel
## 
## 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: 22
## 
##         OOB estimate of  error rate: 2.49%
## Confusion matrix:
##       no  yes class.error
## no  3031  159  0.04984326
## yes    0 3190  0.00000000

Pada model fb_forest, nilai Out of Bag Error sebesar 2.49%. Dengan kata lain, akurasi model pada data test (out of bag data) adalah 97,51%. Selanjutnya kita akan menggunakan model yang telah kita buat kedalam prediksi dengan menggunakan data test yang telah kita buat dan kita akan mengecek apakah model yang kita buat memiliki accuracy, Specificity , precision, dan sensitivity yang tinggi.

bank_pred_rf <- predict(bank_model_rf, newdata = bank_test)
confusionMatrix(bank_pred_rf , bank_test$subscribe, positive = "yes")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  no yes
##        no  764  52
##        yes  46  43
##                                           
##                Accuracy : 0.8917          
##                  95% CI : (0.8696, 0.9112)
##     No Information Rate : 0.895           
##     P-Value [Acc > NIR] : 0.6524          
##                                           
##                   Kappa : 0.4072          
##                                           
##  Mcnemar's Test P-Value : 0.6135          
##                                           
##             Sensitivity : 0.45263         
##             Specificity : 0.94321         
##          Pos Pred Value : 0.48315         
##          Neg Pred Value : 0.93627         
##              Prevalence : 0.10497         
##          Detection Rate : 0.04751         
##    Detection Prevalence : 0.09834         
##       Balanced Accuracy : 0.69792         
##                                           
##        'Positive' Class : yes             
## 

Dapat dilihat walaupun memiliki tingkat akurasi dan Specificity yang baik, tapi dari hasil prediksi diatas model kita memiliki tinggkat sensitivity dan precision yang rendah. Kita perlu membandingkan hasil prediksi model random forest dengan model selanjutnya yaitu decision tree.

Decision tree

Decision Tree adalah model yang terbilang cukup sederhana, namun performanya robust/powerfull dan hasilnya dapat diinterpretasi dengan mudah. Model decision tree merupakan dasar dari tree-based model lainnya seperti Random Forest yang terkenal robust untuk menghasilkan prediksi. Sama seperti model random forest dikarenakan proporsi tarvet variabel kita tidak balance kita akan melakukan metode upsampling pada data train.

bank_train_up <- upSample(x = bank_train %>% select(-subscribe),
                         y = as.factor(bank_train$subscribe),
                         yname = "subscribe")

Setelah itu kita akan buat model decision treenya.

set.seed(128)
bank_model_dtree <- ctree(subscribe ~ ., bank_train_up)

Setelah itu kita akan memprediksi model yang kita buat kedalam data test

bank_pred_dtree <- predict(bank_model_dtree, bank_test)
confusionMatrix(bank_pred_dtree, bank_test$subscribe, positive = "yes")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  no yes
##        no  662  26
##        yes 148  69
##                                           
##                Accuracy : 0.8077          
##                  95% CI : (0.7805, 0.8329)
##     No Information Rate : 0.895           
##     P-Value [Acc > NIR] : 1               
##                                           
##                   Kappa : 0.3469          
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.72632         
##             Specificity : 0.81728         
##          Pos Pred Value : 0.31797         
##          Neg Pred Value : 0.96221         
##              Prevalence : 0.10497         
##          Detection Rate : 0.07624         
##    Detection Prevalence : 0.23978         
##       Balanced Accuracy : 0.77180         
##                                           
##        'Positive' Class : yes             
## 

Dapat dilihat hasil accuracy, recall, dan specificity sudah cukup baik, tapi nilai precision kita masih terlalu rendah yaitu 31%. Model kita masih dapat di imprivo dengan melakukan tuning.

set.seed(128)
bank_dtree_tuning <- ctree(subscribe ~ ., bank_train_up,
                            control = ctree_control(mincriterion = 0.09, minsplit = 100, minbucket = 60))

dtree_prediction_tuning <- predict(bank_dtree_tuning, bank_test, positive = "yes")

Mari kita tuning agar model yang kita buat tidak terlalu kompleks dengan menggunakan mincriterion 0,1, minsplit 60 dan minbucket 50. Setelah itu kita akan prediksi kembali kedalam data test.

confusionMatrix(dtree_prediction_tuning, bank_test$subscribe)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  no yes
##        no  610  16
##        yes 200  79
##                                           
##                Accuracy : 0.7613          
##                  95% CI : (0.7322, 0.7888)
##     No Information Rate : 0.895           
##     P-Value [Acc > NIR] : 1               
##                                           
##                   Kappa : 0.3152          
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 0.7531          
##             Specificity : 0.8316          
##          Pos Pred Value : 0.9744          
##          Neg Pred Value : 0.2832          
##              Prevalence : 0.8950          
##          Detection Rate : 0.6740          
##    Detection Prevalence : 0.6917          
##       Balanced Accuracy : 0.7923          
##                                           
##        'Positive' Class : no              
## 

Setelah kita tuning terjadi peningkatan pada precision dari 31% menjadi 97%. Ini merupakan hasil yang sangat baik dan cukup.

Conclusion

Dari dua model diatas dapat dilihat model decision tree memiliki overall matrix yang sangat baik, dan dalam kasus telemerketing bank kita akan fokus pada target “No” yang artinya kita tidak ingin target dari telemarketing yang diramalkan tidak akan membeli produk atau jasa yang ditawarkan oleh telemarketing, karena kita tidak ingin reputasi bank menurun akibat kontak yang tidak perlu atau di anggap mengganggu, maka dapat dilihat model dtree_prediction_tuning memilki nilai sensitivity yang paling tinggi yaitu 75%.