A. Pendahuluan

Inflasi merupakan kenaikan harga-harga secara umum dan berlangsung secara terus-menerus. Perubahan harga pada komoditas pangan sering menjadi penyumbang utama dalam pembentukan inflasi, khususnya pada komponen volatile food. Komoditas seperti cabai dan bawang memiliki karakteristik harga yang relatif mudah berfluktuasi karena dipengaruhi oleh faktor produksi, distribusi, serta kondisi musim.

Provinsi Jawa Tengah merupakan salah satu provinsi dengan jumlah penduduk yang besar di Indonesia, sehingga kebutuhan terhadap bahan pangan juga tinggi. Di sisi lain, karakteristik ekonomi, distribusi, dan aksesibilitas antar kabupaten/kota berbeda-beda sehingga dapat menyebabkan variasi harga pangan antar wilayah.

Untuk mengidentifikasi pola kemiripan harga tersebut, analisis ini menggunakan K-Means Clustering untuk mengelompokkan 12 kabupaten/kota berdasarkan karakteristik harga beberapa komoditas pangan strategis. Hasil pengelompokan diharapkan dapat menjadi informasi pendukung dalam pemantauan dan pengendalian inflasi pangan yang lebih terarah.

B. Metodologi Penelitian

1. Data yang Digunakan

Data yang digunakan dalam analisis adalah data sekunder yaitu data harga pangan (Rp/kg) pada 12 kabupaten/kota di Provinsi Jawa Tengah pada tanggal 2 Januari 2026. Data tersebut bersumber dari Pusat Informasi Harga Pangan Strategis (PIHPS) Nasional yang dikelola oleh Bank Indonesia dan dapat diakses melalui situs resmi https://www.bi.go.id/hargapangan. Data yang digunakan mencakup berbagai komoditas pangan strategis yang berperan penting dalam pembentukan inflasi daerah.

harga_pangan <- data.frame(
  Daerah = c(
    "Kab. Banyumas", "Kab. Boyolali", "Kab. Cilacap", "Kab. Karanganyar",
    "Kab. Klaten", "Kab. Kudus", "Kota Semarang", "Kab. Sragen",
    "Kab. Sukoharjo", "Kota Surakarta", "Kota Tegal", "Kab. Wonogiri"
  ),
  Beras = c(14750, 14850, 15950, 15250, 14250, 14200, 16100, 14100, 16200, 15850, 14250, 14550),
  Daging_Ayam = c(40250, 36250, 39000, 37750, 35500, 40000, 37000, 36750, 37500, 36750, 40750, 36500),
  Bawang_Merah = c(53750, 40000, 35250, 35000, 42250, 47500, 36250, 36750, 37250, 38250, 41250, 38750),
  Cabai_Merah = c(53750, 38150, 34400, 32750, 39000, 56250, 34650, 29400, 36750, 26900, 42500, 37500),
  Cabai_Rawit = c(63250, 34150, 44400, 44400, 34400, 66250, 47000, 40400, 38750, 40000, 56900, 38150),
  Gula_Pasir = c(17450, 18200, 18100, 17650, 18300, 17350, 17950, 17950, 17800, 18350, 17650, 17350)
)

row.names(harga_pangan) <- harga_pangan$Daerah
data_num <- harga_pangan[, -1]

kable(
  harga_pangan %>% 
    rename_with(~ gsub("_", " ", .x)),   
  caption = "**Tabel 1.** Data Harga Pangan (Rp/kg) pada 12 Kabupaten/Kota",
  row.names = FALSE
)
Tabel 1. Data Harga Pangan (Rp/kg) pada 12 Kabupaten/Kota
Daerah Beras Daging Ayam Bawang Merah Cabai Merah Cabai Rawit Gula Pasir
Kab. Banyumas 14750 40250 53750 53750 63250 17450
Kab. Boyolali 14850 36250 40000 38150 34150 18200
Kab. Cilacap 15950 39000 35250 34400 44400 18100
Kab. Karanganyar 15250 37750 35000 32750 44400 17650
Kab. Klaten 14250 35500 42250 39000 34400 18300
Kab. Kudus 14200 40000 47500 56250 66250 17350
Kota Semarang 16100 37000 36250 34650 47000 17950
Kab. Sragen 14100 36750 36750 29400 40400 17950
Kab. Sukoharjo 16200 37500 37250 36750 38750 17800
Kota Surakarta 15850 36750 38250 26900 40000 18350
Kota Tegal 14250 40750 41250 42500 56900 17650
Kab. Wonogiri 14550 36500 38750 37500 38150 17350

