Beberapa package yang akan digunakan dalam laporan ini antara lain sebagai berikut:
library(readxl) # untuk membaca file excel
library(sjmisc) # untuk deskripsi statistik data
library(grid) # untuk menyusun >1 plot dalam 1 frame
library(gridExtra) # untuk menyusun >1 plot dalam 1 frame
library(ggplot2) # membuat plot
library(lattice) # membuat barchart
library(smotefamily) # melakukan SMOTE
library(randomForest) # membentuk model Random Forest
## randomForest 4.7-1.1
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
##
## margin
## The following object is masked from 'package:gridExtra':
##
## combine
library(randomForestExplainer) # memperoleh visualisasi hasil pemodelan RF
## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
library(caret) # membuat matriks konfusi
library(pROC) # membuat ROC curve
## Type 'citation("pROC")' for a citation.
##
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
##
## cov, smooth, var
Sebuah operator telekomunikasi seluler akan menjajagi kerja sama dengan sebuah bank untuk meningkatkan angka recharge pelanggan pra-bayar melalui channel perbankan (ATM, internet banking, dsb). Mereka ingin memperoleh model yang dapat digunakan untuk memprediksi pelanggan mana saja yang potensial untuk diajak menggunakan channel tersebut.
data <- read_excel('Tugas_STA581.xlsx')
data <- data[-1] # menghapus kolom ID dalam data
Digunakan package readxl untuk membaca data excel ke
dalam bentuk dataframe di R.
head(data)
## # A tibble: 6 × 12
## X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 Y
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 257 13643 27 0.780 0.963 5217 0.769 768 30006 6.3 4.7 1
## 2 24 13897 15 0.739 0.906 18025 0.291 2368 39954 7.3 5.3 1
## 3 1 760 1 0.5 1 3275 0.962 2542 43544 1.3 1.3 0
## 4 627 41160 97 0.707 0.998 2864 0.723 0 0 9.3 5 0
## 5 42 19002 38 0.750 0.964 2794 0.748 0 0 4 1 0
## 6 65 38253 43 0.779 1 1122 1 5 1719 17 14.7 1
Sebelumnya dilakukan pembagian data ke dalam data latih dan data uji yang selanjutnya data yang sama ini akan dipakai menjadi data latih dan data uji di semua metode.
eksp_data <- data
eksp_data$Y <- as.factor(eksp_data$Y)
as.data.frame(descr(eksp_data))
## var type label n NA.prc mean sd se
## 1 X1 numeric X1 50000 0 1.106676e+02 1.806347e+02 8.078230e-01
## 4 X2 numeric X2 50000 0 2.790754e+04 2.736161e+04 1.223649e+02
## 5 X3 numeric X3 50000 0 4.023486e+01 5.120954e+01 2.290160e-01
## 6 X4 numeric X4 50000 0 7.342076e-01 1.395345e-01 6.240174e-04
## 7 X5 numeric X5 50000 0 8.824384e-01 1.950748e-01 8.724011e-04
## 8 X6 numeric X6 50000 0 6.745711e+03 7.915107e+03 3.539743e+01
## 9 X7 numeric X7 50000 0 7.718046e-01 2.549293e-01 1.140078e-03
## 10 X8 numeric X8 50000 0 9.468719e+02 2.260855e+03 1.011085e+01
## 11 X9 numeric X9 50000 0 1.614417e+04 2.682608e+04 1.199699e+02
## 2 X10 numeric X10 50000 0 7.129130e+00 4.841823e+00 2.165329e-02
## 3 X11 numeric X11 50000 0 3.676492e+00 2.414185e+00 1.079656e-02
## 12 Y categorical Y 50000 0 1.867200e-01 3.896905e-01 1.742749e-03
## md trimmed range iqr skew
## 1 4.700000e+01 7.218118e+01 4130 (0-4130) 1.170000e+02 4.657092
## 4 2.209200e+04 2.385257e+04 638172 (0-638172) 2.600850e+04 3.929080
## 5 2.400000e+01 3.022100e+01 1007 (0-1007) 4.000000e+01 3.717217
## 6 7.498324e-01 7.485643e-01 1 (0-1) 1.035349e-01 -2.745926
## 7 9.702921e-01 9.286175e-01 1 (0-1) 1.437488e-01 -2.588062
## 8 3.975000e+03 5.284058e+03 244444 (0-244444) 7.839000e+03 3.215393
## 9 8.626017e-01 8.155916e-01 1 (0-1) 3.416490e-01 -1.314261
## 10 7.300000e+01 4.329732e+02 59611 (0-59611) 9.250000e+02 5.749438
## 11 2.514500e+03 1.042832e+04 504086 (0-504086) 2.443125e+04 3.386872
## 2 6.000000e+00 6.523237e+00 30.7 (0-30.7) 6.400000e+00 1.207519
## 3 3.000000e+00 3.349972e+00 35.7 (0-35.7) 2.700000e+00 1.991754
## 12 0.000000e+00 1.084000e-01 1 (0-1) 0.000000e+00 1.607904
Keterangan:
ggplot(eksp_data, aes(x = factor(1), fill = Y)) + geom_bar(width = 1) + scale_fill_brewer(palette="Set2") + coord_polar("y") + stat_count(geom = "text", aes(label = stat(count)/50000), position=position_stack(vjust=0.5), colour="white")
## Warning: `stat(count)` was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
Dari plot di atas diperoleh bahwa dari 50000 pelanggan yang diamati, sebanyak 18.672% pelanggan merupakan pelanggan potensial dan sebanyak 81.328% sisanya bukan merupakan pelanggan yang potensial untuk diajak menggunakan channel perbankan.
x1 <- ggplot(eksp_data, aes(X1)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#3F2B44") +
geom_density(color = "#000000", fill = "#E3ADB5", alpha = 0.6) + ggtitle('Rata-rata Durasi Melakukan Panggilan per Bulan (menit)')
x2 <- ggplot(eksp_data, aes(X2)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#231F20") +
geom_density(color = "#000000", fill = "#95DFE3", alpha = 0.6) + ggtitle('Rata-rata Penghasilan dari Transaksi Panggilan per Bulan (Rp)')
x3 <- ggplot(eksp_data, aes(X3)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#3F2B44") +
geom_density(color = "#000000", fill = "#E3ADB5", alpha = 0.6) + ggtitle('Rata-rata Frekuensi Melakukan Panggilan per Bulan')
x4 <- ggplot(eksp_data, aes(X4)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#3F2B44") +
geom_density(color = "#000000", fill = "#E3ADB5", alpha = 0.6) + ggtitle('Persentase Panggilan di Jam Kerja')
x5 <- ggplot(eksp_data, aes(X5)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#454727") +
geom_density(color = "#000000", fill = "#95B8E3", alpha = 0.6) +ggtitle('Persentase Panggilan Sesama Operator')
x6 <- ggplot(eksp_data, aes(X6)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#231F20") +
geom_density(color = "#000000", fill = "#95DFE3", alpha = 0.6) +ggtitle('Rata-rata Penghasilan dari Transaksi SMS per Bulan (Rp)')
x7 <- ggplot(eksp_data, aes(X7)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#454727") +
geom_density(color = "#000000", fill = "#95B8E3", alpha = 0.6) + ggtitle("Persentase Transaksi SMS Sesama Operator")
x8 <- ggplot(eksp_data, aes(X8)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#3E2A1F") +
geom_density(color = "#000000", fill = "#A99887", alpha = 0.6) + ggtitle("Rata-rata Pemakaian Data per Bulan (MB)")
x9 <- ggplot(eksp_data, aes(X9)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#231F20") +
geom_density(color = "#000000", fill = "#95DFE3", alpha = 0.6) + ggtitle("Rata-rata Penghasilan dari Transaksi Pemakaian Data per Bulan (Rp)")
x10 <- ggplot(eksp_data, aes(X10)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#3E2A1F") +
geom_density(color = "#000000", fill = "#A99887", alpha = 0.6) + ggtitle("Rata-rata Frekuensi Melakukan Recharge Pulsa per Bulan")
x11 <- ggplot(eksp_data, aes(X11)) +
geom_histogram(aes(y = ..density..), color = "#000000", fill = "#3E2A1F") +
geom_density(color = "#000000", fill = "#A99887", alpha = 0.6) + ggtitle("Rata-rata Banyaknya Wilayah yang Dikunjungi per Bulan")
blank<-rectGrob(gp=gpar(col="white"))
figure1 <- grid.arrange(blank, x1, blank,x3, blank, x4, blank, ncol=1, heights = c(1/40, 3/10, 1/40, 3/10, 1/40, 3/10, 1/40))
## Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
figure2 <- grid.arrange(blank, x2, blank,x6, blank, x9, blank, ncol=1, heights = c(1/40, 3/10, 1/40, 3/10, 1/40, 3/10, 1/40))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
figure3 <- grid.arrange(blank, x8, blank,x10, blank, x11, blank, ncol=1, heights = c(1/40, 3/10, 1/40, 3/10, 1/40, 3/10, 1/40))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
figure4 <- grid.arrange(blank, x5, blank,x7, blank, ncol=1, heights = c(1/30, 4.5/10, 1/30, 4.5/10, 1/30))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Metode Synthetic Minority Over-sampling Technique (SMOTE) merupakan metode yang populer diterapkan dalam rangka menangani ketidak seimbangan kelas. Teknik ini mensintesis sampel baru dari kelas minoritas untuk menyeimbangkan dataset dengan cara sampling ulang sampel kelas minoritas.
# data tanpa SMOTE
set.seed(2022)
sample <- sample(c(TRUE, FALSE), nrow(data), replace=TRUE, prob=c(0.8,0.2))
train <- data[sample, ]
test <- data[!sample, ]
Dalam hal ini, ketidakseimbangan kelas dalam data train dilihat dalam grafik berikut:
barchart(as.factor(train$Y), col='maroon')
Terlihat bahwa proporsi pelanggan yang tidak potensial jauh lebih besar daripada pelanggan potensial. Karena data latih digunakan untuk membentuk model prediksi padahal datanya tidak seimbang, maka perlu dilakukan SMOTE.
smote_data <- SMOTE(data[,-12], data$Y)
newdata <- smote_data$data
set.seed(2022)
newdata$class <- as.numeric(newdata$class)
sample <- sample(c(TRUE, FALSE), nrow(newdata), replace=TRUE, prob=c(0.8,0.2))
newtrain <- newdata[sample, ]
newtest <- newdata[!sample, ]
barchart(as.factor(newtrain$class), col='navy')
Terlihat bahwa data latih sudah balance dan siap digunakan dalam
klasifikasi.
Random Forest adalah kombinasi dari masing – masing tree yang baik kemudian dikombinasikan ke dalam satu model. Random Forest bergantung pada sebuah nilai vector random dengan distribusi yang sama pada semua pohon yang masing masing decision tree memiliki kedalaman yang maksimal. Random Forest adalah classifier yang terdiri dari classifier yang berbentuk pohon {\(h(x, \theta_k), k=1. ...\)} dimana \(\theta_k\) adalah random vector yang diditribusikan secara independen dan masing masing tree pada sebuah unit kan memilih class yang paling popular pada input x.
rf_data <- newdata
rf_train <- newtrain
rf_test <- test
Model RF dibentuk dengan menggunakan fungsi
randomForest() dan kemudian diperoleh hasil akurasi sebagai
berikut:
set.seed(2022)
rf_model <- randomForest(data=rf_train,
as.factor(class)~.,
ntree=500)
rf_model
##
## Call:
## randomForest(formula = as.factor(class) ~ ., data = rf_train, ntree = 500)
## Type of random forest: classification
## Number of trees: 500
## No. of variables tried at each split: 3
##
## OOB estimate of error rate: 14.17%
## Confusion matrix:
## 0 1 class.error
## 0 28377 4159 0.1278276
## 1 4678 25158 0.1567905
plot(rf_model)
Plot di atas merupakan error yang dihasilkan dari banyak pohon yang berbeda-beda. Model yang dibentuk sebelumnya memiliki 500 pohon dengan error rate terkecil.
varImpPlot(rf_model)
Plot di atas menjelaskan kontribusi masing-masing peubah di mana X11 menjadi peubah prediktor yang paling penting dalam memprediksi Y.
rf_pred <- predict(rf_model, rf_test, type="prob")
rf_predict <- ifelse(rf_pred[,2] > 0.5, 1, 0)
rf_conf <- confusionMatrix(as.factor(rf_predict), as.factor(rf_test$Y), positive = "1", mode='everything')
rf_conf
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 7983 170
## 1 203 1656
##
## Accuracy : 0.9627
## 95% CI : (0.9588, 0.9664)
## No Information Rate : 0.8176
## P-Value [Acc > NIR] : < 2e-16
##
## Kappa : 0.876
##
## Mcnemar's Test P-Value : 0.09754
##
## Sensitivity : 0.9069
## Specificity : 0.9752
## Pos Pred Value : 0.8908
## Neg Pred Value : 0.9791
## Precision : 0.8908
## Recall : 0.9069
## F1 : 0.8988
## Prevalence : 0.1824
## Detection Rate : 0.1654
## Detection Prevalence : 0.1857
## Balanced Accuracy : 0.9411
##
## 'Positive' Class : 1
##
Diperoleh nilai-nilai akurasi sebagai berikut:
rf_roc <- roc(rf_test$Y, rf_pred[,2], direction = "<", plot = T)
## Setting levels: control = 0, case = 1
rf_roc$auc
## Area under the curve: 0.9854
Diperoleh nilai AUC sebesar 98.54% yang artinya model mampu mengklasifikasikan 98.54% kelas dengan benar. Sehingga, berdasarkan klasifikasi dengan random forest diperoleh ukuran-ukuran akurasi sebagai berikut:
rf_df <- data.frame(Metode = 'Rand Forest',
Akurasi = rf_conf$overall[1],
Sensitivitas = rf_conf$byClass[1],
Spesifisitas = rf_conf$byClass[2],
AUC = rf_roc$auc)
rf_df
## Metode Akurasi Sensitivitas Spesifisitas AUC
## Accuracy Rand Forest 0.9627447 0.9069003 0.9752016 0.9854496
rf_result <- data.frame(actual = rf_test$Y,
predicted = rf_predict)
ggplot(rf_result, aes(x = factor(1), fill = as.factor(predicted))) + geom_bar(width = 1) + scale_fill_brewer(palette="Set1") + coord_polar("y") + stat_count(geom = "text", aes(label = stat(count)/10012), position=position_stack(vjust=0.5), colour="white")
Berdasarkan hasil prediksi yang terbentuk, diperoleh bahwa sebanyak 18.57% pelanggan diprediksi akan menjadi pelanggan potensial dan sisanya sebanyak 81.43 menjadi pelanggan tidak potensial. Selanjutnya akan dilihat bagaimana hubungan masing-masing peubah prediktor dengan peubah respon.
plot_predict_interaction(rf_model, rf_test, 'X2', 'X10')
Pelanggan yang menghasilkan transaksi panggilan kecil dan sering
melakukan recharge pulsa per bulan cenderung diklasifikasikan dalam
kelas pelanggan tidak potensial. Sebaliknya, pelanggan dengan nominal
transaksi panggilan besar (> Rp50000) dengan frekuensi recharge pulsa
per bulan < 10, cenderung diklasifikasikan ke dalam pelanggan
potensial.
plot_predict_interaction(rf_model, rf_test, 'X4', 'X6')
Pelanggan yang melakukan panggilan di jam kerja secara intens
(presentase > 75%) dengan nominal transaksi SMS besar ( > Rp50000)
cenderung diklasifikasikan ke dalam pelanggan potensial.
plot_predict_interaction(rf_model, rf_test, 'X6', 'X10')
Pelanggan yang memiliki nominal transaksi SMS bulanan kecil (<
Rp50000) dengan rata-rata frekuensi recharge pulsa per bulan banyak
(> 10 kali) cenderung diklasifikasikan ke dalam pelanggan tidak
potensial.
plot_predict_interaction(rf_model, rf_test, 'X8', 'X9')
Pelanggan yang memiliki rata-rata pemakaian data kecil dalam sebulan
serta rata-rata mengeluarkan nominal yang kecil untuk data bulanan
cenderung diklasifikasikan ke dalam kelompok pelanggan tidak
potensial.
plot_predict_interaction(rf_model, rf_test, 'X10', 'X11')
Pelanggan yang tidak mengunjungi banyak wilayah selama sebulan (< 5 wilayah) dengan rata-rata recharge pulsa di bawah 10 kali selama sebulan cenderung diklasifikasikan ke dalam pelanggan potensial.