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(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
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:
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.
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)
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.
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)
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 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.
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)
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)
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.
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)
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)
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.