2. Variabel Penelitian

Variabel yang digunakan dalam penelitian ini adalah:

  1. \(X_1\): Harga Beras (Rp/kg)
  2. \(X_2\): Harga Daging Ayam (Rp/kg)
  3. \(X_3\): Harga Bawang Merah (Rp/kg)
  4. \(X_4\): Harga Cabai Merah (Rp/kg)
  5. \(X_5\): Harga Cabai Rawit (Rp/kg)
  6. \(X_6\): Harga Gula Pasir (Rp/kg)

3. Langkah-langkah Analisis Data

Data yang diperoleh dianalisis menggunakan pendekatan Non-Hierarchical Clustering menggunakan metode K-Means. Metode ini bertujuan untuk mengelompokkan objek-objek ke dalam k kelompok berdasarkan kesamaan karakteristiknya. Pengolahan data dilakukan dengan bantuan program R.

Langkah-langkah analisis menggunakan metode K-Means Clustering adalah sebagai berikut:

  1. Memilih variabel yang digunakan sebagai indikator penelitian;
  2. Melakukan uji kecukupan data menggunakan uji KMO;
  3. Melakukan uji asumsi multikolinearitas;
  4. Menentukan jumlah cluster optimal menggunakan metode silhouette;
  5. Melakukan analisis cluster dengan metode K-Means Clustering;
  6. Melakukan profiling cluster atau interpretasi karakteristik dari setiap cluster.

C. Hasil dan Pembahasan

Pada bagian ini ditampilkan hasil analisis beserta interpretasinya, sesuai langkah analisis pada bagian metodologi.

1. Uji Asumsi Kecukupan Data

Pengujian kecukupan data dilakukan untuk mengukur apakah sampel yang digunakan sudah mewakili populasi yang ada. Uji kecukupan data dapat dilakukan dengan Uji Kaiser Mayer Olkin (KMO). Uji KMO ini mengukur kecukupan sampling secara menyeluruh dan mengukur kecukupan sampling untuk setiap indikator. Uji Kaiser-Mayer-Olkin (KMO) memiliki nilai 0 sampai dengan 1. Data dinyatakan layak apabila nilai KMO ≥ 0.5. Berdasarkan hasil pengujian, diperoleh nilai KMO untuk masing-masing variabel sebagai berikut:

kmo_result <- psych::KMO(cor(data_num))

kmo_table <- data.frame(
  Variabel = c("Overall", gsub("_", " ", colnames(data_num))),
  Nilai_KMO = round(c(kmo_result$MSA, kmo_result$MSAi), 2)
)

knitr::kable(
  kmo_table,
  col.names = c("Variabel", "Nilai KMO Factor Adequacy"),
  caption = "**Tabel 2.** Uji Kaiser-Meyer-Olkin (KMO) *Measure of Sampling Adequacy*",
  row.names = FALSE
)
Tabel 2. Uji Kaiser-Meyer-Olkin (KMO) Measure of Sampling Adequacy
Variabel Nilai KMO Factor Adequacy
Overall 0.74
Beras 0.83
Daging Ayam 0.71
Bawang Merah 0.67
Cabai Merah 0.76
Cabai Rawit 0.74
Gula Pasir 0.77

Karena seluruh nilai KMO berada di atas 0.5, maka dapat disimpulkan bahwa sampel yang digunakan sudah cukup layak untuk dianalisis lebih lanjut.

2. Uji Asumsi Multikolinearitas (VIF)

Uji multikolinearitas dilakukan untuk memastikan bahwa antar variabel tidak memiliki hubungan yang terlalu kuat. Pengujian ini menggunakan nilai Variance Inflation Factor (VIF). Nilai VIF diperoleh dengan meregresikan setiap variabel secara bergantian sebagai variabel dependen (Y), sedangkan variabel lainnya dijadikan variabel independen (X). Suatu variabel dinyatakan tidak mengalami multikolinearitas apabila memiliki nilai VIF < 10. Berdasarkan hasil pengujian menggunakan R, diperoleh nilai VIF sebagai berikut:

