PENERAPAN K-MEANS CLUSTERING DALAM PENGELOMPOKAN KABUPATEN/KOTA DI PROVINSI JAWA TIMUR BERDASARKAN RATA-RATA KONSUMSI per KAPITA SEMINGGU BEBERAPA MACAM BAHAN MAKANAN PENTING

Data

Data yang digunakan untuk pengelompokan kali ini yaitu data rata-rata konsumsi per kapita seminggu beberapa macam bahan makanan penting 2025. Data rata-rata konsumsi per kapita seminggu merupakan indikator statistik yang menggambarkan jumlah rata-rata bahan makanan yang dikonsumsi setiap penduduk dalam satu minggu, yang dihitung dari survei rumah tangga.

data <- "Kabupaten_Kota,ayam_ras,Tahu,Tempe,Telur
Pacitan,0.095,0.22,0.208,2.586
Ponorogo,0.1,0.223,0.214,2.363
Trenggalek,0.124,0.186,0.185,2.266
Tulungagung,0.127,0.179,0.149,2.518
Blitar,0.099,0.22,0.167,2.407
Kediri,0.134,0.282,0.183,2.281
Malang,0.125,0.216,0.219,2.128
Lumajang,0.125,0.191,0.191,1.995
Jember,0.106,0.284,0.248,1.901
Banyuwangi,0.119,0.265,0.175,1.897
Bondowoso,0.105,0.393,0.249,1.837
Situbondo,0.082,0.342,0.202,1.546
Probolinggo,0.085,0.25,0.179,1.737
Pasuruan,0.098,0.236,0.261,1.967
Sidoarjo,0.161,0.21,0.204,2.484
Mojokerto,0.118,0.251,0.231,2.017
Jombang,0.1,0.256,0.19,1.968
Nganjuk,0.121,0.372,0.27,2.294
Madiun,0.126,0.237,0.293,2.264
Magetan,0.148,0.198,0.219,2.502
Ngawi,0.119,0.198,0.238,2.537
Bojonegoro,0.106,0.149,0.228,1.883
Tuban,0.113,0.154,0.202,1.914
Lamongan,0.128,0.168,0.149,2.069
Gresik,0.168,0.2,0.176,2.578
Bangkalan,0.101,0.15,0.117,1.614
Sampang,0.077,0.218,0.148,1.93
Pamekasan,0.089,0.207,0.123,1.974
Sumenep,0.094,0.222,0.118,1.698
Kota Kediri,0.143,0.302,0.17,2.388
Kota Blitar,0.117,0.181,0.133,2.152
Kota Malang,0.174,0.236,0.217,2.49
Kota Probolinggo,0.117,0.281,0.172,2.164
Kota Pasuruan,0.141,0.251,0.243,2.369
Kota Mojokerto,0.18,0.212,0.205,2.291
Kota Madiun,0.153,0.222,0.228,2.403
Kota Surabaya,0.199,0.264,0.161,2.858
Kota Batu,0.162,0.226,0.25,2.355"


df <- read.csv(text = data)
df_final <- df %>% remove_rownames() %>% column_to_rownames(var = "Kabupaten_Kota")

head(df_final)
##             ayam_ras  Tahu Tempe Telur
## Pacitan        0.095 0.220 0.208 2.586
## Ponorogo       0.100 0.223 0.214 2.363
## Trenggalek     0.124 0.186 0.185 2.266
## Tulungagung    0.127 0.179 0.149 2.518
## Blitar         0.099 0.220 0.167 2.407
## Kediri         0.134 0.282 0.183 2.281

Uji Multikolinearitas

Uji asumsi non-multikolinearitas bertujuan untuk mengetahui apakah terdapat korelasi yang tinggi di antara variabel-variabelindependen yang digunakan dalam model. Untuk mengevaluasi hal ini, digunakan nilai Tolerance dan VIF (Variance Inflation Factor) dari masing-masing variabel. Asumsi non-multikolinearitas dianggap terpenuhi jika nilai VIF kurang dari 10, yang berarti tidak terdapat hubungan linear yang kuat antar variabel dalam model penelitian.

  • Hipotesis

\(H_0\):Tidak terdapat hubungan linier antar variabel independen (Tidak terjadi multikolinieritas)

\(H_1\):Terdapat hubungan linier antar variabel independen (Terjadi multikolinieritas)

  • Taraf Signifikansi

\(\alpha=5\%\)

  • Statistik Uji
df_numerik <- df_final[, c("ayam_ras", "Tahu", "Tempe", "Telur")]

vif_all <- sapply(names(df_numerik), function(x) {
  formula <- as.formula(paste(x, "~ ."))
  model <- lm(formula, data = df_numerik)
  
  r_sq <- summary(model)$r.squared
  vif_val <- 1 / (1 - r_sq)
  return(vif_val)
})

