Prediksi Pelanggan dari Distributor Grosir

Pendahuluan

Dalam portofolio Learning by Building untuk Machine Learning: Classification 1, saya akan menganalisis data wholesale tentang jenis pelanggan dari distributor grosir berdasarkan variabel-variabel pengeluaran tahunan dari beberapa jenis produknya. Dalam proses pembuatan model prediksi, saya menggunakan logistik regression dan k-nearest neighbor yang termasuk dalam supervised learning.

Data Preparation

Library

Langkah pertama yang harus dilakukan adalah install.package() dan / atau mengaktifkan library() yang dibutuhkan dalam proses analisis dan pembuatan model prediksi.

library(dplyr)
## 
## 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(gtools)
library(gmodels)
library(ggplot2)
library(class)
library(tidyr)
library(caret)
## Loading required package: lattice
library(rsample)
## 
## Attaching package: 'rsample'
## The following object is masked from 'package:gtools':
## 
##     permutations

Load Dataset

Dataset yang akan digunakan adalah data tentang pelanggan dari distributor grosir yang mencakup besaran pengeluaran tahunan berdasarkan produk yang ada.

wholesale <- read.csv("wholesale.csv")

head(wholesale)
##   Channel Region Fresh Milk Grocery Frozen Detergents_Paper Delicassen
## 1       2      3 12669 9656    7561    214             2674       1338
## 2       2      3  7057 9810    9568   1762             3293       1776
## 3       2      3  6353 8808    7684   2405             3516       7844
## 4       1      3 13265 1196    4221   6404              507       1788
## 5       2      3 22615 5410    7198   3915             1777       5185
## 6       2      3  9413 8259    5126    666             1795       1451
  • Channel: Pelanggan dari Distributor Grosir, 1: Horeca (Hotel, Restoran, Cafe) dan 2: Ritel.
  • Region: Wilayah pelanggan, 1: Lisbon, 2: Porto dan 3: lainnya.
  • Fresh: Pengeluaran tahunan untuk produk segar (Fresh).
  • Milk: Pengeluaran tahunan untuk produk susu (Milk).
  • Grocery: Pengeluaran tahunan untuk produk kelontong (Grocery).
  • Frozen: Pengeluaran tahunan untuk produk beku (Frozen).
  • Detergents_Paper: Pengeluaran tahunan untuk produk deterjen dan produk kertas (Detergents & Paper).
  • Delicassen: Pengeluaran tahunan untuk produk delicatessen.

Cek Tipe Data

Dari data yang ada, cek apakah semua tipe data yang ada telah sesuai.

glimpse(wholesale)
## Rows: 440
## Columns: 8
## $ Channel          <int> 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1,…
## $ Region           <int> 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,…
## $ Fresh            <int> 12669, 7057, 6353, 13265, 22615, 9413, 12126, 7579, 5…
## $ Milk             <int> 9656, 9810, 8808, 1196, 5410, 8259, 3199, 4956, 3648,…
## $ Grocery          <int> 7561, 9568, 7684, 4221, 7198, 5126, 6975, 9426, 6192,…
## $ Frozen           <int> 214, 1762, 2405, 6404, 3915, 666, 480, 1669, 425, 115…
## $ Detergents_Paper <int> 2674, 3293, 3516, 507, 1777, 1795, 3140, 3321, 1716, …
## $ Delicassen       <int> 1338, 1776, 7844, 1788, 5185, 1451, 545, 2566, 750, 2…

Menghapus Kolom

Dari data yang sudah ada, kolom Region bisa dihapus.

wholesale <- wholesale %>% 
  select(-Region)

Mengubah Tipe Data

Terdapat 2 kolom yang tipe datanya belum sesuai, sehingga perlu mengubah tipe data dari 2 kolom tersebut.

wholesale <- wholesale %>% 
  mutate(Channel = as.factor(Channel))