vif_results <- data.frame(
  Variabel = character(),
  R_Squared = numeric(),
  VIF = numeric()
)

for (i in seq_len(ncol(data_num))) {
  target <- colnames(data_num)[i]
  predictors <- colnames(data_num)[-i]
  
  form <- as.formula(paste(target, "~", paste(predictors, collapse = "+")))
  model <- lm(form, data = data_num)
  
  r2 <- summary(model)$r.squared
  vif <- 1 / (1 - r2)
  
  vif_results <- rbind(
    vif_results,
    data.frame(
      Variabel = target,
      R_Squared = round(r2, 4),
      VIF = round(vif, 3)
    )
  )
}

vif_results$Variabel <- gsub("_", " ", vif_results$Variabel)
names(vif_results)[names(vif_results) == "R_Squared"] <- "R Squared"

kable(
  vif_results,
  digits = 3,
  caption = "**Tabel 3.** Hasil Pemeriksaan Multikolinearitas (VIF)",
  row.names = FALSE
)
Tabel 3. Hasil Pemeriksaan Multikolinearitas (VIF)
Variabel R Squared VIF
Beras 0.294 1.416
Daging Ayam 0.830 5.895
Bawang Merah 0.822 5.609
Cabai Merah 0.863 7.283
Cabai Rawit 0.889 8.983
Gula Pasir 0.561 2.279

Berdasarkan Tabel 3 diperoleh bahwa nilai VIF dari setiap variabel memiliki nilai yang kurang dari 10, sehingga dapat disimpulkan bahwa tidak terjadi multikolinearitas antar variabel dan uji asumsi multikolinearitas terpenuhi.

3. Standardisasi Data

Seluruh variabel memiliki satuan yang sama (Rp/kg), namun rentang nilainya berbeda cukup signifikan. Perbedaan skala ini dapat menyebabkan variabel dengan nilai besar mendominasi perhitungan jarak dalam clustering. Oleh karena itu, data distandarisasi menggunakan metode Z-score agar setiap variabel memiliki rata-rata 0 dan simpangan baku 1, sehingga kontribusinya menjadi seimbang dalam analisis. Berikut adalah data yang telah distandardisasi:

data_scaled <- scale(data_num)

kable(
  data_scaled,
  digits = 3,
  col.names = gsub("_", " ", colnames(data_scaled)),
  caption = "**Tabel 4.** Hasil Standardisasi Data"
)
Tabel 4. Hasil Standardisasi Data
Beras Daging Ayam Bawang Merah Cabai Merah Cabai Rawit Gula Pasir
Kab. Banyumas -0.340 1.389 2.454 1.735 1.625 -1.098
Kab. Boyolali -0.216 -0.910 -0.034 -0.040 -1.065 1.005
Kab. Cilacap 1.144 0.671 -0.894 -0.466 -0.117 0.724
Kab. Karanganyar 0.278 -0.048 -0.939 -0.654 -0.117 -0.537
Kab. Klaten -0.958 -1.341 0.373 0.057 -1.042 1.285
Kab. Kudus -1.020 1.245 1.323 2.019 1.902 -1.379
Kota Semarang 1.329 -0.479 -0.713 -0.438 0.123 0.304
Kab. Sragen -1.144 -0.623 -0.622 -1.035 -0.487 0.304
Kab. Sukoharjo 1.453 -0.192 -0.532 -0.199 -0.640 -0.117
Kota Surakarta 1.020 -0.623 -0.351 -1.320 -0.524 1.425
Kota Tegal -0.958 1.677 0.192 0.455 1.038 -0.537
Kab. Wonogiri -0.587 -0.766 -0.260 -0.114 -0.695 -1.379

4. Analisis Cluster dengan K-Means

4.1 Penentuan Jumlah Cluster Optimal (Metode Silhouette)

