1 Import Library dan Data

df <- read.csv("~/Aliyah/GITHUB LOMBA/perlombaan/POISSON 2025/Mall_Customers.csv")
head(df)
##   CustomerID Gender Age Annual.Income..k.. Spending.Score..1.100.
## 1          1   Male  19                 15                     39
## 2          2   Male  21                 15                     81
## 3          3 Female  20                 16                      6
## 4          4 Female  23                 16                     77
## 5          5 Female  31                 17                     40
## 6          6 Female  22                 17                     76

1.1 Deskripsi Dataset

Data yang digunakan dalam analisis ini berasal dari dataset Mall Customer Segmentation yang tersedia di platform Kaggle.

Dataset ini berisi informasi mengenai pelanggan dari sebuah pusat perbelanjaan, dengan variabel sebagai berikut:

  • CustomerID : ID unik setiap pelanggan
  • Gender : Jenis kelamin pelanggan
  • Age : Usia pelanggan
  • Annual Income (k$) : Pendapatan tahunan dalam ribuan dolar
  • Spending Score (1–100) : Skor pengeluaran berdasarkan perilaku belanja

2 Ekplorasi Data

summary(df)
##    CustomerID        Gender               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
par(mfrow = c(2, 2)) # layout 2x2

hist(df$Age,
main = "Distribusi Usia Pelanggan",
xlab = "Usia",
col = "skyblue",
border = "white")

hist(df$Annual.Income..k..,
main = "Distribusi Pendapatan Tahunan",
xlab = "Pendapatan (k$)",
col = "lightgreen",
border = "white")

hist(df$Spending.Score..1.100.,
main = "Distribusi Spending Score",
xlab = "Spending Score (1–100)",
col = "lightpink",
border = "white")

plot(df$Annual.Income..k.., df$Spending.Score..1.100.,
main = "Hubungan Pendapatan vs Spending Score",
xlab = "Pendapatan (k$)",
ylab = "Spending Score (1–100)",
pch = 19, col = "orange",
cex = 0.5)

par(mfrow = c(1, 1))

2.1 Data yang digunakan

data_cluster <- df %>%
  mutate(
    Annual_income = Annual.Income..k..,
    Spending_score = Spending.Score..1.100.,
  ) %>%
  select(Annual_income, Spending_score)
head(data_cluster)
##   Annual_income Spending_score
## 1            15             39
## 2            15             81
## 3            16              6
## 4            16             77
## 5            17             40
## 6            17             76

Pada tahap ini, dilakukan pemilihan variabel yang akan digunakan dalam proses klasterisasi pelanggan.
Dari dataset Mall Customer Segmentation, dua variabel utama dipilih karena dianggap paling relevan dalam menggambarkan perilaku konsumen, yaitu:

  • Annual Income (k$) → menunjukkan tingkat pendapatan tahunan pelanggan dalam ribuan dolar.
  • Spending Score (1–100) → skor yang diberikan oleh pihak mall berdasarkan kebiasaan belanja dan perilaku pelanggan.

3 Hierarchical Clustering

Tahap pertama analisis dilakukan dengan metode Hierarchical Clustering. Sebelum proses klasterisasi dilakukan, data terlebih dahulu distandarisasi agar seluruh variabel berada pada skala yang sebanding.
Setelah itu, dibuat matriks jarak (distance matrix) menggunakan metode Euclidean distance sebagai dasar pengelompokan antar pengamatan.

3.1 Standarisasi dan Matriks Jarak

library(RColorBrewer)

data <- data_cluster %>% select(where(is.numeric)) %>% na.omit()
data_scaled <- scale(data)
# Matriks jarak Euclidean
dist_matrix <- dist(data_scaled, method = "euclidean")#"euclidean", "maximum", "manhattan", "canberra", "binary" or "minkowski"
# Hierarchical clustering dengan metode Ward
hc_ward <- hclust(dist_matrix, method = "ward.D2") #"single", "complete", "average" (= UPGMA), "mcquitty" "median" or "centroid"
hc_ward
## 
## Call:
## hclust(d = dist_matrix, method = "ward.D2")
## 
## Cluster method   : ward.D2 
## Distance         : euclidean 
## Number of objects: 200

3.2 Dendrogam