vif_table <- data.frame(Variabel = names(vif_all), VIF = as.numeric(vif_all))
print(vif_table)
##   Variabel      VIF
## 1 ayam_ras 1.980769
## 2     Tahu 1.183005
## 3    Tempe 1.210851
## 4    Telur 2.017420
corr_matrix <- cor(df_final[, c("ayam_ras", "Tahu", "Tempe", "Telur")])
ggcorrplot(corr_matrix, 
           type = "full",             # Menampilkan 4x4 penuh
           lab = TRUE,                # Menampilkan angka korelasi
           lab_size = 4, 
           method = "square",         # Bentuk kotak agar terlihat seperti matriks
           colors = c("#6D9EC1", "white", "#E4672E"), 
           title = "Matriks Korelasi 4x4 Konsumsi Protein Jatim",
           ggtheme = theme_minimal())
## 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 ggcorrplot package.
##   Please report the issue at <https://github.com/kassambara/ggcorrplot/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

  • Kriteria Penolakan

\(H_0\) ditolak jika nilai VIF kurang dari 10.

  • Keputusan

\(H_0\) gagal ditolak karena nilai VIF masing-masing variabel kurang dari 10

  • Kesimpulan

Pada taraf signifikansi $% $ \(H_0\) ditolak sehingga dapat disimpulkan bahwa asumsi Non-Multikolinieritas terpenuhi

Standarisasi Data

Standardisasi data merupakan proses transformasi data yang bertujuan untuk menyamakan skala atau rentang nilai antar variabel sehingga data dapat dibandingkan secara lebih proporsional. Proses ini dilakukan dengan mengubah nilai asli setiap variabel menjadi nilai baku menggunakan metode Z-score

df_scaled <- scale(df_final)
head(df_scaled)
##                ayam_ras       Tahu      Tempe     Telur
## Pacitan     -0.97275695 -0.2345065  0.2353606 1.3447182
## Ponorogo    -0.79986283 -0.1801697  0.3733097 0.6162683
## Trenggalek   0.03002898 -0.8503246 -0.2934445 0.2994089
## Tulungagung  0.13376546 -0.9771106 -1.1211393 1.1225900
## Blitar      -0.83444165 -0.2345065 -0.7072919 0.7599983
## Kediri       0.37581723  0.8884557 -0.3394275 0.3484078

Penentuan jumlah cluster optimal

Penentuan jumlah cluster optimal dilakukan menggunakan metode Silhouette. Metode ini mengevaluasi kualitas klaster berdasarkan nilai koefisien Silhouette, yang menunjukkan seberapa baik suatu data cocok dengan klasternya sendiri dibandingkan dengan klaster lain. Jumlah cluster yang optimal ditentukan dari nilai Silhouette rata-rata tertinggi, yang mengindikasikan pemisahan klaster yang baik serta kekompakan antar anggota dalam klaster.

wss <- numeric(10)
for (k in 1:10) {
  set.seed(123)
  wss[k] <- kmeans(df_scaled, centers = k, nstart = 25)$tot.withinss
}

plot(1:10, wss, type = "b", pch = 19, frame = FALSE, 
     xaxt = "n", # Menghilangkan sumbu X otomatis
     xlab = "Jumlah Klaster (K)", 
     ylab = "Total WSS",
     main = "Elbow Method: Penentuan K Optimal",
     col = "blue")

axis(1, at = 1:10) 
abline(v = 3, lty = 2, col = "red")

k_range <- 2:10
sil_widths <- numeric(length(k_range))
for (i in seq_along(k_range)) {
  k <- k_range[i]
  km_temp <- kmeans(df_scaled, centers = k, nstart = 25)
  sh <- silhouette(km_temp$cluster, dist(df_scaled))
  sil_widths[i] <- mean(sh[, 3])
}
tabel_silhouette <- data.frame(
  K = k_range,
  Average_Silhouette_Width = round(sil_widths, 4)
)
knitr::kable(tabel_silhouette, 
             col.names = c("Jumlah Klaster (K)", "Rata-rata Nilai Silhouette"),
             caption = "Nilai Silhouette untuk Penentuan K Optimal")
Nilai Silhouette untuk Penentuan K Optimal
Jumlah Klaster (K) Rata-rata Nilai Silhouette
2 0.2544
3 0.2835
4 0.2387
5 0.2349
6 0.2731
7 0.2939
8 0.2949
9 0.3026
10 0.3234

Dari hasil di atas menunjukkan hasil penentuan jumlah cluster optimal yang diperoleh adalah 3 cluster, karena nilai koefisien Silhouette tertinggi ditemukan pada cluster ke-3 dengan nilai sebesar 0.2835. Penentuan ini dilakukan dikarenakan bila memilih nilai K yang besar dengal populasi yang sedikit akan kurang efektif.