glimpse(wholesale)
## Rows: 440
## Columns: 7
## $ Channel          <fct> 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1,…
## $ Fresh            <int> 12669, 7057, 6353, 13265, 22615, 9413, 12126, 7579, 5…
## $ Milk             <int> 9656, 9810, 8808, 1196, 5410, 8259, 3199, 4956, 3648,…
## $ Grocery          <int> 7561, 9568, 7684, 4221, 7198, 5126, 6975, 9426, 6192,…
## $ Frozen           <int> 214, 1762, 2405, 6404, 3915, 666, 480, 1669, 425, 115…
## $ Detergents_Paper <int> 2674, 3293, 3516, 507, 1777, 1795, 3140, 3321, 1716, …
## $ Delicassen       <int> 1338, 1776, 7844, 1788, 5185, 1451, 545, 2566, 750, 2…

Cek Missing Value

Selanjutnya perlu mengetahui apakah ada missing value pada dataset wholesale.

colSums(is.na(wholesale))
##          Channel            Fresh             Milk          Grocery 
##                0                0                0                0 
##           Frozen Detergents_Paper       Delicassen 
##                0                0                0

Cek Proporsi dari Variabel Target wholesale$Channel

prop.table(table(wholesale$Channel))
## 
##         1         2 
## 0.6772727 0.3227273

Membagi Data Train & Test

Data yang sudah ada, dibagi menjadi 2, yaitu data train yang digunakan untuk membuat modal dan data test untuk mengevaluasi model.

set.seed(193)
intrain <- sample(nrow(wholesale), size = 0.8*nrow(wholesale))
wholesale_train <- wholesale[intrain,]
wholesale_test <- wholesale[-intrain,]
wholesale$Channel %>% 
  levels()
## [1] "1" "2"

Logistic Regression

Dengan permodelan Logistic Regression, akan dibuat 2 model, model pertama dengan menggunakan semua variabel pada data dan model kedua menggunakan stepwise.

Model dengan Semua Variabel

Pertama, dibuat model menggunakan semua variabel prediktor.

Model

Untuk pembuatan model, fungsi yang digunakan adalah fungsi glm.

model1 <- glm(formula = Channel~., family = "binomial", 
             data = wholesale)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
summary(model1)
## 
## Call:
## glm(formula = Channel ~ ., family = "binomial", data = wholesale)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.8094  -0.3163  -0.2285   0.0395   3.1918  
## 
## Coefficients:
##                    Estimate Std. Error z value Pr(>|z|)    
## (Intercept)      -3.700e+00  4.375e-01  -8.457  < 2e-16 ***
## Fresh             6.453e-06  1.700e-05   0.380   0.7043    
## Milk              7.372e-05  5.389e-05   1.368   0.1713    
## Grocery           1.177e-04  5.906e-05   1.993   0.0463 *  
## Frozen           -1.671e-04  9.207e-05  -1.815   0.0695 .  
## Detergents_Paper  8.436e-04  1.334e-04   6.324 2.54e-10 ***
## Delicassen       -6.766e-05  1.086e-04  -0.623   0.5333    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 553.44  on 439  degrees of freedom
## Residual deviance: 203.91  on 433  degrees of freedom
## AIC: 217.91
## 
## Number of Fisher Scoring iterations: 7

Prediksi

Dari model yang telah dibuat, selanjutnya membuat prediksi dengan data test yang telah dibagi sebelumnya.

pred1 <- predict(model1, type = "response", newdata = wholesale_test)
pred1_Label <- ifelse(test = pred1 > 0.5,
                      yes = 2,
                      no = 1)

Evaluasi

Dengan data prediksi yang telah dibuat, bisa dilakukan evaluasi dengan data aktual dari data test dengan confusionMatrix.

pred_model1_conf <- confusionMatrix(data = as.factor(pred1_Label), #hasil prediksi dari model yg sudah dibuat
                                    reference = wholesale_test$Channel, #data aktual dari data test
                                    positive = "2")

pred_model1_conf
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2
##          1 56  6
##          2  1 25
##                                          
##                Accuracy : 0.9205         
##                  95% CI : (0.843, 0.9674)
##     No Information Rate : 0.6477         
##     P-Value [Acc > NIR] : 2.664e-09      
##                                          
##                   Kappa : 0.819          
##                                          
##  Mcnemar's Test P-Value : 0.1306         
##                                          
##             Sensitivity : 0.8065         
##             Specificity : 0.9825         
##          Pos Pred Value : 0.9615         
##          Neg Pred Value : 0.9032         
##              Prevalence : 0.3523         
##          Detection Rate : 0.2841         
##    Detection Prevalence : 0.2955         
##       Balanced Accuracy : 0.8945         
##                                          
##        'Positive' Class : 2              
## 