fviz_dend(hc_ward, k = 5, rect = TRUE, palette = "jco",
main = "Dendrogram Hierarchical Clustering (Ward.D2)")
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the factoextra package.
##   Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## ℹ The deprecated feature was likely used in the factoextra package.
##   Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
## of ggplot2 3.3.4.
## ℹ The deprecated feature was likely used in the factoextra package.
##   Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Gambar di atas menunjukkan dendrogram hasil Hierarchical Clustering dengan metode pengelompokan Ward.D2. Sumbu vertikal (Height) merepresentasikan jarak atau tingkat perbedaan antar kelompok yang digabungkan. Dari hasil dendrogram, terlihat bahwa data pelanggan dapat dibagi menjadi sekitar lima klaster utama.

3.3 Hasil Clustering

3.3.1 Jumlah anggota tiap cluster

cluster_hc <- cutree(hc_ward, k = 5)
table(cluster_hc)
## cluster_hc
##  1  2  3  4  5 
## 23 21 85 39 32

Distribusi jumlah anggota pada masing-masing klaster adalah sebagai berikut:

  • Klaster 1: 23 pelanggan
  • Klaster 2: 21 pelanggan
  • Klaster 3: 85 pelanggan
  • Klaster 4: 39 pelanggan
  • Klaster 5: 32 pelanggan

Dari hasil tersebut, klaster 3 memiliki jumlah pelanggan terbanyak, menunjukkan bahwa sebagian besar pelanggan memiliki karakteristik yang mirip dengan kelompok ini. Sementara itu, klaster 2 merupakan yang paling sedikit, menandakan segmen pelanggan yang lebih spesifik atau unik.

3.3.2 Evaluasi Clustering

eval_cluster <- function(data_scaled, cluster, dist_matrix) {
sil <- mean(silhouette(cluster, dist_matrix)[, 3])
ch <- intCriteria(traj = as.matrix(data_scaled),
part = cluster, crit = "Calinski_Harabasz")$calinski_harabasz
dunn <- intCriteria(traj = as.matrix(data_scaled),
part = cluster, crit = "Dunn")$dunn
return(data.frame(Silhouette = sil,
Calinski_Harabasz = ch,
Dunn = dunn))
}

eval_cluster(data_scaled, cluster_hc, dist_matrix)
##   Silhouette Calinski_Harabasz       Dunn
## 1  0.5538089          244.4103 0.09029561

Nilai Silhouette di atas 0.5 menunjukkan klaster yang cukup baik, nilai Calinski–Harabasz yang tinggi menandakan klaster terpisah dengan baik, sedangkan Dunn Index menunjukkan pemisahan antar klaster yang masih tergolong moderat.

3.3.3 Karakteristik Clustering

data_clustered <- data.frame(data, cluster = factor(cluster_hc))
cluster_summary <- data_clustered %>%
group_by(cluster) %>%
summarise(across(where(is.numeric), mean, .names = "{.col}"))
cluster_summary
## # A tibble: 5 × 3
##   cluster Annual_income Spending_score
##   <fct>           <dbl>          <dbl>
## 1 1                26.3           20.9
## 2 2                25.1           80.0
## 3 3                55.8           49.1
## 4 4                86.5           82.1
## 5 5                89.4           15.6
heat_data <- cluster_summary %>%
pivot_longer(-cluster, names_to = "Variabel", values_to = "Rata2")
ggplot(heat_data, aes(x = Variabel, y = cluster, fill = Rata2)) +
geom_tile(color = "white") +
scale_fill_gradientn(colors = c("#56B1F7", "white", "#FF6B6B")) +
labs(title = "Heatmap Karakteristik Cluster",
x = "Variabel", y = "Cluster") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(heat_data, aes(x = Variabel, y = Rata2, fill = cluster)) +
geom_bar(stat = "identity", position = "dodge") +
scale_fill_brewer(palette = "Set2") +
labs(title = "Bar Plot Karakteristik Cluster",
x = "Variabel", y = "Rata-rata Nilai") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

Melalui hasil hierarchical clustering, Terbentuk 5 klaster dengan karakteristik sebagai berikut:

  • Klaster 1 → Pendapatan rendah, pengeluaran rendah (low income – low spending).
  • Klaster 2 → Pendapatan rendah, pengeluaran tinggi (low income – high spending).
  • Klaster 3 → Pendapatan menengah, pengeluaran sedang (middle income – moderate spending).
  • Klaster 4 → Pendapatan tinggi, pengeluaran tinggi (high income – high spending).
  • Klaster 5 → Pendapatan tinggi, pengeluaran rendah (high income – low spending).

Secara umum, kombinasi ini menunjukkan adanya lima segmen pelanggan dengan perilaku belanja yang berbeda berdasarkan tingkat pendapatan dan pengeluarannya.

