Segmentasi Kabupaten/Kota Berdasarkan Dinamika Harga Bulanan Melalui Price Momentum Index dan Algoritma K-Means

Impor Data

df <- read.csv("~/Downloads/0_df_long.csv",sep = ";",header = TRUE)
head(df)
str(df)
## 'data.frame':    340620 obs. of  9 variables:
##  $ KabKot  : chr  "Kab. Aceh Barat" "Kab. Aceh Barat" "Kab. Aceh Barat" "Kab. Aceh Barat" ...
##  $ Tahun   : int  2022 2022 2022 2022 2022 2022 2022 2022 2022 2022 ...
##  $ Bulan   : chr  "Januari" "Januari" "Januari" "Januari" ...
##  $ Produk  : chr  "Beras Premium" "Beras Medium" "Bawang Merah" "Bawang Putih (Bonggol)" ...
##  $ Harga   : num  11429 9979 31000 28636 19409 ...
##  $ Kategori: chr  "Beras" "Beras" "Bawang" "Bawang" ...
##  $ KodeBPS : num  11 11 11 11 11 ...
##  $ KodeProv: int  11 11 11 11 11 11 11 11 11 11 ...
##  $ NamaProv: chr  "Aceh" "Aceh" "Aceh" "Aceh" ...

Library

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(tidyr)
library(ggplot2)
library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union

Imputasi Harga dan Penanganan Missing Value

Pada dataset harga komoditas pangan, terdapat sejumlah nilai missing pada variabel Harga. Supaya analisis tidak bias dan model klaster dapat berjalan, dilakukan proses imputasi menggunakan pendekatan rata-rata wilayah terdekat, dengan dua tahap:

1. Imputasi tingkat provinsi (wilayah terdekat) Setiap kombinasi:

  • Tahun

  • Bulan

  • Produk

  • Provinsi

    dihitung rata-rata harganya.

    Jika dalam satu kombinasi tersebut terdapat kabupaten/kota dengan nilai harga yang hilang, nilai tersebut diganti dengan rata-rata harga komoditas yang sama pada provinsi tersebut di bulan yang sama.

2. Imputasi tingkat nasional

Jika pada kombinasi tertentu seluruh kabupaten/kota di provinsi itu hilang (rare), maka digunakan: rata-rata nasional untuk Tahun x Bulan x Produk yang sama.

df_imp <- df %>%
  # 1) rata-rata per provinsi
  group_by(Tahun, Bulan, Produk, NamaProv) %>%
  mutate(
    Harga_imp = ifelse(
      is.na(Harga),
      mean(Harga, na.rm = TRUE),
      Harga
    )
  ) %>%
  ungroup() %>%
  # 2) kalau masih NA, pakai rata-rata nasional
  group_by(Tahun, Bulan, Produk) %>%
  mutate(
    Harga_imp = ifelse(
      is.na(Harga_imp),
      mean(Harga_imp, na.rm = TRUE),
      Harga_imp
    )
  ) %>%
  ungroup()
sum(is.na(df$Harga))       # berapa NA awal
## [1] 61513
sum(is.na(df_imp$Harga))  
## [1] 61513
sum(is.na(df_imp$Harga_imp))  # ini idealnya 0 atau minimal banget
## [1] 39011
head(df_imp)

Hitung Price Momentum Bulanan

Momentum bulanan = perubahan harga dari bulan sebelumnya.

Secara matematis:

Momentumt = Hargat - Hargat-1

Hasilnya menunjukkan kenaikan atau penurunan dari bulan sebelumnya. Dengan begitu, wilayah yang cenderung naik cepat, stabil, atau turun bakal ke-detect dan bisa di-cluster lebih masuk akal.

Kolom Bulanan

bulan_map <- c(
  "Januari" = 1,
  "Februari" = 2,
  "Maret" = 3,
  "April" = 4,
  "Mei" = 5,
  "Juni" = 6,
  "Juli" = 7,
  "Agustus" = 8,
  "September" = 9,
  "Oktober" = 10,
  "November" = 11,
  "Desember" = 12
)

df_imp2 <- df_imp %>%
  mutate(
    Bulan_num = bulan_map[Bulan],
    Tanggal = as.Date(paste(Tahun, Bulan_num, "01", sep = "-"))
  )

head(df_imp2)

Hitung Momentum tiap Kabupaten/Kota x Produk