Jumlah cluster ditentukan menggunakan metode Silhouette. Nilai average silhouette width yang lebih besar menunjukkan pemisahan antar cluster yang lebih baik dan kekompakan dalam cluster yang lebih tinggi.

set.seed(123)

fviz_nbclust(data_scaled, kmeans, method = "silhouette") +
  labs(
    x = "Jumlah cluster (k)",
    y = "Average silhouette width"
  )
Gambar 1. Penentuan Jumlah Cluster Optimal dengan Metode Silhouette

Gambar 1. Penentuan Jumlah Cluster Optimal dengan Metode Silhouette

Untuk memperjelas, dihitung rata-rata silhouette pada rentang kandidat k.

k_range <- 2:10
sil_width <- numeric(length(k_range))

for (i in seq_along(k_range)) {
  k <- k_range[i]
  km_tmp <- kmeans(data_scaled, centers = k, nstart = 50)
  ss <- silhouette(km_tmp$cluster, dist(data_scaled))
  sil_width[i] <- mean(ss[, 3])
}

sil_table <- data.frame(
  Jumlah_Cluster = k_range,
  Average_Silhouette = round(sil_width, 3)
)

kable(
  sil_table,
  col.names = c("Jumlah *Cluster*", "Rata-Rata *Silhouette*"),
  caption = "**Tabel 5.** Rata-rata *Silhouette* untuk Berbagai Nilai k",
  row.names = FALSE,
  align = c("c","c")
)
Tabel 5. Rata-rata Silhouette untuk Berbagai Nilai k
Jumlah Cluster Rata-Rata Silhouette
2 0.501
3 0.359
4 0.345
5 0.307
6 0.252
7 0.228
8 0.205
9 0.198
10 0.124

Berdasarkan metode silhouette, jumlah cluster optimal terletak pada jumlah k = 2 dengan nilai rata-rata silhouette tertinggi yaitu 0.501 yang dapat digolongkan ke dalam susunan cluster baik. Sehingga jumlah cluster optimal yang dipilih adalah 2 cluster.

4.2 Hasil Clustering

set.seed(123)
km_res <- kmeans(data_scaled, centers = 2, nstart = 50)

# Tabel jumlah anggota cluster
cluster_size <- data.frame(
  Cluster = 1:length(km_res$size),
  Jumlah_Anggota = km_res$size
)

kable(
  cluster_size,
  col.names = c("*Cluster*", "Jumlah Anggota"),
  caption = "**Tabel 6**. Jumlah Anggota *Cluster*",
  row.names = FALSE,
  align = c("c","c")
)
Tabel 6. Jumlah Anggota Cluster
Cluster Jumlah Anggota
1 9
2 3

Berdasarkan output tersebut, dari 12 kabupaten/kota di Provinsi Jawa tengah dibentuk 2 cluster dengan jumlah anggota terbanyak terdapat pada cluster 1 yaitu 9 kabupaten/kota. Kemudian cluster 2 memiliki anggota sebanyak 3 kabupaten/kota. Berikut adalah tabel yang memuat informasi mengenai keanggotaan dari masing-masing cluster.

hasil_cluster <- harga_pangan %>%
  mutate(Cluster = factor(km_res$cluster))

colnames(hasil_cluster) <- gsub("_", " ", colnames(hasil_cluster))

kable(
  hasil_cluster,
  caption = "**Tabel 7.** Keanggotaan *Cluster* untuk 12 Kabupaten/Kota",
  row.names = FALSE
)
Tabel 7. Keanggotaan Cluster untuk 12 Kabupaten/Kota
Daerah Beras Daging Ayam Bawang Merah Cabai Merah Cabai Rawit Gula Pasir Cluster
Kab. Banyumas 14750 40250 53750 53750 63250 17450 2
Kab. Boyolali 14850 36250 40000 38150 34150 18200 1
Kab. Cilacap 15950 39000 35250 34400 44400 18100 1
Kab. Karanganyar 15250 37750 35000 32750 44400 17650 1
Kab. Klaten 14250 35500 42250 39000 34400 18300 1
Kab. Kudus 14200 40000 47500 56250 66250 17350 2
Kota Semarang 16100 37000 36250 34650 47000 17950 1
Kab. Sragen 14100 36750 36750 29400 40400 17950 1
Kab. Sukoharjo 16200 37500 37250 36750 38750 17800 1
Kota Surakarta 15850 36750 38250 26900 40000 18350 1
Kota Tegal 14250 40750 41250 42500 56900 17650 2
Kab. Wonogiri 14550 36500 38750 37500 38150 17350 1

