Data wholesale.csv merupakan data yang merujuk terkait
klien distributor wholesale (grosir). Data ini menjelaskan terkait
pengeluaran tahunan dalam satuan moneter (monetary unit / m.u) untuk
beragam kategori produk.
Data wholesale.csv dapat dilihat diambil dari link
berikut ini : https://www.kaggle.com/datasets/sahistapatel96/wholesale-customer-datacsv.
Pada data ini, terdapat target variabel Channel yang
merupakan tempat belanja (channel) customer membeli produk-produk yang
dijual oleh distributor wholesale tersebut. Dengan kata lain, target
variabel ini juga menandakan segmentasi customer terhadap produk-produk
yang menjadi variabel prediktor di data ini nantinya.
Pada kesempatan kali ini, saya akan mencoba melakukan analisis segmentasi customer terkait data wholesale ini. Tujuannya adalah untuk memprediksi apakah customer tersebut termasuk dalam segmen / channel yang mana berdasarkan data-data penjualan produk-produk yang ada.
Algoritma yang akan saya gunakan yaitu menggunakan Logistic Regression dan K-Nearest Neighbor yang termasuk dalam supervised learning.
Kita juga akan evaluasi kedua model tersebut dan bandingkan, manakah model yang lebih baik evaluasinya dan berdasarkan dari sisi pemahaman bisnis nantinya, nilai evaluasi apa (accuracy, recall atau precision) yang akan kita jadikan acuan dalam memilih model untuk analisis segmentasi customer wholesale ini.
Kita panggil setiap library yang dibutuhkan dalam proses pembuatan model klasifikasi hingga evaluasi dan hasil interpretasi model nantinya.
Kita mulai dengan membaca dataset wholesale.csv dan kita
simpan dalam variabel wholesale.
Cek struktur data:
#> 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…
Berikut ini adalah penjelasan terkait variabel-variabel tersebut:
Channel : Channel / segmen pelanggan yang dituju,
terbagi menjadi 2 kelas: 1. Horeca -> hotel, restoran, cafe 2. Retail
-> supermarket, toko, dsbRegion : Wilayah pembelanjaan customer, dibagi menjadi
3 kelas: 1 (Lisbon), 2 (Porto), 3 (Wilayah lain)Fresh : Pengeluaran tahunan untuk produk sayuran /
buah-buahanMilk : Pengeluaran tahunan untuk produk susu dan olahan
susuGrocery : Pengeluaran tahunan untuk produk bahan
kebutuhan sehari-hariFrozen : Pengeluaran tahunan untuk produk makanan
bekuDetergents_Paper : Pengeluaran tahunan untuk produk
seperti sabun / detergen dan produk alat tulis / kertasDelicassen : Pengeluaran tahunan untuk produk
makanan-makanan khusus untuk restoran yang khusus, seperti sosis, ham,
dsbKita mencoba untuk melihat persebaran data dari setiap variabel pada data tersebut.
Terdapat 2 kelas yang akan menjadi target variabel yang merupakan
segmen customer nantinya akan kita petakan. Kita lihat pembagian /
proporsi dari masing-masing kelas pada variabel Channel
tersebut.
#>
#> 1 2
#> 298 142
Ternyata kelas 1 (Horeca) lebih besar / banyak jumlahnya
dibandingkan kelas 2 (Retail).
Selain itu, terdapat variabel Region yang terdiri dari 3
nilai. Kita lihat pembagian / proporsi dari masing-masing kelas pada
variabel tersebut.
#>
#> 1 2 3
#> 77 47 316
Terlihat bahwa kelas 3 yaitu wilayah lain, lebih banyak sebaran datanya dibandingkan wilayah 1 (Lisbon) dan 2 (Porto).
Berdasarkan penjelasan dari setiap variabel tersebut, kita perlu
melakukan konversi terhadap 2 variabel yaitu Channel dan
Region yang dapat kita ubah menjadi tipe data factor.
wholesale <- wholesale %>%
mutate(Channel = as.factor(Channel),
Region = as.factor (Region))
glimpse(wholesale)#> Rows: 440
#> Columns: 8
#> $ Channel <fct> 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1,…
#> $ Region <fct> 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…
Sebelum melakukan pemodelan, kita akan pecah data
wholesale dengan pembagian proporsi 80% untuk data
train dan 20% untuk data test menggunakan fungsi
sample(). Menggunakan set.seed() dengan
besaran 123. Menyimpan hasil pemisahan data pada object
train_wholesale dan test_wholesale.
RNGkind(sample.kind = "Rounding")
set.seed(123)
splitter <- initial_split(data = wholesale, prop = 0.8)
train_wholesale <- training(splitter)
test_wholesale <- testing(splitter)Kita lihat lagi proporsi dari target variabel yang kita miliki pada data train kita.
#>
#> 1 2
#> 0.6846591 0.3153409
Ternyata proporsi variabel target pada data train tidak seimbang, yaitu 68 : 32.
Karena perbedaan banyaknya data antar kelas pada variabel target
yaitu 1 dan 2 cukup jauh, kita akan melakukan
upsampling.
RNGkind(sample.kind = "Rounding")
set.seed(123)
train_up_wholesale <- upSample(
x = train_wholesale %>% select(-Channel),
y = train_wholesale$Channel,
yname = "Channel"
)
head(train_up_wholesale)Cek proporsi kelas target setelah melakukan upsampling.
#>
#> 1 2
#> 0.5 0.5
Melakukan pemodelan menggunakan logistic regression. Pemodelan
menggunakan fungsi glm() dalam memodelkan menggunakan
logistic regression dengan menggunakan semua variabel prediktor yang
ada.
model_logistic <- glm(formula = Channel ~ .,
data = train_up_wholesale,
family = "binomial")
summary(model_logistic)#>
#> Call:
#> glm(formula = Channel ~ ., family = "binomial", data = train_up_wholesale)
#>
#> Coefficients:
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) -3.97918426 0.66868359 -5.951 0.00000000266879 ***
#> Region2 1.66198008 0.75002926 2.216 0.0267 *
#> Region3 0.61084806 0.52854272 1.156 0.2478
#> Fresh 0.00001234 0.00001821 0.678 0.4979
#> Milk 0.00013537 0.00005855 2.312 0.0208 *
#> Grocery 0.00009577 0.00005756 1.664 0.0961 .
#> Frozen -0.00023761 0.00010138 -2.344 0.0191 *
#> Detergents_Paper 0.00097888 0.00013930 7.027 0.00000000000211 ***
#> Delicassen -0.00022091 0.00013069 -1.690 0.0910 .
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for binomial family taken to be 1)
#>
#> Null deviance: 668.19 on 481 degrees of freedom
#> Residual deviance: 206.70 on 473 degrees of freedom
#> AIC: 224.7
#>
#> Number of Fisher Scoring iterations: 8
Interpretasi model:
Region2, Milk,
Frozen, Detergents_Paper.Sekarang mari kita mengeksplorasi algoritma klasifikasi K-Nearest
Neighbor. Pada algoritma K-Nearest Neighbor, kita perlu
melakukan satu tahap data pre-process tambahan. Untuk setiap data
train dan test yang kita miliki, hilangkan
variabel kategorik kecuali variabel Channel. Pisahkan
variabel prediktor dan target dari data train dan
test.
# variabel prediktor pada data train
train_x <- train_up_wholesale %>%
select(-c("Region", "Channel"))
# variabel prediktor pada data test
test_x <- test_wholesale %>%
select(-c("Region", "Channel"))
# variabel target pada data train
train_y <- train_up_wholesale$Channel
# variabel target pada data test
test_y <- test_wholesale$ChannelKita cek data train_x.
#> Rows: 482
#> Columns: 6
#> $ Fresh <int> 19219, 3157, 11635, 6633, 7780, 8656, 1206, 7363, 306…
#> $ Milk <int> 1840, 4888, 922, 2096, 2495, 2746, 3620, 475, 7209, 3…
#> $ Grocery <int> 1658, 2500, 1614, 4563, 9464, 2501, 2857, 585, 4897, …
#> $ Frozen <int> 8195, 4477, 2583, 1389, 669, 6845, 1945, 1112, 18711,…
#> $ Detergents_Paper <int> 349, 273, 192, 1860, 2518, 694, 353, 72, 763, 235, 35…
#> $ Delicassen <int> 483, 2165, 1068, 1892, 501, 980, 967, 216, 2876, 4365…
Ingatlah bahwa pengukuran jarak pada KNN sangat bergantung pada skala data dari variabel prediktor yang dimasukkan sebagai input model. Adanya prediktor yang memiliki range nilai yang amat berbeda dari prediktor lainnya dapat menyebabkan masalah pada model klasifikasi. Oleh karena itu, mari lakukan normalisasi data untuk menyamakan skala dari tiap variabel prediktor agar memiliki range nilai yang standar.
Untuk menormalisasi data train_x, maka kita menggunakan
fungsi scale(). Sementara itu, untuk menormalisasi data
test, menggunakan fungsi yang sama namun menggunakan atribut
center dan scale yang didapat dari data
train_x.
# scale train_x data
train_x_scaled <- scale(train_x)
# scale test_x data
test_x_scaled <- scale(test_x,
center = attr(train_x_scaled, "scaled:center"),
scale = attr(train_x_scaled, "scaled:center"))Setelah kita selesai menormalisasi data, kita perlu menemukan nilai
k yang optimum untuk digunakan pada model KNN.
Pada praktiknya, memilih nilai k bergantung pada
kompleksitas data yang sedang dianalisis dan banyaknya observasi/baris
yang terdapat pada data train.
#> [1] 22
Karena hasil akarnya adalah 22 yang merupakan angka genap, maka untuk nilai
knya kita tentukan antara 21 atau 23. Disini saya memilih menggunakan 23.
Dengan menggunakan nilai k yang telah kita dapatkan,
sekarang kita coba untuk memprediksi test_y dengan
menggunakan data train_x dan train_y. Untuk
membuat model KNN, saya menggunakan fungsi knn()
dan menyimpan hasil prediksi pada object model_knn.
model_knn <- knn(train = train_x_scaled,
test = test_x_scaled,
cl = train_y,
k = 23)
head(model_knn)#> [1] 2 1 1 2 2 2
#> Levels: 1 2
Sekarang, kita kembali pada model_logistic. Pada bagian
ini, kita coba untuk memprediksi data test menggunakan
model_logistic untuk menghasilkan nilai probabilitas.
Menggunakan fungsi predict() dengan mengatur parameter
type = "response" kemudian menyimpan hasilnya ke dalam
object prob_value.
#> 1 2 3 4 5 6
#> 0.4441820 0.1188120 0.4097629 0.6729572 0.9923522 0.9585477
Karena hasil prediksi pada model logistic regression berupa
probabilitas, kita harus mengubah nilai tersebut menjadi kategori /
kelas target kita. Dengan menggunakan threshold 0.51, kita coba
untuk mengklasifikasikan customer tesebut termasuk dalam channel yang
mana apakah horeca atau retail. Kita akan menggunakan fungsi
ifelse() dan menyimpan hasil prediksi pada object
pred_value.
#> pred_value
#> 1 2
#> 59 29
Pada bagian sebelumnya, kita telah melakukan prediksi menggunakan
model Logistic Regression maupun algoritma
KNN. Namun, kita juga perlu mengevaluasi kebaikan model dalam
memprediksi data baru (unseen data). Pada tahap ini, membuat
confusion matrix dari model logistic regression menggunakan
label aktual dari data test dan hasil prediksi
(pred_value). Karena kita ingin lebih melihat / mengamati
channel retail dibandingkan horeca, oleh karena itu,
kita memilih kelas retail menjadi kelas positif.
Sehingga kita atur kelas positif yaitu “2”.
Evaluasi model logistic regression, kita membuat confusion
matrix dengan label aktual Channel pada data
test_wholesale. Berikut hasil evaluasinya:
#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction 1 2
#> 1 54 5
#> 2 3 26
#>
#> Accuracy : 0.9091
#> 95% CI : (0.8287, 0.9599)
#> No Information Rate : 0.6477
#> P-Value [Acc > NIR] : 0.00000001509
#>
#> Kappa : 0.7978
#>
#> Mcnemar's Test P-Value : 0.7237
#>
#> Sensitivity : 0.8387
#> Specificity : 0.9474
#> Pos Pred Value : 0.8966
#> Neg Pred Value : 0.9153
#> Prevalence : 0.3523
#> Detection Rate : 0.2955
#> Detection Prevalence : 0.3295
#> Balanced Accuracy : 0.8930
#>
#> 'Positive' Class : 2
#>
Berdasarkan hasil confusion matrix di atas, dapat kita ketahui bahwa Model Logistic Regression dalam melakukan prediksi untuk segmentasi ini adalah:
Evaluasi model KNN kita membuat confusion matrix dengan
label aktual test_y. Berikut hasil evaluasinya:
#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction 1 2
#> 1 54 7
#> 2 3 24
#>
#> Accuracy : 0.8864
#> 95% CI : (0.8009, 0.9441)
#> No Information Rate : 0.6477
#> P-Value [Acc > NIR] : 0.0000003334
#>
#> Kappa : 0.7434
#>
#> Mcnemar's Test P-Value : 0.3428
#>
#> Sensitivity : 0.7742
#> Specificity : 0.9474
#> Pos Pred Value : 0.8889
#> Neg Pred Value : 0.8852
#> Prevalence : 0.3523
#> Detection Rate : 0.2727
#> Detection Prevalence : 0.3068
#> Balanced Accuracy : 0.8608
#>
#> 'Positive' Class : 2
#>
Berdasarkan hasil confusion matrix di atas, dapat kita ketahui bahwa Model KNN dalam melakukan prediksi untuk segmentasi ini adalah:
Jika saya adalah seorang agen distributor wholesale, dimana saya akan mengirimkan jenis-jenis produk ke channel / segmen yang sesuai dengan target customer / pelanggan, maka saya ingin memastikan bahwa setiap produk saya akan diterima oleh channel / segmen customer yang tepat. Hal ini penting, agar produk yang saya jual sebisa mungkin diterima oleh customer yang tepat sehingga tidak ada produk yang tidak terbeli karena salah market. Oleh karena itu saya lebih ingin memastikan agar model yang saya buat tidak salah mengklasifikasikan pelanggan ke dalam segmen yang salah, atau dengan kata lain saya meminimalisir terjadinya False Positive.
Oleh karena itu saya akan fokus pada nilai precision untuk memastikan bahwa model saya mengidentifikasi channel pelanggan yang benar dan menghindari kesalahan klasifikasi.
Dengan demikian, berdasarkan hasil evaluasi model, Model Logistic Regression lebih baik untuk digunakan karena menghasilkan nilai precision yang lebih baik yaitu sebesar 89.66%.