df_momentum <- df_imp2 %>%
  group_by(KabKot, Produk) %>%
  arrange(Tanggal) %>%                # urutkan berdasarkan waktu
  mutate(
    momentum = Harga_imp - lag(Harga_imp)
  ) %>%
  ungroup()
head(df_momentum[, c("KabKot", "Produk", "Tanggal", "Harga_imp", "momentum")])

Nilai momentum yang positif menunjukkan kenaikan harga, negatif menunjukkan penurunan, dan nilai yang mendekati nol menunjukkan harga relatif stabil. Dengan memakai momentum, klaster yang terbentuk merefleksikan dinamika perubahan harga, bukan hanya tingkat harga absolut di tiap wilayah.

Buat Fitur Momentum pada Kabupaten/Kota

Buat Data siap di Cluster:

1). Setiap Kab/Kot jadi 1 baris

2). Setiap Produk (15 Komoditas) jadi 1 kolom

3). Nilai = Rata-rata momentum bulanan komoditas itu di wilayah tersebut

df_features <- df_momentum %>% group_by(KabKot, Produk) %>% summarise( momentum_avg = mean(momentum, na.rm = TRUE), .groups = “drop” )

df_features <- df_momentum %>%
  group_by(KabKot, Produk) %>%
  summarise(
    momentum_avg = mean(momentum, na.rm = TRUE),
    .groups = "drop"
  )
df_features_wide <- df_features %>%
  tidyr::pivot_wider(
    names_from = Produk,
    values_from = momentum_avg
  )
sum(is.na(df_features_wide))
## [1] 33
df_features_wide[is.na(df_features_wide)] <- 0
head(df_features_wide)

Untuk setiap kabupaten/kota dan setiap komoditas, dihitung rata-rata momentum bulanan selama keseluruhan periode. Nilai ini merepresentasikan kecenderungan perubahan harga komoditas tersebut di wilayah tersebut.

Hasilnya berupa matriks berukuran:

(jumlah kabupaten/kota) × (15 komoditas pangan)

Baris = Kabupaten/Kota

Kolom = Rata-rata momentum 15 komoditas

Matriks inilah yang menjadi input untuk algoritma K-Means, karena bentuknya sudah numerik, berskala sama (nanti di-scale), dan merepresentasikan dinamika harga tiap wilayah.

Standarisasi + K-Means

Pisah Nama Kabupaten/Kota & Fitur Numerik

Karena Nama Kabupaten/Kota itu nama Identitas dan Fitur Numerik yang dipakai Momentum Komoditas.

kabkot_names <- df_features_wide$KabKot

X <- df_features_wide %>% 
  select(-KabKot)

Standarisasi

Tujuan diberikannya Standarisasi di K-Means adalah agar komoditas yang angka atau nilainya besar ngga menguasai hasil dari cluster dan bikin berantakan. Makanya diberikan Standarisasi agar skalanya tetap sama.

X_scaled <- scale(X)

Cari Jumlah Cluster

Untuk cari Jumlah Cluster yang optimal digunakan Elbow Method dengan mengamati nilai within Sum of Squares (WSS) untuk berbagai nilai k.

set.seed(123)
wss <- sapply(1:10, function(k){
  kmeans(X_scaled, centers = k, nstart = 25)$tot.withinss
})

plot(1:10, wss, type = "b",
     xlab = "Jumlah Cluster (3)",
     ylab = "Within Sum of Squares",
     main = "Elbow Plot K-Means")

Pada grafik:

  • Dari k = 1 → 2 → 3, terjadi penurunan WSS yang besar, menunjukkan bahwa menambah cluster sampai 3 meningkatkan kualitas pengelompokan secara signifikan.

  • Mulai dari k = 3 → 10, penurunan WSS menjadi lebih landai, artinya tambahan cluster setelah k=3 hanya memberikan peningkatan kecil dan tidak efisien.

    Fenomena ini disebut elbow point, yaitu titik di mana manfaat menambah cluster mulai berkurang drastis. Pada grafik penelitian ini, elbow point terlihat jelas pada k = 3, sehingga dipilih sebagai jumlah cluster optimal.

Tujuan memilih 3 cluster bukan hanya berdasarkan bentuk grafik, tapi juga pertimbangan interpretasi ekonomi. Dengan 3 cluster, dinamika perubahan harga dapat tersegmentasi menjadi kelompok naik tinggi, stabil, dan fluktuatif.

K-Means