Model dengan Fitting Stepwise

Kedua, model yang dibuat menggunakan stepwise dari model pertama yang menggunakan semua variabel prediktor.

Model

Untuk pembuatan model, fungsi yang digunakan adalah fungsi glm.

model2 <- step(object = model1,
               direction = "backward",
               trace = 0)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
summary(model2)
## 
## Call:
## glm(formula = Channel ~ Grocery + Frozen + Detergents_Paper, 
##     family = "binomial", data = wholesale)
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -2.87319  -0.31394  -0.23700   0.04548   3.01358  
## 
## Coefficients:
##                    Estimate Std. Error z value Pr(>|z|)    
## (Intercept)      -3.622e+00  4.018e-01  -9.015  < 2e-16 ***
## Grocery           1.499e-04  4.989e-05   3.004  0.00267 ** 
## Frozen           -1.383e-04  7.283e-05  -1.899  0.05751 .  
## Detergents_Paper  8.441e-04  1.296e-04   6.513 7.39e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 553.44  on 439  degrees of freedom
## Residual deviance: 206.03  on 436  degrees of freedom
## AIC: 214.03
## 
## Number of Fisher Scoring iterations: 7

Prediksi

Dari model stepwise yang telah dibuat, selanjutnya membuat prediksi dengan data test yang telah dibagi sebelumnya.

pred2 <- predict(model2, type = "response", newdata = wholesale_test)
pred2_Label <- ifelse(test = pred2 > 0.5,
                      yes = 2,
                      no = 1)

Evaluasi

Dengan data prediksi yang telah dibuat, bisa dilakukan evaluasi dengan data aktual dari data test dengan confusionMatrix.

pred_model2_conf <- confusionMatrix(data = as.factor(pred2_Label), #hasil prediksi dari model yg sudah dibuat
                                    reference = wholesale_test$Channel, #data aktual dari data test
                                    positive = "2")

pred_model2_conf
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2
##          1 56  5
##          2  1 26
##                                           
##                Accuracy : 0.9318          
##                  95% CI : (0.8575, 0.9746)
##     No Information Rate : 0.6477          
##     P-Value [Acc > NIR] : 4.07e-10        
##                                           
##                   Kappa : 0.8461          
##                                           
##  Mcnemar's Test P-Value : 0.2207          
##                                           
##             Sensitivity : 0.8387          
##             Specificity : 0.9825          
##          Pos Pred Value : 0.9630          
##          Neg Pred Value : 0.9180          
##              Prevalence : 0.3523          
##          Detection Rate : 0.2955          
##    Detection Prevalence : 0.3068          
##       Balanced Accuracy : 0.9106          
##                                           
##        'Positive' Class : 2               
## 

K-Nearest Neighbour

Untuk model yang ketiga, menggunakan K-Nearest Neighbour.

Data Pre-processing

Pada model KNN ini, tidak perlu membuat model, sehingga diperlukan persiapan data.

Memisahkan Prediktor dan Target

Pada model KNN, variabel prediktor dan variabel target harus dipisahkan.

wholesale_train_x <- wholesale_train %>% 
  select(-Channel)
wholesale_test_x <- wholesale_test %>% 
  select(-Channel)

wholesale_train_y <- wholesale_train$Channel
wholesale_test_y <- wholesale_test$Channel

Scaling Data

summary(wholesale)
##  Channel     Fresh             Milk          Grocery          Frozen       
##  1:298   Min.   :     3   Min.   :   55   Min.   :    3   Min.   :   25.0  
##  2:142   1st Qu.:  3128   1st Qu.: 1533   1st Qu.: 2153   1st Qu.:  742.2  
##          Median :  8504   Median : 3627   Median : 4756   Median : 1526.0  
##          Mean   : 12000   Mean   : 5796   Mean   : 7951   Mean   : 3071.9  
##          3rd Qu.: 16934   3rd Qu.: 7190   3rd Qu.:10656   3rd Qu.: 3554.2  
##          Max.   :112151   Max.   :73498   Max.   :92780   Max.   :60869.0  
##  Detergents_Paper    Delicassen     
##  Min.   :    3.0   Min.   :    3.0  
##  1st Qu.:  256.8   1st Qu.:  408.2  
##  Median :  816.5   Median :  965.5  
##  Mean   : 2881.5   Mean   : 1524.9  
##  3rd Qu.: 3922.0   3rd Qu.: 1820.2  
##  Max.   :40827.0   Max.   :47943.0