4 Non Hierarchical Clustering

Selanjutnya dilakukan proses klasterisasi menggunakan metode non-hierarchical, yaitu K-Means dan K-Medoids (PAM). Metode ini dipilih karena membutuhkan penentuan jumlah klaster (k) di awal proses. Dalam analisis ini, kedua metode diterapkan untuk mengelompokkan pelanggan berdasarkan variabel Annual Income dan Spending Score, sehingga tiap klaster mencerminkan perilaku belanja yang berbeda.

4.1 Menentukan Jumlah Cluster Optimal

fviz_nbclust(data_scaled, kmeans, method = "wss") +
labs(title = "Elbow Method")

fviz_nbclust(data_scaled, kmeans, method = "silhouette") +
labs(title = "Silhouette Method")

Berdasarkan hasil Silhouette Method, diperoleh bahwa nilai k = 6 memberikan hasil klasterisasi yang paling optimal.

4.2 K-Means

set.seed(123)
kmeans_result <- kmeans(data_scaled, centers = 6, nstart = 25)
kmeans_result
## K-means clustering with 6 clusters of sizes 22, 23, 10, 81, 29, 35
## 
## Cluster means:
##   Annual_income Spending_score
## 1    -1.3262173     1.12934389
## 2    -1.3042458    -1.13411939
## 3     1.8709508     1.23143545
## 4    -0.2004097    -0.02638995
## 5     0.6850149     1.23811207
## 6     1.0523622    -1.28122394
## 
## Clustering vector:
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
##   2   1   2   1   2   1   2   1   2   1   2   1   2   1   2   1   2   1   2   1 
##  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
##   2   1   2   1   2   1   2   1   2   1   2   1   2   1   2   1   2   1   2   1 
##  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
##   2   1   2   4   2   1   4   4   4   4   4   4   4   4   4   4   4   4   4   4 
##  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
##   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4 
##  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
##   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4 
## 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
##   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4   4 
## 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
##   4   4   4   5   6   5   4   5   6   5   6   5   4   5   6   5   6   5   6   5 
## 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
##   6   5   4   5   6   5   6   5   6   5   6   5   6   5   6   5   6   5   6   5 
## 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 
##   6   5   6   5   6   5   6   5   6   5   6   5   6   5   6   5   6   5   6   5 
## 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 
##   6   3   6   3   6   3   6   3   6   3   6   3   6   3   6   3   6   3   6   3 
## 
## Within cluster sum of squares by cluster:
## [1]  5.217630  7.577407  3.681858 14.485632  5.514889 18.304646
##  (between_SS / total_SS =  86.2 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

4.2.1 Hasil Clustering

table(kmeans_result$cluster)
## 
##  1  2  3  4  5  6 
## 22 23 10 81 29 35

Distribusi jumlah anggota pada masing-masing klaster hasil K-Means adalah sebagai berikut:

  • Klaster 1: 22 pelanggan
  • Klaster 2: 23 pelanggan
  • Klaster 3: 10 pelanggan
  • Klaster 4: 81 pelanggan
  • Klaster 5: 29 pelanggan
  • Klaster 6: 35 pelanggan

Dari hasil tersebut, klaster 4 memiliki jumlah pelanggan terbanyak, menunjukkan kelompok dengan karakteristik yang paling umum di antara seluruh data.
Sebaliknya, klaster 3 memiliki anggota paling sedikit, menandakan segmen pelanggan yang lebih spesifik atau unik.

head(data.frame(Observasi = 1:length(kmeans_result$cluster),
           Cluster = kmeans_result$cluster),10)
##    Observasi Cluster
## 1          1       2
## 2          2       1
## 3          3       2
## 4          4       1
## 5          5       2
## 6          6       1
## 7          7       2
## 8          8       1
## 9          9       2
## 10        10       1

4.2.2 Evaluasi Clustering

cluster_km <- kmeans_result$cluster
dist_matrix <- dist(data_scaled, method = "euclidean")

sil <- mean(silhouette(cluster_km, dist_matrix)[, 3])
ch <- intCriteria(traj = as.matrix(data_scaled),
part = cluster_km,
crit = "Calinski_Harabasz")$calinski_harabasz
dunn <- intCriteria(traj = as.matrix(data_scaled),
part = cluster_km,
crit = "Dunn")$dunn

data.frame(Silhouette = sil,
Calinski_Harabasz = ch,
Dunn = dunn)
##   Silhouette Calinski_Harabasz       Dunn
## 1  0.5398801          243.0879 0.05989224