Pasang set.seed agar hasil cluster tidak berubah-ubah dan mengunci hasil k-means.

dengan telah ditentukan 3 cluster maka, K-means:

  • Mencari 3 kelompok kabupaten/kota yang punya pola momentum harga yang mirip.

  • Yang naiknya mirip → 1 cluster

  • Yang stabil mirip → 1 cluster

  • Yang fluktuatif mirip → 1 cluster

set.seed(123)
k <- 3

km <- kmeans(X_scaled, centers = k, nstart = 25)

df_cluster <- df_features_wide
df_cluster$cluster <- factor(km$cluster)

Hasil Cluster

head(df_cluster)
table(df_cluster$cluster)
## 
##   1   2   3 
##   4 372 130
cluster_summary <- df_cluster %>%
  group_by(cluster) %>%
  summarise(across(where(is.numeric), mean, na.rm = TRUE))
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(where(is.numeric), mean, na.rm = TRUE)`.
## ℹ In group 1: `cluster = 1`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
## 
##   # Previously
##   across(a:b, mean, na.rm = TRUE)
## 
##   # Now
##   across(a:b, \(x) mean(x, na.rm = TRUE))
cluster_summary

Analisis klaster menghasilkan tiga kelompok kabupaten/kota berdasarkan Price Momentum Index. Cluster 1 menunjukkan wilayah dengan momentum harga sangat tinggi, yang mencerminkan kenaikan harga bulanan yang agresif dan potensi volatilitas harga. Cluster 2 berisi wilayah dengan momentum menengah atau moderat, sedangkan Cluster 3 merupakan kelompok dengan momentum terendah, menunjukkan stabilitas harga yang relatif baik.

Kabupaten/kota terklaster secara otomatis, sehingga daerah yang memiliki pola perubahan harga serupa terkumpul dalam cluster yang sama.

Cek tiap Cluster Kabupaten/Kota

Momentum Tinggi = Cluster 1

df_cluster %>% filter(cluster == 1)

Momentum Menengah = Cluster 2

df_cluster %>% filter(cluster == 2)

Momentum Rendah/Stabil = Cluster 3

df_cluster %>% filter(cluster == 3)

Visualisasi

library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
fviz_cluster(km, data = X_scaled,
             geom = "point",
             main = "Visualisasi Cluster K-Means Berdasarkan Momentum Harga")

Visualisasi K-Means menunjukkan tiga kelompok kabupaten/kota yang terpisah jelas berdasarkan Price Momentum Index. Cluster 1 (merah) berada jauh di sisi kanan dan menggambarkan wilayah dengan momentum harga paling tinggi atau kenaikan harga bulanan yang agresif. Cluster 2 (hijau) terkonsentrasi di sekitar pusat grafik, mencerminkan wilayah dengan perubahan harga moderat dan stabil. Sementara itu, Cluster 3 (biru) terletak di sisi kiri bawah, menunjukkan wilayah dengan momentum rendah atau fluktuasi harga kecil. Pemisahan ketiga cluster yang jelas dalam ruang PCA menandakan bahwa perbedaan pola perubahan harga antar wilayah memang signifikan dan dapat ditangkap dengan baik oleh algoritma K-Means.

Kesimpulan

Penelitian ini berhasil mengelompokkan kabupaten/kota berdasarkan dinamika perubahan harga bulanan komoditas pangan menggunakan Price Momentum Index dan metode K-Means. Hasil analisis menunjukkan bahwa jumlah cluster optimal adalah tiga, sesuai dengan titik elbow pada grafik WSS. Ketiga cluster memiliki karakteristik yang berbeda, yaitu: cluster dengan momentum harga tinggi yang menunjukkan kecenderungan kenaikan harga bulanan yang agresif; cluster momentum menengah dengan stabilitas perubahan harga yang lebih moderat; serta cluster momentum rendah yang cenderung memiliki perubahan harga kecil atau fluktuasi minimal. Visualisasi PCA mengonfirmasi bahwa ketiga cluster terpisah dengan baik, mengindikasikan struktur pola yang kuat dalam data. Secara keseluruhan, hasil klasterisasi ini memberikan gambaran yang jelas mengenai wilayah mana yang rawan kenaikan harga, wilayah yang stabil, serta wilayah yang relatif aman sehingga dapat menjadi dasar untuk analisis lanjutan maupun perumusan kebijakan stabilisasi harga pangan.