4.3 Jarak Anggota Cluster ke Centroid dan Jarak Antar Cluster

Jarak anggota ke centroid digunakan untuk melihat seberapa “mewakili” suatu wilayah terhadap karakteristik rata-rata cluster-nya. Jarak yang lebih kecil berarti wilayah lebih dekat dengan centroid (lebih representatif). Selain itu, jarak antar centroid menunjukkan tingkat perbedaan karakteristik rata-rata antar cluster.

centroids <- km_res$centers
distance_to_centroid <- numeric(nrow(data_scaled))

for (i in seq_len(nrow(data_scaled))) {
  cl_id <- km_res$cluster[i]
  distance_to_centroid[i] <- sqrt(sum((data_scaled[i, ] - centroids[cl_id, ])^2))
}

tabel_jarak <- data.frame(
  Daerah = rownames(data_scaled),
  Cluster = km_res$cluster,
  Jarak_ke_Centroid = round(distance_to_centroid, 4)
) %>%
  arrange(Cluster, Jarak_ke_Centroid)

colnames(tabel_jarak) <- gsub("_", " ", colnames(tabel_jarak))

kable(
  tabel_jarak,
  caption = "**Tabel 8.** Jarak Kabupaten/Kota terhadap *Centroid Cluster*",
  row.names = FALSE,
  align = c("l","c","c")
)
Tabel 8. Jarak Kabupaten/Kota terhadap Centroid Cluster
Daerah Cluster Jarak ke Centroid
Kab. Karanganyar 1 1.1754
Kab. Boyolali 1 1.2325
Kota Semarang 1 1.2733
Kab. Sukoharjo 1 1.3467
Kab. Sragen 1 1.5301
Kota Surakarta 1 1.5891
Kab. Cilacap 1 1.6172
Kab. Wonogiri 1 1.9814
Kab. Klaten 1 2.0855
Kab. Kudus 2 0.8730
Kab. Banyumas 2 1.2643
Kota Tegal 2 1.6498
jarak_matrix <- as.matrix(dist(centroids))
colnames(jarak_matrix) <- paste("Cluster", 1:2)
rownames(jarak_matrix) <- paste("Cluster", 1:2)
kable(jarak_matrix, caption = "**Tabel 9.** Jarak Antar *Centroid Cluster*")
Tabel 9. Jarak Antar Centroid Cluster
Cluster 1 Cluster 2
Cluster 1 0.00000 4.15399
Cluster 2 4.15399 0.00000

4.4 Visualisasi Hasil Clustering

Visualisasi berikut merupakan proyeksi dua dimensi untuk memudahkan interpretasi pemisahan cluster. Proses clustering tetap dilakukan menggunakan seluruh variabel harga yang dianalisis.

fviz_cluster(
  km_res,
  data = data_scaled,
  geom = "point",
  ellipse.type = "convex",
  show.clust.cent = FALSE
) +
  labs(title = "Cluster Plot (K-Means)")
Gambar 2. Visualisasi Hasil K-Means Clustering (k = 2)

Gambar 2. Visualisasi Hasil K-Means Clustering (k = 2)

4.5 Profiling Cluster

Profiling dilakukan untuk menginterpretasikan karakteristik tiap cluster berdasarkan rata-rata harga komoditas.

profil_cluster <- hasil_cluster %>%
  group_by(Cluster) %>%
  summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE)), .groups = "drop")

kable(profil_cluster, caption = "**Tabel 10.** Rata-rata Harga per Komoditas pada Setiap Cluster (Rp/kg)")
Tabel 10. Rata-rata Harga per Komoditas pada Setiap Cluster (Rp/kg)
Cluster Beras Daging Ayam Bawang Merah Cabai Merah Cabai Rawit Gula Pasir
1 15233.33 37000.00 37750 34388.89 40183.33 17961.11
2 14400.00 40333.33 47500 50833.33 62133.33 17483.33

