1 Introduction

Halo!, ini merupakan Learn By Building yang dilakukan di Algoritma Data Science. Kali ini saya akan mencoba melakukan Customer Segmentation dengan pendekatan K-mean Clustering

Customer Segmentation adalah pembagian pasar menjadi kelompok-kelompok customer yang berbeda yang memiliki karakteristik serupa. Customer Segmentation bisa menjadi alat yang sangat berguna untuk mengidentifikasi kebutuhan pelanggan yang tidak terpenuhi. Dengan menggunakan data di atas, perusahaan dapat melampaui kompetisi dengan mengembangkan produk dan layanan yang unik dan menarik.

Data ini saya peroleh melalui kaggle : https://www.kaggle.com/datasets/dev0914sharma/customer-clustering/data yang berisikan data dasar tentang pelanggan Anda seperti ID Pelanggan, usia, jenis kelamin, pendapatan tahunan, dan skor pengeluaran. Tujuan kita ingin memahami pelanggan seperti siapa saja pelanggan yang menjadi target sehingga informasi ini dapat diberikan kepada tim pemasaran dan merencanakan strategi sesuai.

2 Data Preparation

2.1 Import Data

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(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ readr     2.1.4
## ✔ ggplot2   3.4.3     ✔ stringr   1.5.0
## ✔ lubridate 1.9.2     ✔ tibble    3.2.1
## ✔ purrr     1.0.2     ✔ tidyr     1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(factoextra)
## Warning: package 'factoextra' was built under R version 4.3.2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(FactoMineR)
## Warning: package 'FactoMineR' was built under R version 4.3.2
library(plotly)
## 
## Attaching package: 'plotly'
## 
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## The following object is masked from 'package:graphics':
## 
##     layout
options(scipen = 999)

2.2 Import Data

customer <- read.csv("segmentation data.csv")

2.3 Inspect Data

head(customer, n = 10)
 unique(customer$Education)
## [1] 2 1 0 3

Okay, data ini terdiri dari 8 kolom yang terdiri dari : Deskripsi

  • ID berisikan nomor ID masing - masing pelanggan
  • Sex merujuk pada jenis kelamin pelanggan dalam bentuk biner. ( Jika Pria maka 1)
  • Marital.status merujuk pada status pernikahan (Jika 1 maka menikah)
  • Age merujuk pada usia pelanggan dari rentang nilai maksimum 76 dan minimum 18
  • Education merujuk pada tingkat pendidikan yang dimiliki oleh pelanggan [ 0 = tidak sekolah dan 3 = Post Graduates atau Pendidikan Doktor ]
  • Income pendapatan pelanggan[35832 hingga 309364. ( Tidak diketahui dalam mata uang apa dan apakah pendapatan pertahuna tau perbulan, mari kita asumsikan dengan pendapatan pertahun dalam bentuk USD)
  • Occupation: status pekerjaan ( 0 = Unemployed , 1 = Employed, 2 = Highly Employed)
  • Settlement.size : tempat tingga ( 0 = small city, 1 = med city, 2 = big city)

Dengan kita mengetahui isi dari perkolomnya, kita perlu mengubah tipe data untuk memastikan tipe data pada masing - masing kolom sudah sesuai atau tidak

glimpse(customer)
## Rows: 2,000
## Columns: 8
## $ ID              <int> 100000001, 100000002, 100000003, 100000004, 100000005,…
## $ Sex             <int> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, …
## $ Marital.status  <int> 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, …
## $ Age             <int> 67, 22, 49, 45, 53, 35, 53, 35, 61, 28, 25, 24, 22, 60…
## $ Education       <int> 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, …
## $ Income          <int> 124670, 150773, 89210, 171565, 149031, 144848, 156495,…
## $ Occupation      <int> 1, 1, 0, 1, 1, 0, 1, 2, 0, 2, 1, 1, 1, 0, 1, 0, 1, 1, …
## $ Settlement.size <int> 2, 2, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 2, 0, 1, 0, 0, 2, …

Untuk memudahkan kita dalam melakukan clustering, kita bisa mengganti nilai variabel

customer <- customer %>%
  mutate(Sex = recode(Sex, `0` = "Male", `1` = "Female")) %>% 
  mutate(Marital.status = recode(Marital.status, '0' = "Single", '1' = "Married" )) %>% 
  mutate(Education = recode(Education, '0' = "Others", '1' = "High School", '2' = "University", '3' = "Post Graduate" )) %>%
  mutate(Occupation = recode(Occupation, '0' = "Unemployed", '1' = "Employed", '2' = "Highly Employed" )) %>%
  mutate(Settlement.size = recode(Settlement.size, '0' = "Small City", '1' = "Med City" , '2' = "Big City")) 
glimpse(customer)
## Rows: 2,000
## Columns: 8
## $ ID              <int> 100000001, 100000002, 100000003, 100000004, 100000005,…
## $ Sex             <chr> "Male", "Female", "Male", "Male", "Male", "Male", "Mal…
## $ Marital.status  <chr> "Single", "Married", "Single", "Single", "Single", "Si…
## $ Age             <int> 67, 22, 49, 45, 53, 35, 53, 35, 61, 28, 25, 24, 22, 60…
## $ Education       <chr> "University", "High School", "High School", "High Scho…
## $ Income          <int> 124670, 150773, 89210, 171565, 149031, 144848, 156495,…
## $ Occupation      <chr> "Employed", "Employed", "Unemployed", "Employed", "Emp…
## $ Settlement.size <chr> "Big City", "Big City", "Small City", "Med City", "Med…

Terlihat kita sudah mengubah isi variabel kita pada beberapa bagian kolom agar memudahkan membaca data yang ada

customer <- customer %>%
  mutate_at(vars("Sex", "Marital.status", "Education", "Occupation", "Settlement.size"), as.factor)

kita berhasil mengelompokan sesuai dengan tipe data yang seharunya digunakan

3 Exploratory Data Analysis

Selanjutnya kita akan melihat EDA, untuk melihat persebaran pada data customer yang kita miliki

Coba kita memerika terlebih dahulu pada kolom numerik yaitu Age dan Income dari customer yang kita miliki

#Melihat `Age`

ggplot(customer, aes(x = Age)) +
  geom_histogram(binwidth = 5, fill = "skyblue", color = "black") +
  labs(title = "Distribusi Usia (Age)",
       x = "Usia (Age)", y = "Frekuensi")

Insight :

data customer kita memiliki pelanggan paling banyak berusia 20 - 30 tahun

ggplot(customer, aes(x = Income)) +
  geom_histogram(binwidth = 50000, fill = "lightgreen", color = "black") +
  labs(title = "Distribusi Pendapatan (Income)",
       x = "Pendapatan (Income)", y = "Frekuensi")

Lalu, pada kolom Income pendapatan pelanggan paling banyak berkisar 100k

Selanjutnya, mari kita lihat hubungan Age dan Income melalui plot

plot <- plot_ly(customer, x = ~Age, y = ~Income, mode = "markers", type = "scatter", marker = list(color = ~Income, colorscale = 'Viridis')) %>%
  layout(title = "Hubungan Antara Usia (Age) dan Pendapatan (Income)",
         xaxis = list(title = "Usia (Age)"),
         yaxis = list(title = "Pendapatan (Income)"))

plot 

Terlihat pada plot, data usia 20 - 30 tahun banyak berkumpul pada data pendapatan sekisar 100k - 150K

Selanjutnya, kita akan melihat lebih spesifik pada kolom data kategorik

kita ingin melihat bagaimana persebaran pada kolom Sex, Marital.status, Education, Occupation, Settlement.size.

plot_sex <- plot_ly(customer, x = ~Sex, type = "histogram", marker = list(color = "skyblue")) %>%
  layout(title = "Persebaran Jenis Kelamin (Sex)",
         xaxis = list(title = "Jenis Kelamin"),
         yaxis = list(title = "Frekuensi"))

plot_sex
plot_marital <- plot_ly(customer, x = ~Marital.status, type = "histogram", marker = list(color = "lightgreen")) %>%
  layout(title = "Persebaran Status Pernikahan (Marital Status)",
         xaxis = list(title = "Status Pernikahan"),
         yaxis = list(title = "Frekuensi"))

plot_marital
plot_education <- plot_ly(customer, x = ~Education, type = "histogram", marker = list(color = "salmon")) %>%
  layout(title = "Persebaran Pendidikan (Education)",
         xaxis = list(title = "Pendidikan"),
         yaxis = list(title = "Frekuensi"))

plot_education
plot_occupation <- plot_ly(customer, x = ~Occupation, type = "histogram", marker = list(color = "yellow")) %>%
  layout(title = "Persebaran Pekerjaan (Occupation)",
         xaxis = list(title = "Pekerjaan"),
         yaxis = list(title = "Frekuensi"))

plot_occupation
plot_settlement <- plot_ly(customer, x = ~Settlement.size, type = "histogram", marker = list(color = "orange")) %>%
  layout(title = "Persebaran Ukuran Tempat Tinggal (Settlement Size)",
         xaxis = list(title = "Ukuran Tempat Tinggal"),
         yaxis = list(title = "Frekuensi"))

plot_settlement

Agar memudahkan dalam membaca insight mari kita menggabungkan semua barplot pada setiap kolom

subplot(plot_sex, plot_marital, plot_education, plot_occupation, plot_settlement, nrows = 5) %>%
  layout(showlegend = FALSE)

Insight :

  • plot_sex : persebaran Female dan Male terlihat hampir seimbang walaupun pelanggan Female lebih banyak
  • plot_marital : perseparan Single dan Married seimbang
  • plot_education : paling banyak pelanggan kita memiliki pendidikan High School
  • plot_occupation : paling banyak pelanggan kita Employed
  • plot_settlement : paling banyak tinggal di Small City

Selanjutnya kita ingin mengetahui bagaimana perbandingan antara Sex, Income, serta Age

scatter_plot <- plot_ly(customer, x = ~Age, y = ~Income, color = ~factor(Sex),
                        type = "scatter", mode = "markers", marker = list(opacity = 0.7)) %>%
  layout(title = "Hubungan Antara Usia (Age) dan Pendapatan (Income) dengan Perbedaan Jenis Kelamin (Sex)",
         xaxis = list(title = "Usia (Age)"), yaxis = list(title = "Pendapatan (Income)"),
         colorway = c("blue", "red"), legend = list(title = "Jenis Kelamin"))

scatter_plot
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

terlihat pada plot, hubungan antara usia pelanggan dan gender mereka mempengaruhi pendapatan yang dimiliki hal ini menjadikan sebuah pertimbangan dalam menentukan cluster apa yang akan nantinya dibuat

4 Clustering

Sebelum kita melakukan clustering, kita perlu menentukan jumlah cluster yang optimal. Dalam metode pengelompokan, kami berupaya meminimalkan total jumlah kuadrat dalam kluster (artinya jarak minimum antara observasi dalam kluster yang sama).

Untuk mencari jumlah cluster yang optimal, kita dapat menggunakan 3 metode: metode elbow, metode siluet, dan statistik gap. kita akan menentukan jumlah klaster berdasarkan suara terbanyak.

Saat kita mencoba k-means clustering perlu variabel berisikan tipe data numerik, kita akan mencoba apakah bisa kita melakukan clustering dengan data ini atau tidak, kita perlu mengubah beberapa kolom menjadi tipe data numerik

customer_clean <- customer %>%
  mutate(Sex = as.numeric(Sex),
         Marital.status = as.numeric(Marital.status),
         Education = as.numeric(Education),
         Occupation = as.numeric(Occupation),
         Settlement.size = as.numeric(Settlement.size)) %>% 
  select(-"ID")

customer_clean

Menentukan K-means dengan Elbow Method :

fviz_nbclust(
  x = customer_clean,
  FUNcluster = kmeans,
  method = "wss"
)

kita tentukan k = 9

# Standarisasi variabel numerik
customer_clean[, c("Age", "Income")] <- scale(customer_clean[, c("Age", "Income")])

# Penerapan K-means
set.seed(123) # untuk hasil yang konsisten
customer_cluster <- kmeans(customer_clean, centers = 9) 
# WSS

customer_cluster$withinss
## [1] 157.7580 274.8204 164.4439 705.3052 212.9942 703.3782 212.7580 135.3401
## [9] 268.1025
#BSS/TSS

customer_cluster$betweenss/customer_cluster$totss
## [1] 0.723044
customer_cluster$size
## [1] 240 122 102 214 174 507 210 144 287

4.1 Interpretation: Cluster Profiling

customer_clean$cluster <- as.factor(customer_cluster$cluster)

customer_clean

Untuk bisa menginterpretasi karakteristik setiap cluster, kita bisa melihat dari centroid/titik pusat cluster.

data.frame(customer_cluster$centers)

Grouping data berdasarkan cluster label

Melakukan grouping berdasarkan cluster yang terbentuk, untuk mengetahui karakteristik dari masing-masing cluster.

# melakukan profiling cluster
customer_centroid <- customer_clean %>% 
  group_by(cluster) %>% # mengelompokkan data berdasarkan cluster
  summarise_all(mean) # menentukan rata-rata untuk setiap kolom


customer_centroid

4.1.1 Visualisasi Clustering

# visualisasi 2 dimensi
fviz_cluster(object = customer_cluster , 
             data = customer_clean
             %>% select(-cluster),
             labelsize = 7) #mengatur font gambar

Kita mencoba melakukan clustring dengan pendeketan k-means, tetapi pada hasil visualiasi yang diberikan tidak dapat membantu kita mengenali clustering mana pada data kita berada

5 Conclusion

Pada hasil proses data ini, perlu mengenali dan menentukan pendeketan clustering mana yang akan kita gunakan. Pada LBB yang saya lakukan, saya melakukan kesalahan dengan tidak menentukan pendeketan apa yang cocok pada data yang saya miliki

ternyata data Customer Segmentation tidak cocok menggunakan k-means clustering, yang dimana data tersebut lebih cocok untuk data yang memiliki skala atau pertipe numerik.

Solusi yang bisa dilakukan untuk melakukan clustering pada data Customer Segmentation adalah dengan Hierarchical Clustering.

Hierarchical Clustering adalah salah satu teknik dalam analisis cluster yang digunakan untuk mengelompokkan data ke dalam hierarki yang berstruktur secara bertingkat. Metode ini bekerja dengan cara membangun serangkaian cluster secara bertahap, di mana pada awalnya setiap data dianggap sebagai satu cluster individu. Kemudian, langkah demi langkah, algoritma menggabungkan dua cluster yang paling mirip menjadi satu cluster yang lebih besar, membentuk hierarki atau “dendrogram”.

Di lain kesempatan, saya akan mencoba membuat clustring dengan menggunakan Hierarchical Clustering dengan dataset Customer Segmentation seperti LBB ini.