Dari range data wholesale, range dari masing-masing variabel memiliki skala yang berbeda-beda. Karena range harus sama karena knn mengklasifikasikan berdasarkan jarak

#tidak menggunakan argumen center dan scale karena data train
wholesale_train_x_scale <- scale(x = wholesale_train_x)

#menggunakan center dan scale dari data train
wholesale_test_x_scale <- scale(x = wholesale_test_x,
                           center = attr(wholesale_train_x_scale, "scaled:center"),
                           scale  =attr(wholesale_train_x_scale, "scaled:scale"))

Prediksi

Sebelum membuat prediksi, dicari nilai k menggunakan akar dari jumlah data.

sqrt(nrow(wholesale_train))
## [1] 18.76166

Karena pada KNN tidak membuat model, maka langsung dibuat prediksi.

pred_knn <- knn(train = wholesale_train_x_scale,
                test = wholesale_test_x_scale,
                cl = wholesale_train_y, #menggunakan dataframe harus dipanggil nama kolomnya/variabelnya
                k = 19)


head(pred_knn)
## [1] 1 1 2 1 1 1
## Levels: 1 2

Evaluasi

Dengan data prediksi yang telah dibuat, bisa dilakukan evaluasi dengan data aktual dari data test dengan confusionMatrix.

pred_knn_conf <- confusionMatrix(data = as.factor(pred_knn), #hasil prediksi dari model yg sudah dibuat
                                 reference = wholesale_test$Channel, #data aktual dari data test
                                 positive = "2")

pred_knn_conf
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2
##          1 55  7
##          2  2 24
##                                           
##                Accuracy : 0.8977          
##                  95% CI : (0.8147, 0.9522)
##     No Information Rate : 0.6477          
##     P-Value [Acc > NIR] : 7.518e-08       
##                                           
##                   Kappa : 0.7673          
##                                           
##  Mcnemar's Test P-Value : 0.1824          
##                                           
##             Sensitivity : 0.7742          
##             Specificity : 0.9649          
##          Pos Pred Value : 0.9231          
##          Neg Pred Value : 0.8871          
##              Prevalence : 0.3523          
##          Detection Rate : 0.2727          
##    Detection Prevalence : 0.2955          
##       Balanced Accuracy : 0.8696          
##                                           
##        'Positive' Class : 2               
## 

Model Evaluation

Dari semua model dan prediksi yang telah dibuat, dibandingkan hasil evaluasi dari masing-masing confusionMatrix setiap model.

model_eval <- data.frame(Model_prediksi = c("Model Logit Semua Prediktor", "Model Logit Step", "Model KNN"),
                         Accuracy = c(pred_model1_conf$overall[1],pred_model2_conf$overall[1],pred_knn_conf$overall[1]),
                         Recall = c(pred_model1_conf$byClass[1],pred_model2_conf$byClass[1],pred_knn_conf$byClass[1]),
                         Specificity = c(pred_model1_conf$byClass[2],pred_model2_conf$byClass[2],pred_knn_conf$byClass[2]),
                         Precision = c(pred_model1_conf$byClass[3],pred_model2_conf$byClass[3],pred_knn_conf$byClass[3]))

model_eval
##                Model_prediksi  Accuracy    Recall Specificity Precision
## 1 Model Logit Semua Prediktor 0.9204545 0.8064516   0.9824561 0.9615385
## 2            Model Logit Step 0.9318182 0.8387097   0.9824561 0.9629630
## 3                   Model KNN 0.8977273 0.7741935   0.9649123 0.9230769

Conclusion

Dari data wholesale, telah dibuat model dengan algoritma logistic regression dan K-Nearest Neighbour. Hasil evaluasi dari prediksi pada semua model, menunjukkan prediksi dengan K-Nearest Neighbour menghasilkan model yang lebih baik dibanding model dengan logistic regression.