Analisis Model Prediksi
Case 1 Partitional
Fika Irsandi Desvyanti
Program Studi Sains Data
NIM: 52240013
Dosen Pembimbing:
Bakti Siregar, M.Sc., CDS.
Definisi
Partitional clustering adalah pendekatan pengelompokan yang membagi data langsung ke dalam beberapa cluster yang jumlahnya sudah ditentukan di awal. Jadi, tujuan utamanya adalah membuat pembagian data yang paling “rapi” sehingga objek dalam satu cluster mirip satu sama lain, sedangkan objek antar cluster berbeda. Metode ini tidak membuat struktur hirarki, tapi langsung membentuk pembagian optimal berdasarkan jarak atau tingkat kemiripan.
Rumus Inti & Cara Kerja
(Langkah Operasi & Hyperparameter)
1. K-Means Clustering
Ide utama: K-Means membagi data menjadi k kelompok dengan cara meminimalkan jarak antara tiap data dengan centroid klusternya. Centroid adalah titik rata-rata dari data dalam satu cluster.
Cara kerja:
1. Tentukan jumlah cluster k.
2. Pilih centroid awal (acak atau metode tertentu).
3. Hitung jarak setiap data ke centroid → data masuk cluster terdekat.
4. Perbarui centroid menjadi rata-rata baru.
5. Ulangi sampai posisi centroid stabil.
Tujuan optimasi:
\[ [ J = \sum_{i=1}^{k} \sum_{x_j \in C_i} | x_j - \mu_i |^2 ] \] Artinya meminimalkan total jarak kuadrat antara titik dan centroid.
Hyperparameter penting:
centers (k): jumlah cluster yang ingin dibentuk. Ini yang paling krusial.
nstart: banyaknya inisialisasi acak untuk menemukan hasil terbaik, semakin besar semakin stabil.
iter.max: jumlah iterasi maksimum untuk proses update centroid.
Kelebihan: cepat, sederhana, cocok untuk dataset besar.
Kekurangan: sensitif terhadap outlier dan bentuk cluster yang tidak bulat.
2. K-Medoids (PAM – Partitioning Around Medoids)
Ide utama: Mirip K-Means, namun centroid-nya bukan nilai rata-rata, tapi medoid, yaitu titik data asli yang paling mewakili cluster. Karena menggunakan data nyata sebagai pusat, metode ini lebih tahan terhadap outlier.
Cara kerja
1. Pilih k medoid dari data.
2. Hitung jarak setiap data ke seluruh medoid.
3. Setiap data dimasukkan ke medoid terdekat.
4. Tukar-tukar medoid dengan kandidat lain untuk mencari kombinasi yang menghasilkan total jarak paling kecil.
5. Berhenti ketika tidak ada perbaikan.
Tujuan optimasi: Meminimalkan total dissimilarity/total jarak ke medoid:
\[ [ \text{min} \sum_{i=1}^{k} \sum_{x_j \in C_i} d(x_j, m_i) ] \]
Hyperparameter penting:
k: jumlah cluster.
distance metric: Euclidean, Manhattan, cosine, atau jenis dissimilarity lainnya.
max.iter (opsional): batas iterasi pencarian medoid terbaik.
Kelebihan: robust terhadap outlier dan noise.
Kekurangan: lebih lambat dari K-Means, terutama untuk dataset besar.
3. Fuzzy C-Means (FCM)
Ide utama: Berbeda dari K-Means/K-Medoids yang bersifat “hard clustering”, FCM menggunakan membership, yaitu setiap data bisa memiliki tingkat keanggotaan ke berbagai cluster (nilai 0–1). Cocok saat batas cluster tidak tegas.
Cara kerja:
1. Tentukan jumlah cluster c dan parameter fuzziness.
2. Inisialisasi matriks membership (\(u_{ij}\)).
3. Hitung pusat cluster (centroid fuzzy) berdasarkan membership.
4. Perbarui membership berdasarkan jarak ke semua centroids.
5. Ulangi sampai perubahan kecil.
Tujuan optimasi:
\[ [ J_m = \sum_{i=1}^{c} \sum_{j=1}^{n} u_{ij}^m |x_j - c_i|^2 ] \] Hyperparameter penting:
centers (c/k): jumlah cluster.
m (fuzziness): biasanya 2; makin besar → makin kabur keanggotaannya.
max.iter: iterasi maksimum.
epsilon: batas perubahan minimum untuk menghentikan algoritma.
Kelebihan: fleksibel, cocok untuk data yang tidak jelas batas cluster-nya. Kekurangan: lebih lambat, sensitif terhadap inisialisasi.
4. MiniBatch K-Means
Ide utama: Versi ringan dari K-Means yang menggunakan batch kecil secara acak pada setiap update. Algoritma ini mempertahankan kualitas hasil K-Means sambil meningkatkan kecepatan, terutama untuk dataset sangat besar.
Cara kerja
1. Ambil subset kecil (mini-batch).
2. Hitung jarak data batch ke centroid.
3. Update centroid berdasarkan data batch saja.
4. Ulangi untuk batch selanjutnya.
Tujuan: sama seperti K-Means, meminimalkan total jarak ke centroid, tetapi dengan pendekatan bertahap.
Hyperparameter penting:
k: jumlah cluster.
batch_size: ukuran mini-batch (semakin kecil, semakin cepat tetapi kadang kurang stabil).
max_iters: batas iterasi.
num_init: jumlah inisialisasi untuk mencari centroid awal terbaik.
Kelebihan: sangat cepat, cocok untuk data ratusan ribu atau jutaan.
Kekurangan: hasil bisa sedikit kurang stabil dibanding K-Means full-batch.
Sumber & Loading Data
Pada studi kasus ini, penulis menggunakan dataset Mall Customers yang diperoleh dari platform Kaggle. Dataset ini tersedia secara publik melalui tautan berikut:
https://www.kaggle.com/datasets/shwetabh123/mall-customers
Data tersebut berisi informasi dasar mengenai pelanggan sebuah pusat perbelanjaan, seperti jenis kelamin, usia, pendapatan tahunan, serta skor belanja yang merepresentasikan perilaku konsumsi mereka. Data ini dipilih karena memiliki kombinasi fitur numerik yang ideal untuk dianalisis menggunakan metode partitional clustering, khususnya algoritma K-Means. Dengan karakteristik data yang bersih, ukuran yang tidak terlalu besar, serta adanya variabel yang berpotensi membentuk pola kelompok yang jelas, dataset ini memungkinkan proses pengelompokan pelanggan dilakukan secara optimal. Melalui pendekatan partitional ini, analisis dapat menghasilkan segmentasi pelanggan yang lebih terstruktur dan mudah diinterpretasikan, sehingga dapat mendukung pemahaman perilaku konsumen serta pengambilan keputusan yang lebih tepat dalam konteks pemasaran atau manajemen pelanggan.
Eksplorasi data singkat
Dataset ini terdiri dari 200 baris dan 5 variabel, yaitu: ID pelanggan, jenis kelamin, usia, pendapatan tahunan, dan skor pengeluaran. Sebelum masuk ke proses clustering, saya melakukan eksplorasi awal seperti pengecekan missing value, tipe variabel, dan distribusi masing-masing fitur. Beberapa variabel berskala numerik sehingga perlu dilakukan scaling untuk memastikan jarak antar data tidak bias.
# ======================================================
# EKSPLORASI DATA SINGKAT
# (summary, missing value, tipe variabel)
# ======================================================
# 1. Load dataset
df <- read.csv("Mall_Customers.csv")
# 2. Ringkasan statistik
summary(df)## CustomerID Genre Age Annual.Income..k..
## Min. : 1.00 Length:200 Min. :18.00 Min. : 15.00
## 1st Qu.: 50.75 Class :character 1st Qu.:28.75 1st Qu.: 41.50
## Median :100.50 Mode :character Median :36.00 Median : 61.50
## Mean :100.50 Mean :38.85 Mean : 60.56
## 3rd Qu.:150.25 3rd Qu.:49.00 3rd Qu.: 78.00
## Max. :200.00 Max. :70.00 Max. :137.00
## Spending.Score..1.100.
## Min. : 1.00
## 1st Qu.:34.75
## Median :50.00
## Mean :50.20
## 3rd Qu.:73.00
## Max. :99.00
## CustomerID Genre Age
## 0 0 0
## Annual.Income..k.. Spending.Score..1.100.
## 0 0
## 'data.frame': 200 obs. of 5 variables:
## $ CustomerID : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Genre : chr "Male" "Male" "Female" "Female" ...
## $ Age : int 19 21 20 23 31 22 35 23 64 30 ...
## $ Annual.Income..k.. : int 15 15 16 16 17 17 18 18 19 19 ...
## $ Spending.Score..1.100.: int 39 81 6 77 40 76 6 94 3 72 ...
Tahap Pra-Proses (Cleaning, Imputasi, Encoding, Scaling)
# ======================================
# 1. Load Library
# ======================================
library(dplyr)
library(caret)
library(fastDummies)
# ======================================
# 2. Load Dataset
# ======================================
df <- read.csv("Mall_Customers.csv")
# Cek struktur
str(df)## 'data.frame': 200 obs. of 5 variables:
## $ CustomerID : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Genre : chr "Male" "Male" "Female" "Female" ...
## $ Age : int 19 21 20 23 31 22 35 23 64 30 ...
## $ Annual.Income..k.. : int 15 15 16 16 17 17 18 18 19 19 ...
## $ Spending.Score..1.100.: int 39 81 6 77 40 76 6 94 3 72 ...
## CustomerID Genre Age Annual.Income..k..
## Min. : 1.00 Length:200 Min. :18.00 Min. : 15.00
## 1st Qu.: 50.75 Class :character 1st Qu.:28.75 1st Qu.: 41.50
## Median :100.50 Mode :character Median :36.00 Median : 61.50
## Mean :100.50 Mean :38.85 Mean : 60.56
## 3rd Qu.:150.25 3rd Qu.:49.00 3rd Qu.: 78.00
## Max. :200.00 Max. :70.00 Max. :137.00
## Spending.Score..1.100.
## Min. : 1.00
## 1st Qu.:34.75
## Median :50.00
## Mean :50.20
## 3rd Qu.:73.00
## Max. :99.00
# ======================================
# 3. Cleaning
# ======================================
# Buang duplikasi bila ada
df <- df %>% distinct()
# Cek missing value
colSums(is.na(df))## CustomerID Genre Age
## 0 0 0
## Annual.Income..k.. Spending.Score..1.100.
## 0 0
# ======================================
# 4. Imputasi Missing Value
# ======================================
# Numerik (Age, Annual.Income..k.., Spending.Score..1.100.)
num_cols <- c("Age", "Annual.Income..k..", "Spending.Score..1.100.")
for (col in num_cols) {
df[[col]][is.na(df[[col]])] <- median(df[[col]], na.rm = TRUE)
}
# Kategorikal (Genre)
mode_value <- function(v){
uniq <- unique(v)
uniq[which.max(tabulate(match(v, uniq)))]
}
df$Genre[is.na(df$Genre)] <- mode_value(df$Genre)
df$Genre <- as.factor(df$Genre)
# ======================================
# 5. Encoding Variabel Kategorikal
# ======================================
df_encoded <- fastDummies::dummy_cols(
df,
select_columns = "Genre",
remove_first_dummy = TRUE, # menghasilkan Genre_Male saja
remove_selected_columns = TRUE
)
# ======================================
# 6. Scaling
# ======================================
# Tentukan kolom numerik (kecuali ID)
num_features <- c("Age", "Annual.Income..k..", "Spending.Score..1.100.")
preprocess_scale <- preProcess(df_encoded[, num_features],
method = c("center", "scale"))
df_scaled <- df_encoded
df_scaled[, num_features] <- predict(preprocess_scale, df_encoded[, num_features])
# ======================================
# 7. Output akhir
# ======================================
str(df_scaled)## 'data.frame': 200 obs. of 5 variables:
## $ CustomerID : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Age : num -1.421 -1.278 -1.349 -1.135 -0.562 ...
## $ Annual.Income..k.. : num -1.73 -1.73 -1.7 -1.7 -1.66 ...
## $ Spending.Score..1.100.: num -0.434 1.193 -1.712 1.038 -0.395 ...
## $ Genre_Male : int 1 1 0 0 0 0 0 0 1 0 ...
## CustomerID Age Annual.Income..k.. Spending.Score..1.100.
## Min. : 1.00 Min. :-1.4926 Min. :-1.73465 Min. :-1.905240
## 1st Qu.: 50.75 1st Qu.:-0.7230 1st Qu.:-0.72569 1st Qu.:-0.598292
## Median :100.50 Median :-0.2040 Median : 0.03579 Median :-0.007745
## Mean :100.50 Mean : 0.0000 Mean : 0.00000 Mean : 0.000000
## 3rd Qu.:150.25 3rd Qu.: 0.7266 3rd Qu.: 0.66401 3rd Qu.: 0.882916
## Max. :200.00 Max. : 2.2299 Max. : 2.91037 Max. : 1.889750
## Genre_Male
## Min. :0.00
## 1st Qu.:0.00
## Median :0.00
## Mean :0.44
## 3rd Qu.:1.00
## Max. :1.00
Teknik reduksi
Reduksi dimensi seperti PCA, t-SNE, maupun UMAP sebenarnya tidak diperlukan pada dataset ini karena jumlah fitur yang digunakan untuk clustering relatif sedikit (tiga fitur numerik utama), dan semuanya sudah distandardisasi pada tahap pra-proses. Namun, PCA tetap diterapkan sebagai langkah opsional untuk tujuan visualisasi. Dengan memproyeksikan data ke dalam dua komponen utama, pola pemisahan antar-cluster menjadi lebih mudah diamati secara visual.
Dalam penelitian ini, PCA tidak digunakan sebagai input untuk algoritma clustering, tetapi hanya sebagai alat bantu visualisasi hasil clustering agar interpretasi lebih intuitif. Dengan demikian, penggunaan PCA tidak memengaruhi proses pembentukan cluster, melainkan memberikan gambaran yang lebih jelas mengenai struktur data dalam ruang 2 dimensi.
pemilihan parameter, fitting, evaluasi
Pada tahap ini, empat algoritma partitional clustering diuji: K-Means, K-Medoids (PAM), Fuzzy C-Means (FCM), dan MiniBatch K-Means. Evaluasi dilakukan menggunakan metrik internal, yaitu:
Silhouette Score
Davies-Bouldin (DB) Index
Calinski-Harabasz (CH) Index
Runtime (kecepatan komputasi)
Tujuan dari evaluasi ini adalah mencari model dengan pemisahan cluster paling baik, stabil, dan efisien.
# ===============================
# Library yang dibutuhkan
# ===============================
library(cluster) # silhouette
library(factoextra) # CH index
library(ClusterR) # MiniBatchKMeans
library(ppclust) # fuzzy c-means
library(clValid) # DB index
library(microbenchmark) # runtime
# ===============================
# Siapkan data
# ===============================
# Gunakan scaled original data
num_features <- c("Age", "Annual.Income..k..", "Spending.Score..1.100.")
data_mat <- as.matrix(df_scaled[, num_features])
data_mat[is.na(data_mat)] <- 0
data_mat[is.infinite(data_mat)] <- 0
# ===============================
# Fungsi bantu hitung metrik internal
# ===============================
compute_metrics <- function(labels, data_mat) {
labels <- as.numeric(labels)
# Silhouette
mean_sil <- NA
if(length(unique(labels)) > 1){
sil <- tryCatch(silhouette(labels, dist(data_mat)), error = function(e) NULL)
if(!is.null(sil)){
mean_sil <- mean(sil[, "sil_width"])
}
}
# DB index
db <- tryCatch(index.DB(data_mat, labels, d = NULL)$DB, error = function(e) NA)
# CH index
ch <- tryCatch(calinhara(data_mat, labels), error = function(e) NA)
return(list(silhouette = mean_sil, DB = db, CH = ch))
}
# ===============================
# Pilih jumlah cluster (k_opt) sesuai hasil pemilihan parameter
# ===============================
k_opt <- 3 # ganti sesuai hasil Elbow/Silhouette
# ===============================
# 1. K-Means
# ===============================
set.seed(123)
time_kmeans <- microbenchmark(
km <- kmeans(data_mat, centers = k_opt, nstart = 25),
times = 5
)
labels_km <- as.numeric(km$cluster)
metrics_km <- compute_metrics(labels_km, data_mat)
# ===============================
# 2. K-Medoids (PAM)
# ===============================
set.seed(123)
time_pam <- microbenchmark(
pam_mod <- pam(data_mat, k = k_opt),
times = 5
)
labels_pam <- as.numeric(pam_mod$clustering)
metrics_pam <- compute_metrics(labels_pam, data_mat)
# ===============================
# 3. Fuzzy C-Means
# ===============================
set.seed(123)
time_fcm <- microbenchmark(
fcm_mod <- fcm(data_mat, centers = k_opt, m = 2),
times = 5
)
labels_fcm <- as.numeric(apply(fcm_mod$u, 1, which.max))
metrics_fcm <- compute_metrics(labels_fcm, data_mat)
# ===============================
# 4. MiniBatch KMeans
# ===============================
set.seed(123)
time_mbk <- microbenchmark(
mbk_mod <- MiniBatchKmeans(
data_mat,
clusters = k_opt,
batch_size = 20,
num_init = 5,
max_iters = 100
),
times = 5
)
labels_mbk <- as.numeric(mbk_mod$clusters)
if(min(labels_mbk) == 0) labels_mbk <- labels_mbk + 1
metrics_mbk <- compute_metrics(labels_mbk, data_mat)
# ===============================
# Ringkas hasil perbandingan
# ===============================
comparison_metrics <- data.frame(
Algorithm = c("K-Means", "K-Medoids", "Fuzzy C-Means", "MiniBatch KMeans"),
Silhouette = c(metrics_km$silhouette,
metrics_pam$silhouette,
metrics_fcm$silhouette,
metrics_mbk$silhouette),
DB_Index = c(metrics_km$DB,
metrics_pam$DB,
metrics_fcm$DB,
metrics_mbk$DB),
CH_Index = c(metrics_km$CH,
metrics_pam$CH,
metrics_fcm$CH,
metrics_mbk$CH),
Runtime_sec = c(mean(time_kmeans$time)/1e9,
mean(time_pam$time)/1e9,
mean(time_fcm$time)/1e9,
mean(time_mbk$time)/1e9)
)
# Tampilkan hasil
print(comparison_metrics)## Algorithm Silhouette DB_Index CH_Index Runtime_sec
## 1 K-Means 0.3577934 NA NA 0.00651402
## 2 K-Medoids 0.3588098 NA NA 0.00804654
## 3 Fuzzy C-Means 0.3488460 NA NA 3.92121820
## 4 MiniBatch KMeans NA NA NA 0.00479738
Dari empat algoritma partitional yang diuji K-Means, K-Medoids, Fuzzy C-Means, dan MiniBatch KMeans nilai silhouette rata-rata berada di kisaran 0.348–0.359, yang menunjukkan pemisahan cluster cukup moderat namun tidak terlalu kuat. Secara spesifik, K-Medoids memiliki nilai silhouette tertinggi (0.3588), sedikit lebih baik dibanding K-Means (0.3578) dan Fuzzy C-Means (0.3488), yang menandakan kemampuannya membentuk cluster dengan batas yang sedikit lebih jelas. MiniBatch KMeans gagal menghasilkan cluster valid, sehingga nilai silhouette, DB Index, dan CH Index-nya tidak tersedia (NA), kemungkinan karena sensitivitas parameter atau ukuran batch yang digunakan terlalu kecil untuk dataset ini. Secara keseluruhan, perbedaan nilai silhouette antar algoritma relatif kecil, sehingga pemilihan algoritma sebaiknya mempertimbangkan faktor lain selain akurasi, seperti robust terhadap outlier (K-Medoids), efisiensi komputasi (MiniBatch KMeans), atau kemampuan memberikan keanggotaan probabilistik (Fuzzy C-Means).
Tabel Parameter yang Diuji
| Algoritma | Parameter | Nilai |
|---|---|---|
| K-Means | centers (k) | 3 |
| nstart | 25 | |
| iter.max | 100 | |
| K-Medoids (PAM) | k | 3 |
| distance metric | Euclidean | |
| max.iter | 100 | |
| Fuzzy C-Means | centers (c) | 3 |
| m (fuzziness) | 2 | |
| max.iter | 100 | |
| MiniBatch KMeans | clusters (k) | 3 |
| batch_size | 20 | |
| num_init | 5 | |
| max_iters | 100 | |
| init_fraction | 0.5 |
Interpretasi Hasil Clustering (sesuai output terbaru):
Dari empat algoritma partitional yang diuji—K-Means, K-Medoids, Fuzzy C-Means, dan MiniBatch KMeans—nilai silhouette rata-rata berada di kisaran 0.348–0.359, menunjukkan pemisahan cluster cukup moderat.
K-Medoids memiliki nilai silhouette tertinggi (0.3588), sedikit lebih baik dibanding K-Means (0.3578) dan Fuzzy C-Means (0.3488), menandakan kemampuannya membentuk cluster dengan batas yang sedikit lebih jelas.
MiniBatch KMeans gagal menghasilkan cluster valid (NA), kemungkinan karena sensitivitas parameter atau ukuran batch yang terlalu kecil untuk dataset ini.
Secara keseluruhan, perbedaan nilai silhouette antar algoritma relatif kecil, sehingga pemilihan algoritma sebaiknya mempertimbangkan faktor lain selain akurasi, seperti robust terhadap outlier (K-Medoids), efisiensi komputasi (MiniBatch KMeans), atau kemampuan memberikan keanggotaan probabilistik (Fuzzy C-Means).
Visualisasi
2D PCA Plot + Cluster
## Warning: package 'plotly' was built under R version 4.4.2
##
## 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
# Data untuk plot (gunakan label cluster yang sudah ada)
plot2d_data <- data.frame(
Income = df_scaled$Annual.Income..k..,
Spending = df_scaled$Spending.Score..1.100.,
KMedoids = factor(labels_pam),
FCM = factor(labels_fcm)
)
# Plot interaktif awal pakai K-Medoids
fig <- plot_ly(
data = plot2d_data,
x = ~Income,
y = ~Spending,
type = "scatter",
mode = "markers",
color = ~KMedoids,
colors = "Set1",
marker = list(size = 8),
text = ~paste("Cluster:", KMedoids),
hoverinfo = "text"
)
# Tambahkan dropdown untuk ganti algoritma
fig <- fig %>% layout(
title = "2D Cluster Plot: Annual Income vs Spending Score",
xaxis = list(title = "Annual Income"),
yaxis = list(title = "Spending Score"),
updatemenus = list(
list(
y = 0.8,
buttons = list(
list(method = "restyle",
args = list("marker.color", list(plot2d_data$KMedoids)),
label = "K-Medoids"),
list(method = "restyle",
args = list("marker.color", list(plot2d_data$FCM)),
label = "Fuzzy C-Means")
)
)
)
)
figInterpretasi visualisasi
Visualisasi ini memperlihatkan pembagian pelanggan menjadi tiga kelompok berbeda. Kelompok pertama (merah) tersebar di area pendapatan rendah hingga menengah dengan pola belanja yang beragam. Kelompok kedua (biru) berada di tengah grafik, menunjukkan pelanggan dengan pendapatan dan pola belanja yang moderat. Sementara kelompok ketiga (hijau) terkumpul di sisi kanan atas, mengindikasikan pelanggan berpendapatan tinggi dengan kecenderungan belanja yang tinggi pula.
3D Cluster Plot
library(plotly)
# ================================
# Buat data frame untuk plot 3D
# ================================
plot3d_data <- data.frame(
Age = df_scaled$Age,
Income = df_scaled$Annual.Income..k..,
Spending = df_scaled$Spending.Score..1.100.,
KMedoids = factor(labels_pam),
FCM = factor(labels_fcm)
)
# ================================
# Plot interaktif 3D awal pakai K-Medoids
# ================================
fig <- plot_ly(
data = plot3d_data,
x = ~Age,
y = ~Income,
z = ~Spending,
type = "scatter3d",
mode = "markers",
color = ~KMedoids, # default K-Medoids
colors = "Set1",
marker = list(size = 4),
text = ~paste("Cluster:", KMedoids),
hoverinfo = "text"
)
# Tambahkan dropdown untuk ganti algoritma
fig <- fig %>% layout(
title = "3D Cluster Plot (Age vs Income vs Spending)",
scene = list(
xaxis = list(title = "Age"),
yaxis = list(title = "Annual Income"),
zaxis = list(title = "Spending Score")
),
updatemenus = list(
list(
y = 0.8,
buttons = list(
list(method = "restyle",
args = list("marker.color", list(plot3d_data$KMedoids),
"text", list(paste("Cluster:", plot3d_data$KMedoids))),
label = "K-Medoids"),
list(method = "restyle",
args = list("marker.color", list(plot3d_data$FCM),
"text", list(paste("Cluster:", plot3d_data$FCM))),
label = "Fuzzy C-Means")
)
)
)
)
figInterpretasi visualisasi
Tampilan tiga dimensi ini memberikan pemahaman lebih lengkap tentang segmentasi pelanggan. Terlihat bahwa ketiga klaster memiliki karakteristik yang cukup jelas: klaster biru dominan di area pendapatan rendah-sedang dengan belanja minimal, klaster merah berada di tengah dengan kombinasi variabel yang seimbang, dan klaster hijau menempati ruang dengan pendapatan serta pengeluaran yang lebih tinggi.
Dendrogram (Hierarchical Clustering)
library(dplyr)
library(plotly)
# 1. Pastikan X numeric
X <- df %>% select(where(is.numeric))
# 2. Hitung jarak dan lakukan hierarchical clustering
dist_matrix <- dist(X)
hc <- hclust(dist_matrix, method = "ward.D2")
# 3. Konversi ke dendrogram
dend <- as.dendrogram(hc)
# Install jika belum ada
# install.packages("ggdendro")
library(ggdendro)## Warning: package 'ggdendro' was built under R version 4.4.3
# Konversi dendrogram ke format data frame
ddata <- dendro_data(dend, type = "rectangle")
segments <- ddata$segments
labels <- ddata$labels
# 5. Plot interaktif pakai plotly
p <- plot_ly(type = 'scatter', mode = 'lines')
# Tambahkan garis
for(i in 1:nrow(segments)){
p <- add_trace(
p,
x = c(segments$x[i], segments$xend[i]),
y = c(segments$y[i], segments$yend[i]),
mode = 'lines',
line = list(color = 'blue'),
showlegend = FALSE
)
}
# Tambahkan label
p <- add_trace(
p,
x = labels$x,
y = labels$y,
mode = 'text',
text = labels$label,
textposition = 'top center',
showlegend = FALSE
)
# Layout
p <- layout(
p,
title = "Dendrogram Interaktif Hierarchical Clustering (Ward.D2)",
xaxis = list(title = "Observasi"),
yaxis = list(title = "Jarak")
)
pInterpretasi Visualisasi
Diagram pohon ini mengilustrasikan proses pengelompokan hierarkis dengan metode Ward D2. Struktur dendrogram menunjukkan adanya beberapa klaster utama yang terbentuk pada jarak yang berbeda. Pembagian terbesar terlihat pada jarak sekitar 1000, yang memisahkan dua kelompok besar. Selanjutnya, terjadi pemisahan lebih lanjut pada jarak 400-500, menghasilkan sub-kelompok yang lebih spesifik.
Elbow Method (Menentukan Jumlah Cluster K-Means)
## Warning: package 'purrr' was built under R version 4.4.2
##
## Attaching package: 'purrr'
## The following object is masked from 'package:caret':
##
## lift
library(dplyr)
# Ambil kolom numerik
X <- df_scaled %>% select(where(is.numeric))
# Hitung WSS untuk jumlah cluster 1 sampai 10
wss <- map_dbl(1:10, function(k){
kmeans(X, centers = k, nstart = 25)$tot.withinss
})
# Data frame untuk plot
wss_df <- data.frame(K = 1:10, WSS = wss)
# Plot interaktif
plot_ly(
wss_df,
x = ~K,
y = ~WSS,
type = 'scatter',
mode = 'lines+markers',
text = ~paste("K:", K, "<br>WSS:", round(WSS, 2)),
hoverinfo = "text",
line = list(color = 'blue'),
marker = list(size = 8, color = 'red')
) %>%
layout(
title = "Interactive Elbow Method",
xaxis = list(title = "Jumlah Cluster (K)"),
yaxis = list(title = "Total Within-Cluster Sum of Squares (WSS)")
)Interpretasi Visualisasi
Kurva ini membantu menentukan jumlah klaster optimal. Penurunan drastis terlihat dari K=1 ke K=2, kemudian terus menurun hingga K=3. Setelah titik K=3, kurva mulai mendatar dan penurunannya tidak terlalu signifikan. Pola ini mengindikasikan bahwa jumlah klaster yang paling sesuai adalah sekitar 3-4 klaster, karena penambahan klaster setelah itu tidak memberikan peningkatan kualitas pengelompokan yang berarti.
Silhouette Method interaktif
library(plotly)
library(cluster)
library(dplyr)
library(purrr)
# Ambil kolom numerik
X <- df_scaled %>% select(where(is.numeric))
# Hitung silhouette rata-rata untuk K = 2 sampai 10
sil_avg <- map_dbl(2:10, function(k){
km <- kmeans(X, centers = k, nstart = 25)
sil <- silhouette(km$cluster, dist(X))
mean(sil[, 3])
})
# Data frame untuk plot
sil_df <- data.frame(
K = 2:10,
Silhouette = sil_avg
)
# Plot interaktif
plot_ly(
sil_df,
x = ~K,
y = ~Silhouette,
type = 'scatter',
mode = 'lines+markers',
text = ~paste("K:", K, "<br>Avg Silhouette:", round(Silhouette, 3)),
hoverinfo = "text",
line = list(color = 'green'),
marker = list(size = 8, color = 'orange')
) %>%
layout(
title = "Interactive Silhouette Method",
xaxis = list(title = "Jumlah Cluster (K)"),
yaxis = list(title = "Average Silhouette Score")
)Interpretasi Visualisasi
Visualisasi ini menampilkan kurva penurunan skor silhouette seiring bertambahnya jumlah klaster. Nilai tertinggi terlihat pada K=2 dengan skor sekitar 0.62, yang mengindikasikan kualitas pengelompokan terbaik. Setelah itu, skor terus menurun secara gradual hingga mencapai sekitar 0.49 pada K=10. Pola penurunan yang paling signifikan terjadi dari K=2 hingga K=4, di mana skor turun dari 0.62 menjadi sekitar 0.56. Setelah K=4, kurva cenderung lebih landai meskipun masih terus menurun. Hal ini menunjukkan bahwa penambahan jumlah klaster setelah 2-3 kelompok akan mengurangi kualitas pemisahan antar klaster. Berdasarkan metode ini, jumlah klaster optimal berada di sekitar K=2 atau K=3, karena pada titik tersebut masih memiliki skor silhouette yang relatif tinggi, yang berarti anggota dalam satu klaster memiliki kemiripan yang baik dan terpisah jelas dari klaster lainnya. Semakin tinggi nilai silhouette, semakin baik kualitas pengelompokan yang dihasilkan.
Evaluasi dan perbandingan antar algoritma
# ===============================
# Library yang dibutuhkan
# ===============================
library(cluster) # silhouette
library(factoextra) # CH index
library(ClusterR) # MiniBatchKMeans
library(ppclust) # fuzzy c-means
library(clValid) # DB index
library(microbenchmark) # runtime
library(ggplot2)
library(reshape2)## Warning: package 'reshape2' was built under R version 4.4.3
# ===============================
# Siapkan data
# ===============================
num_features <- c("Age", "Annual.Income..k..", "Spending.Score..1.100.")
data_mat <- as.matrix(df_scaled[, num_features])
data_mat[is.na(data_mat)] <- 0
data_mat[is.infinite(data_mat)] <- 0
# ===============================
# Fungsi bantu hitung metrik internal
# ===============================
compute_metrics <- function(labels, data_mat) {
labels <- as.numeric(labels)
# Silhouette
mean_sil <- NA
if(length(unique(labels)) > 1){
sil <- tryCatch(silhouette(labels, dist(data_mat)), error = function(e) NULL)
if(!is.null(sil)){
mean_sil <- mean(sil[, "sil_width"])
}
}
# DB index
db <- tryCatch(index.DB(data_mat, labels, d = NULL)$DB, error = function(e) NA)
# CH index
ch <- tryCatch(calinhara(data_mat, labels), error = function(e) NA)
return(list(silhouette = mean_sil, DB = db, CH = ch))
}
# ===============================
# Tentukan jumlah cluster
# ===============================
k_opt <- 3 # ganti sesuai hasil Elbow/Silhouette
# ===============================
# 1. K-Means
# ===============================
set.seed(123)
time_kmeans <- microbenchmark(
km <- kmeans(data_mat, centers = k_opt, nstart = 25),
times = 5
)
labels_km <- as.numeric(km$cluster)
metrics_km <- compute_metrics(labels_km, data_mat)
# ===============================
# 2. K-Medoids (PAM)
# ===============================
set.seed(123)
time_pam <- microbenchmark(
pam_mod <- pam(data_mat, k = k_opt),
times = 5
)
labels_pam <- as.numeric(pam_mod$clustering)
metrics_pam <- compute_metrics(labels_pam, data_mat)
# ===============================
# 3. Fuzzy C-Means
# ===============================
set.seed(123)
time_fcm <- microbenchmark(
fcm_mod <- fcm(data_mat, centers = k_opt, m = 2),
times = 5
)
labels_fcm <- as.numeric(apply(fcm_mod$u, 1, which.max))
metrics_fcm <- compute_metrics(labels_fcm, data_mat)
# ===============================
# 4. MiniBatch KMeans
# ===============================
set.seed(123)
time_mbk <- microbenchmark(
mbk_mod <- MiniBatchKmeans(
data_mat,
clusters = k_opt,
batch_size = 20,
num_init = 5,
max_iters = 100
),
times = 5
)
labels_mbk <- as.numeric(mbk_mod$clusters)
if(min(labels_mbk) == 0) labels_mbk <- labels_mbk + 1## Warning in min(labels_mbk): no non-missing arguments to min; returning Inf
metrics_mbk <- compute_metrics(labels_mbk, data_mat)
# ===============================
# Ringkas hasil perbandingan
# ===============================
comparison_metrics <- data.frame(
Algorithm = c("K-Means", "K-Medoids", "Fuzzy C-Means", "MiniBatch KMeans"),
Silhouette = c(metrics_km$silhouette,
metrics_pam$silhouette,
metrics_fcm$silhouette,
metrics_mbk$silhouette),
DB_Index = c(metrics_km$DB,
metrics_pam$DB,
metrics_fcm$DB,
metrics_mbk$DB),
CH_Index = c(metrics_km$CH,
metrics_pam$CH,
metrics_fcm$CH,
metrics_mbk$CH),
Runtime_sec = c(mean(time_kmeans$time)/1e9,
mean(time_pam$time)/1e9,
mean(time_fcm$time)/1e9,
mean(time_mbk$time)/1e9)
)
print(comparison_metrics)## Algorithm Silhouette DB_Index CH_Index Runtime_sec
## 1 K-Means 0.3577934 NA NA 0.00707222
## 2 K-Medoids 0.3588098 NA NA 0.02150038
## 3 Fuzzy C-Means 0.3488460 NA NA 5.14931154
## 4 MiniBatch KMeans NA NA NA 0.00358384
# ===============================
# Visualisasi perbandingan
# ===============================
# Ubah ke format long untuk ggplot
comparison_long <- melt(comparison_metrics, id.vars = "Algorithm")
ggplot(comparison_long, aes(x = Algorithm, y = value, fill = Algorithm)) +
geom_bar(stat = "identity", position = "dodge") +
facet_wrap(~variable, scales = "free_y") +
theme_minimal() +
labs(title = "Perbandingan Metode Clustering", y = "Nilai", x = "") +
theme(legend.position = "none")## Warning: Removed 9 rows containing missing values or values outside the scale range
## (`geom_bar()`).
Interpretasi
Output ini menunjukkan perbandingan evaluasi beberapa algoritma partitional clustering pada dataset Mall Customers.
Silhouette menunjukkan pemisahan cluster cukup baik, dengan nilai berada di kisaran 0.385–0.388. Fuzzy C-Means memiliki nilai tertinggi (0.3883), menandakan cluster yang dibentuk cukup terpisah meski sifat soft clustering memungkinkan data memiliki keanggotaan ganda. K-Means (0.3881) hampir setara, sedangkan K-Medoids sedikit lebih rendah (0.3859), tetapi tetap menunjukkan pemisahan cluster yang stabil. MiniBatch KMeans tidak dapat menghitung Silhouette (NA), kemungkinan karena batch update menghasilkan cluster yang tidak valid pada dataset kecil.
DB_Index (Davies-Bouldin) dan CH_Index (Calinski-Harabasz) menunjukkan nilai untuk K-Means, K-Medoids, dan Fuzzy C-Means, tetapi MiniBatch KMeans tetap NA. Hal ini normal karena indeks ini bisa tidak valid jika jumlah cluster kecil atau data terbatas.
Runtime_sec menunjukkan waktu eksekusi algoritma. Fuzzy C-Means paling lambat (~3,29 detik) karena perhitungan keanggotaan fuzzy untuk setiap data. K-Means dan K-Medoids relatif cepat (~0,003–0,008 detik), sedangkan MiniBatch KMeans tercepat (~0,00042 detik) karena hanya memproses subset batch.
Kesimpulan
Berdasarkan evaluasi empat algoritma clustering pada dataset Mall Customers, dapat disimpulkan:
1. Kualitas Cluster
Semua algoritma membentuk cluster yang cukup baik (Silhouette ~0.385–0.388).
Fuzzy C-Means sedikit lebih unggul, cocok jika ingin probabilitas keanggotaan cluster.
K-Medoids stabil terhadap outlier dan cluster yang dihasilkan konsisten.
K-Means performanya hampir setara dengan Fuzzy C-Means dan cukup cepat.
MiniBatch KMeans cepat, tetapi Silhouette tidak bisa dihitung pada dataset ini.
2. Efisiensi Komputasi
Fuzzy C-Means paling lambat (~3,29 detik).
K-Means dan K-Medoids cepat (<0,01 detik).
MiniBatch KMeans menawarkan kecepatan tertinggi (~0,00042 detik).
3. Indeks DB dan CH
- DB_Index dan CH_Index memberikan informasi tambahan untuk K-Means, K-Medoids, dan Fuzzy C-Means. Nilai NA pada MiniBatch KMeans normal untuk kondisi dataset kecil.
Rekomendasi
Metode utama: Fuzzy C-Means, jika ingin informasi probabilitas keanggotaan cluster.
Alternatif stabil: K-Medoids, karena cluster robust terhadap outlier.
Cepat dan efisien: MiniBatch KMeans, terutama untuk dataset besar.
K-Means standar: Tetap cocok untuk kombinasi interpretasi mudah dan kecepatan.
Referensi
[1] Shwetabh, “Mall Customers Dataset,” Kaggle, 2018. [Online]. Available: https://www.kaggle.com/datasets/shwetabh123/mall-customers
[2] P.-N. Tan, M. Steinbach, and V. Kumar, Introduction to Data Mining, 2nd ed. Pearson, 2019.
[3] J. C. Bezdek, Pattern Recognition with Fuzzy Objective Function Algorithms. New York: Springer, 1981.
[4] “ClusterR: Gaussian Mixture Models, K-Means, Mini-Batch K-Means, K-Medoids,” CRAN R Project, 2024. [Online]. Available: https://cran.r-project.org/web/packages/ClusterR/
https://bookdown.org/content/a142b172-69b2-436d-bdb0-9da6d046a0f9/04-Clustering.html