Berikut adalah visualisasi dalam bentuk histogram dan heatmap.

ggplot(profil_long, aes(x = Komoditas, y = Rata_rata, fill = Cluster)) +
  geom_col(position = position_dodge(width = 0.8)) +
  geom_text(
    aes(label = round(Rata_rata, 0)),
    position = position_dodge(width = 0.8),
    vjust = -0.3,
    size = 3
  ) +
  labs(
    x = "Komoditas",
    y = "Rata-rata Harga (Rp/kg)"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
Gambar 3. Perbandingan Rata-rata Harga per Komoditas pada Setiap Cluster

Gambar 3. Perbandingan Rata-rata Harga per Komoditas pada Setiap Cluster

data_long <- hasil_cluster %>%
  pivot_longer(
    cols = -c(Daerah, Cluster),
    names_to = "Komoditas",
    values_to = "Harga"
  )
ggplot(data_long, aes(x = Komoditas, y = Daerah, fill = Harga)) +
  geom_tile(color = "white") +
  facet_wrap(~Cluster, scales = "free_y") +
  labs(
    x = "Komoditas",
    y = "Kabupaten/Kota",
    fill = "Harga (Rp/kg)"
  ) +
  scale_x_discrete(labels = function(x) gsub("_", " ", x)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
Gambar 4. Heatmap Harga Pangan per Kabupaten/Kota Berdasarkan Cluster

Gambar 4. Heatmap Harga Pangan per Kabupaten/Kota Berdasarkan Cluster

Berdasarkan tabel rata-rata variabel tiap cluster, grafik histogram, dan heatmap, terlihat bahwa perbedaan utama antara kedua cluster terletak pada komoditas hortikultura, khususnya cabai merah, cabai rawit, dan bawang merah. Selisih terbesar terdapat pada cabai rawit (Rp21.950), diikuti cabai merah (Rp16.444) dan bawang merah (Rp9.750).

Sebaliknya, perbedaan pada komoditas pokok seperti beras (Rp833) dan gula pasir (Rp478) relatif kecil. Hal ini menunjukkan bahwa pembentukan cluster lebih dipengaruhi oleh komoditas yang bersifat volatile dibandingkan komoditas pokok yang relatif stabil.

Pola ini juga tercermin pada heatmap, di mana intensitas warna untuk komoditas hortikultura tampak lebih kontras antar cluster dibandingkan komoditas beras dan gula pasir.

Secara umum, cluster 1 dapat dikategorikan sebagai wilayah dengan tekanan harga yang relatif lebih rendah pada komoditas volatile food. Meskipun demikian, harga beras dan gula pasir pada cluster ini sedikit lebih tinggi dibandingkan cluster 2, sehingga tetap berpotensi memberikan kontribusi terhadap pembentukan inflasi mengingat bobot kedua komoditas tersebut dalam struktur pengeluaran rumah tangga.Dengan jumlah anggota yang lebih sedikit, cluster 2 dapat dipandang sebagai kelompok wilayah dengan karakteristik harga yang berbeda signifikan dari mayoritas daerah lainnya, sehingga dapat menjadi prioritas dalam strategi stabilisasi pasokan hortikultura.

D. Penutup

1. Kesimpulan

Berdasarkan analisis K-Means Clustering, 12 kabupaten/kota di Provinsi Jawa Tengah dapat dikelompokkan ke dalam dua cluster berdasarkan karakteristik harga pangan. Perbedaan utama antar cluster cenderung dipengaruhi oleh komoditas yang bersifat lebih volatile, sehingga segmentasi wilayah dapat membantu melihat potensi tekanan harga pangan yang berbeda antar kelompok wilayah.

2. Saran

Penelitian selanjutnya disarankan menggunakan cakupan wilayah yang lebih luas serta menambahkan variabel lain yang berkaitan dengan sisi pasokan dan distribusi (misalnya produksi, akses logistik, atau jarak distribusi). Selain itu, penggunaan metode clustering lain sebagai pembanding juga dapat dipertimbangkan untuk menguji konsistensi hasil pengelompokan.