Nilai Silhouette mendekati 0.54 menandakan bahwa pemisahan antar klaster cukup baik. Nilai Calinski–Harabasz yang tinggi menunjukkan klaster yang terpisah dengan jelas. Sementara Dunn Index bernilai 0.0599, menunjukkan jarak antar klaster cukup kecil namun masih dapat diterima.

4.2.3 Karakteristik Cluster

data_clustered <- data.frame(data, cluster = factor(cluster_km))

cluster_summary <- data_clustered %>%
group_by(cluster) %>%
summarise(across(where(is.numeric), mean, .names = "{.col}"))

cluster_summary
## # A tibble: 6 × 3
##   cluster Annual_income Spending_score
##   <fct>           <dbl>          <dbl>
## 1 1                25.7           79.4
## 2 2                26.3           20.9
## 3 3               110.            82  
## 4 4                55.3           49.5
## 5 5                78.6           82.2
## 6 6                88.2           17.1

Berdasarkan hasil rata-rata tiap klaster, diperoleh karakteristik sebagai berikut:

  • Klaster 1 → Pendapatan rendah, spending score tinggi → pelanggan berpenghasilan kecil namun konsumtif.
  • Klaster 2 → Pendapatan rendah, spending score rendah → pelanggan dengan daya beli rendah dan jarang berbelanja.
  • Klaster 3 → Pendapatan sangat tinggi, spending score tinggi → pelanggan premium dengan kemampuan belanja besar.
  • Klaster 4 → Pendapatan menengah, spending score menengah → pelanggan dengan perilaku belanja yang seimbang.
  • Klaster 5 → Pendapatan tinggi, spending score tinggi → pelanggan potensial yang aktif berbelanja.
  • Klaster 6 → Pendapatan tinggi, spending score rendah → pelanggan berdaya beli tinggi namun cenderung hemat atau selektif dalam berbelanja.

Berikut adalah beberapa visualisasi karakteristik dari hasil K-Means Clustering:

fviz_cluster(kmeans_result, data = data_scaled,
palette = "jco", geom = "point",
main = "Visualisasi Hasil K-Means Clustering (3 Cluster)")

heat_data <- cluster_summary %>%
pivot_longer(-cluster, names_to = "Variabel", values_to = "Rata2")
heat_data
## # A tibble: 12 × 3
##    cluster Variabel       Rata2
##    <fct>   <chr>          <dbl>
##  1 1       Annual_income   25.7
##  2 1       Spending_score  79.4
##  3 2       Annual_income   26.3
##  4 2       Spending_score  20.9
##  5 3       Annual_income  110. 
##  6 3       Spending_score  82  
##  7 4       Annual_income   55.3
##  8 4       Spending_score  49.5
##  9 5       Annual_income   78.6
## 10 5       Spending_score  82.2
## 11 6       Annual_income   88.2
## 12 6       Spending_score  17.1
ggplot(heat_data, aes(x = Variabel, y = cluster, fill = Rata2)) +
geom_tile(color = "white") +
scale_fill_gradientn(colors = c("#56B1F7", "white", "#FF6B6B")) +
labs(title = "Heatmap Karakteristik Cluster",
x = "Variabel", y = "Cluster") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(heat_data, aes(x = Variabel, y = Rata2, fill = cluster)) +
geom_bar(stat = "identity", position = "dodge") +
scale_fill_brewer(palette = "Set2") +
labs(title = "Heatmap Karakteristik Cluster",
x = "Variabel", y = "Rata-rata Nilai") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

4.3 K-Medoids

# Membuat matriks jarak (bisa pilih salah satu: Euclidean, Manhattan, Canberra)
dist_euc <- dist(data_scaled, method = "euclidean")

# Klasterisasi dengan metode K-Medoids (PAM)
set.seed(123)
pam_euc <- pam(dist_euc, k = 6)

4.3.1 Hasil Clustering

# Melihat jumlah anggota tiap klaster
table(pam_euc$clustering)
## 
##  1  2  3  4  5  6 
## 21 21 38 49 39 32

Distribusi jumlah anggota pada masing-masing klaster hasil metode K-Medoids adalah sebagai berikut:

  • Klaster 1: 21 pelanggan
  • Klaster 2: 21 pelanggan
  • Klaster 3: 38 pelanggan
  • Klaster 4: 49 pelanggan
  • Klaster 5: 39 pelanggan
  • Klaster 6: 32 pelanggan