Analisis K-Means Clustering

set.seed(123) 
final_cluster <- kmeans(df_scaled, centers = 3, nstart = 25)
print(final_cluster)
## K-means clustering with 3 clusters of sizes 19, 12, 7
## 
## Cluster means:
##     ayam_ras         Tahu      Tempe      Telur
## 1 -0.5432515 -0.467106538 -0.5935443 -0.3523621
## 2  1.1653671  0.008500068  0.2315287  0.9374831
## 3 -0.5232322  1.253289059  1.2141426 -0.6507026
## 
## Clustering vector:
##          Pacitan         Ponorogo       Trenggalek      Tulungagung 
##                1                1                1                1 
##           Blitar           Kediri           Malang         Lumajang 
##                1                2                1                1 
##           Jember       Banyuwangi        Bondowoso        Situbondo 
##                3                1                3                3 
##      Probolinggo         Pasuruan         Sidoarjo        Mojokerto 
##                1                3                2                3 
##          Jombang          Nganjuk           Madiun          Magetan 
##                1                3                3                2 
##            Ngawi       Bojonegoro            Tuban         Lamongan 
##                2                1                1                1 
##           Gresik        Bangkalan          Sampang        Pamekasan 
##                2                1                1                1 
##          Sumenep      Kota Kediri      Kota Blitar      Kota Malang 
##                1                2                1                2 
## Kota Probolinggo    Kota Pasuruan   Kota Mojokerto      Kota Madiun 
##                1                2                2                2 
##    Kota Surabaya        Kota Batu 
##                2                2 
## 
## Within cluster sum of squares by cluster:
## [1] 39.67813 18.67328 17.16103
##  (between_SS / total_SS =  49.0 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"
set.seed(123) 
final_cluster <- kmeans(df_scaled, centers = 3, nstart = 25)
data_hasil_final <- data.frame(df, Cluster = as.factor(final_cluster$cluster))
head(data_hasil_final, 10)
##             Kabupaten_Kota ayam_ras  Tahu Tempe Telur Cluster
## Pacitan            Pacitan    0.095 0.220 0.208 2.586       1
## Ponorogo          Ponorogo    0.100 0.223 0.214 2.363       1
## Trenggalek      Trenggalek    0.124 0.186 0.185 2.266       1
## Tulungagung    Tulungagung    0.127 0.179 0.149 2.518       1
## Blitar              Blitar    0.099 0.220 0.167 2.407       1
## Kediri              Kediri    0.134 0.282 0.183 2.281       2
## Malang              Malang    0.125 0.216 0.219 2.128       1
## Lumajang          Lumajang    0.125 0.191 0.191 1.995       1
## Jember              Jember    0.106 0.284 0.248 1.901       3
## Banyuwangi      Banyuwangi    0.119 0.265 0.175 1.897       1
library(dplyr)
profil_klaster <- data_hasil_final %>%
  group_by(Cluster) %>%
  summarise(
    Jumlah_Daerah = n(),
    Mean_Ayam = mean(ayam_ras),
    Mean_Tahu = mean(Tahu),
    Mean_Tempe = mean(Tempe),
    Mean_Telur = mean(Telur)
  )

Berdasarkan nilai rata-rata di atas, karakteristik tiap klaster dapat dijabarkan sebagai berikut:

Klaster 1: Wilayah Konsumsi Protein Rendah (Minimalis) Klaster ini terdiri dari 14 Kabupaten/Kota. Kelompok ini memiliki nilai rata-rata terendah pada hampir seluruh komoditas, terutama pada ayam ras (0,1050) dan tempe (0,1637). Secara statistik, wilayah dalam kelompok ini menunjukkan pola konsumsi protein yang paling terbatas dibandingkan kelompok lainnya di Jawa Timur.

Klaster 2: Wilayah Konsumsi Protein Hewani Tinggi Klaster ini merupakan kelompok terbesar dengan anggota 17 Kabupaten/Kota. Karakteristik utama kelompok ini adalah tingginya konsumsi protein hewani, di mana rata-rata konsumsi ayam ras mencapai 0,1443 dan telur mencapai 2,4351 (tertinggi di antara semua klaster). Wilayah ini umumnya memiliki daya beli atau aksesibilitas yang lebih baik terhadap produk peternakan.

Klaster 3: Wilayah Konsumsi Protein Nabati Tinggi Klaster ini hanya beranggotakan 7 Kabupaten/Kota, namun memiliki pola yang sangat unik. Meskipun konsumsi ayam dan telurnya berada di level menengah, masyarakat di wilayah ini merupakan konsumen tahu dan tempe tertinggi di Jawa Timur, dengan rata-rata konsumsi tahu sebesar 0,3021 dan tempe 0,2506. Hal ini menunjukkan preferensi kuat terhadap protein berbasis kedelai.