Dari hasil tersebut, klaster 4 memiliki jumlah pelanggan terbanyak, menunjukkan kelompok dengan karakteristik yang paling umum.
Sedangkan klaster 1 dan klaster 2 memiliki jumlah anggota paling sedikit, menggambarkan segmen pelanggan yang lebih spesifik atau berperilaku unik.

4.3.2 Evaluasi Clustering

# Evaluasi hasil klasterisasi
silhouette_score <- mean(silhouette(pam_euc$clustering, dist_euc)[, 3])
ch_score <- intCriteria(traj = as.matrix(data_scaled),
                        part = pam_euc$clustering,
                        crit = "Calinski_Harabasz")$calinski_harabasz
dunn_score <- intCriteria(traj = as.matrix(data_scaled),
                          part = pam_euc$clustering,
                          crit = "Dunn")$dunn
# Menampilkan hasil evaluasi
data.frame(
  Silhouette = silhouette_score,
  Calinski_Harabasz = ch_score,
  Dunn = dunn_score
)
##   Silhouette Calinski_Harabasz       Dunn
## 1  0.4557916            231.72 0.04380516

Nilai Silhouette sebesar 0.46 menandakan bahwa pemisahan antar klaster tergolong cukup baik, meskipun masih bisa ditingkatkan. Nilai Calinski–Harabasz yang cukup tinggi mengindikasikan klaster yang relatif terpisah. Sedangkan Dunn Index menunjukkan jarak antar klaster yang cukup kecil, menandakan adanya beberapa kemiripan antar kelompok pelanggan.

4.3.3 Karakteristik Clustering

# Membuat data hasil klasterisasi K-Medoids
data_clustered_pam <- data.frame(data_cluster, cluster = factor(pam_euc$clustering))

# Menghitung rata-rata tiap variabel untuk setiap klaster
cluster_summary_pam <- data_clustered_pam %>%
  group_by(cluster) %>%
  summarise(across(where(is.numeric), mean, .names = "{.col}"))

cluster_summary_pam
## # A tibble: 6 × 3
##   cluster Annual_income Spending_score
##   <fct>           <dbl>          <dbl>
## 1 1                25.1           19.5
## 2 2                25.1           80.0
## 3 3                46.0           51.3
## 4 4                62.7           46.9
## 5 5                86.5           82.1
## 6 6                89.4           15.6

Berdasarkan hasil rata-rata tiap klaster pada metode K-Medoids, diperoleh karakteristik sebagai berikut:

  • Klaster 1 → Pendapatan rendah, spending score rendah → pelanggan dengan daya beli kecil dan jarang berbelanja.
  • Klaster 2 → Pendapatan rendah, spending score tinggi → pelanggan berpenghasilan kecil namun konsumtif.
  • Klaster 3 → Pendapatan menengah, spending score menengah-tinggi → pelanggan dengan perilaku belanja cukup aktif dan seimbang.
  • Klaster 4 → Pendapatan menengah-tinggi, spending score menengah → pelanggan dengan kemampuan belanja cukup baik namun cenderung stabil.
  • Klaster 5 → Pendapatan tinggi, spending score tinggi → pelanggan premium dengan potensi besar untuk program loyalitas.
  • Klaster 6 → Pendapatan tinggi, spending score rendah → pelanggan berdaya beli tinggi tetapi cenderung hemat atau selektif dalam pengeluaran.

Berikut adalah beberapa visualisasi karakteristik dari hasil K-Medoids Clustering:

# Visualisasi hasil K-Medoids dengan data scaled
fviz_cluster(list(data = data_scaled, cluster = pam_euc$clustering),
             palette = "jco", geom = "point",
             main = "Visualisasi Hasil K-Medoids Clustering (6 Cluster)")

heat_data_pam <- cluster_summary_pam %>%
pivot_longer(-cluster, names_to = "Variabel", values_to = "Rata2")
ggplot(heat_data_pam, aes(x = Variabel, y = cluster, fill = Rata2)) +
geom_tile(color = "white") +
scale_fill_gradientn(colors = c("#56B1F7", "white", "#FF6B6B")) +
labs(title = "Heatmap Karakteristik Cluster (K-Medoids)",
x = "Variabel", y = "Cluster") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(heat_data_pam, aes(x = Variabel, y = Rata2, fill = cluster)) +
geom_bar(stat = "identity", position = "dodge") +
scale_fill_brewer(palette = "Set2") +
labs(title = "Perbandingan Karakteristik Tiap Cluster (K-Medoids)",
x = "Variabel", y = "Rata-rata Nilai") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))