1 Pendahuluan

Latar Belakang Masalah Bawang merah merupakan komoditas hortikultura strategis di Indonesia yang memiliki volatilitas harga tinggi akibat pola produksi yang bersifat musiman. Tantangan utama dalam menganalisis harga nasional adalah adanya disparitas (kesenjangan) harga yang ekstrem antarwilayah; harga di wilayah sentra produksi (seperti Jawa) jauh lebih rendah dibandingkan wilayah timur Indonesia (seperti Papua).

Analisis pengelompokan (clustering) konvensional yang menggunakan jarak Euclidean sering kali gagal menangkap kesamaan pola pada kondisi ini. Metode tersebut cenderung sensitif terhadap perbedaan skala (nominal harga) dan pencilan (outlier), sehingga provinsi dengan pola fluktuasi yang sama namun berbeda tingkat harga sering kali dianggap tidak mirip.

Solusi Penyelesaian Untuk mengatasi masalah tersebut, penelitian ini menerapkan metode Time Series Clustering dengan menggunakan ukuran jarak Mahalanobis Distance-Based Dynamic Time Warping (MDDTW).

Metode MDDTW dipilih karena menggabungkan keunggulan Dynamic Time Warping (DTW) dalam menangkap pergeseran pola waktu musiman, dengan ketangguhan jarak Mahalanobis yang mampu menstandarisasi perbedaan skala harga dan memperhitungkan korelasi antar data. Dengan pendekatan ini, pengelompokan 34 provinsi dilakukan berdasarkan kemiripan perilaku pola pergerakan harga, bukan sekadar nominal harganya, sehingga menghasilkan zonasi harga yang lebih akurat dan representatif untuk pengambilan kebijakan.

2 Import Data

Import data dari file CSV ke R

data_tpg <- read.csv("C:/Users/nyayu/Statistika/Semester 5/TPG (Teknik Peubah Ganda)/data tugas.csv", header = TRUE, sep = ";")

2.1 Mengindentifikasi data awal

Melihat struktur data awal.

str(data_tpg)
## '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.1 11.1 11.1 11.1 11.1 ...
##  $ KodeProv: int  11 11 11 11 11 11 11 11 11 11 ...
##  $ NamaProv: chr  "Aceh" "Aceh" "Aceh" "Aceh" ...
dim(data_tpg)
## [1] 340620      9

Data awal memiliki 9 kolom dan 340620 baris, 9 kolom tersebut adalah KabKot, Tahun, Bulan, Produk, Harga, Kategori, KodeBPS, KodeProv, dan NamaProv.

Selanjutnya, kita akan memeriksa keberadaan nilai NA (kosong) pada data.

2.2 Pemeriksaan NA pada data awal

anyNA(data_tpg)
## [1] TRUE
colSums(is.na(data_tpg))
##   KabKot    Tahun    Bulan   Produk    Harga Kategori  KodeBPS KodeProv 
##        0        0        0        0    61513        0        0        0 
## NamaProv 
##     3450
sum(is.na(data_tpg))
## [1] 64963

Data awal memiliki 2 kolom yang mengandung NA di antaranya yaitu kolom Harga dan kolom NamaProv. Dengan kolom Harga memiliki 61513 baris dan NamaProv 3450 baris.

3 Pengisian NA

3.1 Mengisi NA pada kolom NamaProv

library(dplyr)
data_tpg %>%
  filter(is.na(NamaProv)) %>%
  distinct(KodeProv)
data_tpg %>% 
  filter(KodeProv == 65) %>% 
  distinct(KabKot)

Nilai yang hilang pada kolom Nama Provinsi hanya terjadi untuk Kode Provinsi 65. Setelah dicek berdasarkan nama kabupaten/kota, wilayah tersebut berada di Kalimantan Utara. Karena hanya ada satu provinsi dengan kode tersebut dan datanya yang hilang, maka Nama Provinsi yang benar untuk kode 65 adalah Kalimantan Utara.

data_tpg <- data_tpg %>%
  mutate(NamaProv = ifelse(KodeProv == 65 & is.na(NamaProv), "Kalimantan Utara", NamaProv))

sum(is.na(data_tpg$NamaProv))
## [1] 0

3.2 Mengisi NA pada kolom Harga

Selanjutnya dilakukan filter data untuk hanya menampilkan produk bawang merah.

library(dplyr)

data_bawang_raw <- data_tpg %>%
  filter(Produk == "Bawang Merah")
str(data_bawang_raw)
## 'data.frame':    22708 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" "Februari" "Maret" "April" ...
##  $ Produk  : chr  "Bawang Merah" "Bawang Merah" "Bawang Merah" "Bawang Merah" ...
##  $ Harga   : num  31000 33107 37600 35800 41600 ...
##  $ Kategori: chr  "Bawang" "Bawang" "Bawang" "Bawang" ...
##  $ KodeBPS : num  11.1 11.1 11.1 11.1 11.1 ...
##  $ KodeProv: int  11 11 11 11 11 11 11 11 11 11 ...
##  $ NamaProv: chr  "Aceh" "Aceh" "Aceh" "Aceh" ...
dim(data_bawang_raw)
## [1] 22708     9
summary(data_bawang_raw)
##     KabKot              Tahun         Bulan              Produk         
##  Length:22708       Min.   :2022   Length:22708       Length:22708      
##  Class :character   1st Qu.:2022   Class :character   Class :character  
##  Mode  :character   Median :2023   Mode  :character   Mode  :character  
##                     Mean   :2023                                        
##                     3rd Qu.:2024                                        
##                     Max.   :2025                                        
##                                                                         
##      Harga          Kategori            KodeBPS         KodeProv   
##  Min.   : 10000   Length:22708       Min.   :11.01   Min.   :11.0  
##  1st Qu.: 30370   Class :character   1st Qu.:17.09   1st Qu.:17.0  
##  Median : 36208   Mode  :character   Median :35.21   Median :35.0  
##  Mean   : 37651                      Mean   :43.81   Mean   :43.6  
##  3rd Qu.: 43470                      3rd Qu.:71.01   3rd Qu.:71.0  
##  Max.   :113148                      Max.   :92.71   Max.   :92.0  
##  NA's   :40                                                        
##    NamaProv        
##  Length:22708      
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 

Data bawang merah memiliki 22708 baris dan 9 kolom, dengan kolom Harga terdapat NA. Selanjutnya, kita akan mengurutkan data berdasarkan kota dan tanggal agar lebih mudah dianalisis.

data_bawang_sorted <- data_bawang_raw %>%
  mutate(
    BulanAngka = match(Bulan, c("Januari", "Februari", "Maret", "April", "Mei", "Juni", 
                                "Juli", "Agustus", "September", "Oktober", "November", "Desember")),
    Tanggal = as.Date(paste0(Tahun, "-", BulanAngka, "-01"))
  ) %>%
  arrange(KabKot, Tanggal)

Dilakukan pengecekan NA pada data bawang merah

cek_na_bawang <- data_tpg %>%
  filter(Produk == "Bawang Merah", is.na(Harga)) %>%
  select(NamaProv, KabKot, Tahun, Bulan) %>%          
  arrange(NamaProv, KabKot, Tahun)                  

print(cek_na_bawang)
##            NamaProv             KabKot Tahun     Bulan
## 1             Papua Kab. Teluk Bintuni  2025   Januari
## 2             Papua Kab. Teluk Bintuni  2025       Mei
## 3             Papua Kab. Teluk Bintuni  2025      Juni
## 4       Papua Barat       Kab. Dogiyai  2025     Maret
## 5       Papua Barat    Kab. Intan Jaya  2024   Oktober
## 6       Papua Barat    Kab. Intan Jaya  2024  November
## 7       Papua Barat    Kab. Intan Jaya  2024  Desember
## 8       Papua Barat    Kab. Intan Jaya  2025   Januari
## 9       Papua Barat    Kab. Intan Jaya  2025  Februari
## 10      Papua Barat    Kab. Intan Jaya  2025     Maret
## 11      Papua Barat    Kab. Intan Jaya  2025     April
## 12      Papua Barat    Kab. Intan Jaya  2025       Mei
## 13      Papua Barat    Kab. Intan Jaya  2025      Juni
## 14      Papua Barat    Kab. Intan Jaya  2025      Juli
## 15      Papua Barat    Kab. Jayawijaya  2022   Januari
## 16      Papua Barat    Kab. Jayawijaya  2022  Februari
## 17      Papua Barat    Kab. Jayawijaya  2022     Maret
## 18      Papua Barat    Kab. Jayawijaya  2022     April
## 19      Papua Barat    Kab. Jayawijaya  2022       Mei
## 20      Papua Barat    Kab. Jayawijaya  2025     Maret
## 21      Papua Barat         Kab. Mappi  2022     April
## 22      Papua Barat         Kab. Mappi  2022       Mei
## 23      Papua Barat   Kab. Puncak Jaya  2022   Januari
## 24      Papua Barat   Kab. Puncak Jaya  2024 September
## 25      Papua Barat   Kab. Puncak Jaya  2024  Desember
## 26      Papua Barat   Kab. Puncak Jaya  2025     April
## 27      Papua Barat      Kab. Yahukimo  2024     Maret
## 28      Papua Barat      Kab. Yahukimo  2024     April
## 29 Sulawesi Selatan      Kab. Bantaeng  2023     Maret
## 30 Sulawesi Selatan      Kab. Bantaeng  2023     April
## 31 Sulawesi Selatan      Kab. Bantaeng  2023       Mei
## 32 Sulawesi Selatan      Kab. Bantaeng  2023      Juni
## 33 Sulawesi Selatan      Kab. Bantaeng  2023      Juli
## 34 Sulawesi Selatan      Kab. Bantaeng  2023   Agustus
## 35 Sulawesi Selatan      Kab. Bantaeng  2023 September
## 36 Sulawesi Selatan      Kab. Bantaeng  2023   Oktober
## 37 Sulawesi Selatan      Kab. Bantaeng  2023  November
## 38 Sulawesi Selatan      Kab. Bantaeng  2023  Desember
## 39 Sulawesi Selatan      Kab. Bantaeng  2024   Januari
## 40 Sulawesi Selatan      Kab. Bantaeng  2024  Februari
cek_na_bawang %>% count(NamaProv, KabKot)
library(dplyr)

kekuatan_data <- data_bawang_raw %>%
  group_by(NamaProv) %>%
  summarise(
    Jumlah_Kota = n_distinct(KabKot), 
    Kota_Bermasalah = sum(unique(KabKot) %in% c("Kab. Teluk Bintuni", "Kab. Dogiyai", "Kab. Intan Jaya", "Kab. Jayawijaya", "Kab. Mappi","Kab. Puncak Jaya","Kab. Yahukimo","Kab. Bantaeng" )),
    .groups = "drop"
  ) %>%
  arrange(Jumlah_Kota) 

print("--- Jumlah Kota Penyusun Harga Provinsi ---")
## [1] "--- Jumlah Kota Penyusun Harga Provinsi ---"
print(kekuatan_data)
## # A tibble: 34 × 3
##    NamaProv                  Jumlah_Kota Kota_Bermasalah
##    <chr>                           <int>           <int>
##  1 DI Yogyakarta                       5               0
##  2 Kalimantan Utara                    5               0
##  3 DKI Jakarta                         6               0
##  4 Gorontalo                           6               0
##  5 Kepulauan Bangka Belitung           7               0
##  6 Kepulauan Riau                      7               0
##  7 Sulawesi Barat                      7               0
##  8 Banten                              8               0
##  9 Bali                                9               0
## 10 Bengkulu                           10               0
## # ℹ 24 more rows
filter_provinsi_masalah <- kekuatan_data %>%
  filter(NamaProv %in% c("Sulawesi Selatan", "Papua", "Papua Barat"))

print("--- Detail Provinsi dengan Missing Value ---")
## [1] "--- Detail Provinsi dengan Missing Value ---"
print(filter_provinsi_masalah)
## # A tibble: 3 × 3
##   NamaProv         Jumlah_Kota Kota_Bermasalah
##   <chr>                  <int>           <int>
## 1 Papua                     12               1
## 2 Papua Barat               21               6
## 3 Sulawesi Selatan          24               1
data_bawang_prov <- data_bawang_sorted %>%
  group_by(NamaProv, Tanggal) %>%
  summarise(
    Harga_Mean = mean(Harga, na.rm = TRUE),
    .groups = "drop"
  )

Dengan menggunakan group_by(NamaProv) dan mean(…, na.rm = TRUE), perhitungan rata-rata harga dilakukan secara terpisah untuk setiap provinsi hanya menggunakan data kota/kabupaten yang tersedia di provinsi tersebut. Jadi, jika di Provinsi Papua ada 5 kota tetapi 1 kota datanya kosong, maka rata-rata harga Provinsi Papua pada tanggal itu dihitung murni dari rata-rata 4 kota di Papua yang datanya ada, tanpa terpengaruh oleh data dari provinsi lain atau kota yang kosong tersebut.

3.3 Validasi pola time series setelah pengisian NA

library(dplyr)
library(tidyr)
library(ggplot2)

data_cek <- data_bawang_prov %>%
  filter(NamaProv %in% c("Sulawesi Selatan", "Jawa Timur"))

ggplot(data_cek, aes(x = Tanggal, y = Harga_Mean, color = NamaProv)) +
  geom_line(linewidth = 1) +
  geom_point() +
  scale_x_date(date_breaks = "3 months", date_labels = "%b %Y") +
  theme_minimal() +
  labs(
    title = "Uji Validitas Pola Time Series",
    subtitle = "Perhatikan garis Sulawesi Selatan: Apakah mulus atau terputus?",
    y = "Harga Rata-rata (Rp)",
    x = "Waktu"
  ) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Pada pola tersebut dibandingkan hasil time series antara Sulawesi Selatan (yang memiliki data NA sebelumnya) dengan Jawa Timur (yang tidak memiliki data NA). Terlihat bahwa pola garis Sulawesi Selatan tetap mulus tanpa ada putus, menandakan bahwa pengisian NA dengan rata-rata provinsi berhasil mempertahankan pola pergerakan harga yang valid.

3.4 Cek kembali NA final

cat("Jumlah NA Final di Level Provinsi:", sum(is.na(data_bawang_prov$Harga_Mean)), "\n")
## Jumlah NA Final di Level Provinsi: 0

3.5 data final

datatable(data_bawang_prov, 
          class = 'cell-border stripe', 
          caption = 'Tabel 1: Contoh Hasil Clustering',
          options = list(pageLength = 5, autoWidth = TRUE))

4 Eksplorasi data

library(ggplot2)
library(dplyr)

ggplot(data_bawang_prov, aes(x = Tanggal, y = Harga_Mean, group = NamaProv, color = NamaProv)) +
  geom_line(alpha = 0.7, linewidth = 0.8) + 
  scale_x_date(date_breaks = "4 months", date_labels = "%b %Y") + # Label per 4 bulan
  theme_minimal() +
  labs(
    title = "Tren Harga Bawang Merah Seluruh Provinsi",
    subtitle = "Pola Pergerakan Harga Bulanan (2022-2025)",
    y = "Harga Rata-rata (Rp)",
    x = "Periode Waktu",
    color = "Provinsi"
  ) +
  theme(
    legend.position = "right", 
    legend.key.size = unit(0.4, "cm"), 
    legend.text = element_text(size = 7), 
    axis.text.x = element_text(angle = 45, hjust = 1) 
  )

Meskipun secara umum harga bawang merah mengalami fluktuasi musiman di seluruh Indonesia, visualisasi data memperlihatkan adanya dualitas pola yang mencolok. Mayoritas provinsi menunjukkan pergerakan harga yang relatif lebih terkendali dan seragam di kisaran harga rendah, membentuk pola gerombolan yang rapat di bagian bawah grafik. Kondisi ini sangat kontras dengan segelintir provinsi outlier (terutama di wilayah timur) yang memisahkan diri dengan tren harga jauh lebih tinggi dan mengalami gejolak volatilitas ekstrem, ditandai dengan lonjakan tajam yang tidak dialami oleh provinsi lain. Disparitas perilaku harga ini—antara kelompok yang pola fluktuasinya moderat dan kelompok yang sangat fluktuatif—menegaskan bahwa pendekatan rata-rata nasional akan bias, sehingga mutlak diperlukan metode clustering untuk memisahkan dan menangani karakteristik yang bertolak belakang tersebut.

ggplot(data_bawang_prov, aes(x = reorder(NamaProv, Harga_Mean), y = Harga_Mean, fill = NamaProv)) +
  geom_boxplot(outlier.colour = "red", outlier.shape = 8, show.legend = FALSE) + 
  coord_flip() + 
  theme_minimal() +
  labs(
    title = "Distribusi Harga Bawang Merah Antar Provinsi",
    subtitle = "Diurutkan dari Median Harga Terendah ke Tertinggi",
    y = "Sebaran Harga (Rp)",
    x = "Provinsi"
  ) +
  theme(
    axis.text.y = element_text(size = 8) 
  )

Visualisasi distribusi harga mempertegas adanya disparitas struktural yang ekstrem antarwilayah, di mana sentra produksi utama (seperti NTB dan Jambi) konsisten menempati posisi terendah dengan variabilitas harga yang relatif stabil, sangat kontras dengan wilayah timur (seperti Papua dan Maluku Utara) yang memiliki median harga tertinggi disertai rentang volatilitas yang lebar akibat ketidakpastian rantai pasok . Meskipun keberadaan pencilan (outlier) yang tersebar di hampir seluruh provinsi mengonfirmasi bahwa kerentanan terhadap guncangan harga musiman adalah fenomena nasional, perbedaan fundamental profil risiko antara wilayah “stabil-murah” dan “fluktuatif-mahal” ini menjadi bukti empiris kuat bahwa analisis berbasis rata-rata nasional akan bias, sehingga memvalidasi urgensi penerapan metode clustering untuk memisahkan strategi penanganan berdasarkan karakteristik unik setiap wilayah.

5 Clustering

5.1 Persiapan data

Data bawang merah bentuknya memanjang ke bawah (satu baris = satu tanggal per provinsi). Algoritma clustering dan perhitungan jarak (seperti MDDTW) membutuhkan input di mana satu baris mewakili satu objek utuh (dalam hal ini, satu deret waktu penuh untuk satu provinsi). Hasilnya: Kolom Tanggal akan “disebar” menjadi nama-nama kolom baru, dan isi tabelnya adalah Harga_Mean.

df_wide <- data_bawang_prov %>%
  pivot_wider(names_from = Tanggal, values_from = Harga_Mean)
df_wide[1:5, 1:5] 

Setelah itu kita mengubah dataframe menjadi matriks numerik.

data_matrix <- as.matrix(df_wide[, -1])
rownames(data_matrix) <- df_wide$NamaProv

dim(data_matrix)
## [1] 34 46

5.2 Perhitungan jarak MDDTW dan Penggerombolan Hirearki

Sesuai konsep, MDDTW menggabungkan jarak Mahalanobis (untuk mengatasi perbedaan skala/varians) dengan DTW (untuk mengatasi pergeseran waktu).

Pada langkah ini, kita menyiapkan parameter “pembobot” untuk perhitungan jarak Mahalanobis. Secara sederhana, kode ini menghitung seberapa “liar” atau menyebar data harga bawang merah di seluruh Indonesia, supaya nanti saat kita membandingkan harga antarprovinsi, perbandingannya menjadi adil (terstandarisasi).

  1. var(as.vector(data_matrix)): Kita mengambil seluruh angka harga dari semua provinsi dan semua bulan, lalu menghitung Varians Global-nya. Ini mengukur seberapa jauh harga-harga tersebut menyebar dari rata-rata nasional.

  2. inv_covariance <- 1 / global_variance: Ini adalah Invers Matriks Kovarian (\(S^{-1}\)) versi sederhana. Karena data kita hanya satu jenis (hanya “Harga”, disebut univariat), matriks kovarian berubah menjadi satu angka saja yaitu Varians.Invers-nya berarti \(1\) dibagi Varians (\(1/s^2\)).

global_variance <- var(as.vector(data_matrix), na.rm = TRUE)
inv_covariance <- 1 / global_variance
global_sd <- sqrt(global_variance)

print(paste("Global SD:", global_sd))
## [1] "Global SD: 10078.4703309907"
print(paste("Global Variance:", global_variance))
## [1] "Global Variance: 101575564.212659"

Global SD artinya secara rata-rata, harga bawang merah di Indonesia menyimpang (naik atau turun) sekitar Rp 10.078 dari harga rata-rata nasional. Ini angka yang sangat masuk akal mengingat harga bawang bisa bergerak dari Rp 20.000 ke Rp 40.000-an.

Dalam rumus MDDTW yang digunakan, jarak Mahalanobis dihitung dengan melibatkan matriks kovarian invers (\(S^{-1}\)). Karena data Anda hanya satu variabel (univariat), matriks kovarian \(S\) itu sama dengan Global Variance ini. Nantinya, algoritma akan menggunakan angka ini sebagai pembagi.

\[ \text{Jarak MDDTW} = \frac{\text{Selisih harga}}{\text{Global SD}} \]

Selanjutnya, fungsi di bawah ini bertugas menghitung jarak kemiripan antara dua deret waktu (misal: Provinsi Aceh vs. Sumatera Utara) dengan mempertimbangkan varians data global. Fungsi calc_mddtw ini adalah modifikasi algoritma DTW standar. Jika DTW biasa langsung menghitung jarak Euclidean mentah, fungsi ini membangun Matriks Biaya Lokal secara manual terlebih dahulu menggunakan rumus jarak Mahalanobis. Dengan cara ini, algoritma tidak hanya ‘mencocokkan bentuk’ grafik harga antarprovinsi, tetapi juga menstandarisasi skala harganya berdasarkan varians global data. Hasilnya adalah ukuran kemiripan yang murni berdasarkan pola fluktuasi, dan tidak bias oleh perbedaan nominal harga yang ekstrem antara wilayah barat dan timur Indonesia.

dengan langkah-langkah:

  1. scaling_factor <- sqrt(inv_cov) : Menghitung faktor pembobot.
  2. local_cost_matrix <- abs(outer(ts1, ts2, “-”)) * scaling_factor : Membuat Matriks Biaya Lokal (Local Cost Matrix). Logika outer: Perintah outer(ts1, ts2, “-”) membuat tabel silang raksasa. Jika ts1 punya 100 bulan dan ts2 punya 100 bulan, ia akan menghitung selisih harga untuk setiap kemungkinan pasangan bulan (100x100 = 10.000 kombinasi). Logika abs(…) * scaling: Selisih tersebut dimutlakkan lalu dikalikan faktor skala.
  3. alignment <- dtw(local_cost_matrix, distance.only = TRUE) : Menjalankan algoritma Dynamic Time Warping (DTW). Biasanya, fungsi dtw() di R menghitung jaraknya sendiri (Euclidean). Namun, di sini Anda memaksa fungsi dtw untuk menggunakan peta jarak yang sudah Anda buat sendiri di langkah sebelumnya (local_cost_matrix yang berbasis Mahalanobis).
calc_mddtw <- function(ts1, ts2, inv_cov) {
  ts1 <- as.numeric(ts1)
  ts2 <- as.numeric(ts2)
  

  if(any(is.na(ts1)) | any(is.na(ts2))) return(NA)

  scaling_factor <- sqrt(inv_cov)
 
  local_cost_matrix <- abs(outer(ts1, ts2, "-")) * scaling_factor

  alignment <- dtw(local_cost_matrix, distance.only = TRUE)
  
  return(alignment$distance)
}

Kode di bawah ini bertujuan untuk membangun Matriks Jarak (Distance Matrix) berukuran \(34 \times 34\). Matriks ini berisi nilai ketidakmiripan (dissimilarity) antara satu provinsi dengan provinsi lainnya. Jika nilainya kecil: Pola harga kedua provinsi sangat mirip. Jika nilainya besar: Pola harga kedua provinsi sangat berbeda.

Pada tahap ini, dilakukan komputasi iteratif untuk menyusun Matriks Jarak Simetris antar 34 provinsi. Algoritma membandingkan pola harga setiap pasangan provinsi (total kombinasi \(_{34}C_2\)) menggunakan fungsi MDDTW yang telah didefinisikan.

Hasil akhirnya adalah sebuah matriks yang merepresentasikan ‘peta kedekatan’ pola harga. Sebagai contoh, jika jarak antara Jawa Tengah dan Jawa Timur kecil, berarti kedua provinsi ini memiliki perilaku harga yang serupa dan berpotensi masuk dalam gerombol yang sama. Sebaliknya, jika jarak antara Jawa Tengah dan Papua besar, berarti pola fluktuasi harganya sangat berbeda.

dengan langkah-langkah:

  1. Kita menyiapkan “tabel kosong” berukuran \(34 \times 34\) (karena ada 34 provinsi). Awalnya diisi angka 0 semua.
  2. Looping (Perulangan) Perhitungan
  3. Eksekusi Fungsi MDDTW, dengan proses:
    • Ambil deret waktu harga provinsi ke-\(i\) (misal: Aceh).

    • Ambil deret waktu harga provinsi ke-\(j\) (misal: Papua).

    • calc_mddtw(...): Masukkan kedua deret waktu tersebut ke dalam fungsi MDDTW yang sudah kita buat sebelumnya. Fungsi ini akan mengembalikan satu angka (skor jarak).

    • Penyimpanan: Masukkan skor jarak tersebut ke dalam matriks di posisi \((i, j)\) dan \((j, i)\) agar matriksnya simetris (cerminan).

  4. Konversi untuk Clustering. Fungsi hclust() (Hierarchical Clustering) di R membutuhkan input berupa objek khusus bernama dist, bukan matriks biasa. Perintah ini mengubah format matriks kotak tadi menjadi format segitiga bawah yang lebih hemat memori dan siap untuk di-cluster.
n_prov <- nrow(data_matrix)
dist_matrix <- matrix(0, nrow = n_prov, ncol = n_prov)
rownames(dist_matrix) <- rownames(data_matrix)
colnames(dist_matrix) <- rownames(data_matrix)

# Looping (Perulangan) Perhitungan
for(i in 1:(n_prov-1)) {
  for(j in (i+1):n_prov) {
    ts_i <- data_matrix[i, ]
    ts_j <- data_matrix[j, ]
    
    d_val <- calc_mddtw(ts_i, ts_j, inv_covariance)
    
    dist_matrix[i, j] <- d_val
    dist_matrix[j, i] <- d_val
  }
}

# Ubah menjadi objek 'dist' untuk clustering
dist_object <- as.dist(dist_matrix)

# Cek hasil (tampilkan 5x5 pertama agar laporan tidak terlalu panjang)
print(as.matrix(dist_object)[1:5, 1:5])
##                   Aceh     Bali   Banten Bengkulu DI Yogyakarta
## Aceh           0.00000 25.79051 16.77682 11.94604      20.78226
## Bali          25.79051  0.00000 26.68291 25.45134      20.11716
## Banten        16.77682 26.68291  0.00000 14.31027      16.63738
## Bengkulu      11.94604 25.45134 14.31027  0.00000      19.93489
## DI Yogyakarta 20.78226 20.11716 16.63738 19.93489       0.00000

Output di atas adalah Matriks Ketidakmiripan (Dissimilarity Matrix) antarprovinsi berdasarkan pola harga bawang merah. Angka-angka tersebut merepresentasikan jarak MDDTW; semakin kecil angkanya, semakin mirip pola pergerakan harganya. Sebaliknya, semakin besar angkanya, semakin berbeda polanya. Diagonal Utama Bernilai 0 Nilai \(0.00000\) pada diagonal (Aceh-Aceh, Bali-Bali, dst.) menunjukkan jarak antara provinsi dengan dirinya sendiri.

Matriks jarak menunjukkan variasi nilai ketidakmiripan yang logis. Provinsi dengan kedekatan geografis seperti Aceh dan Bengkulu memiliki jarak MDDTW yang rendah (11.95), mengindikasikan sinkronisasi pola harga yang kuat. Sebaliknya, provinsi yang terpisah jauh secara logistik seperti Banten dan Bali menunjukkan jarak yang lebih besar (26.68), merefleksikan dinamika pasar yang berbeda.

5.3 Mencari Pautan Terbaik dan menghitung Jumlah Cluster Optimal

Tahap ini merupakan validasi internal untuk menentukan metode pautan terbaik. Berdasarkan nilai Koefisien Korelasi Cophenetic tertinggi.

Tujuan: Kita mendaftarkan 5 algoritma penggerombolan yang akan diuji.

  • Single: Menggabungkan berdasarkan tetangga terdekat.

  • Complete: Menggabungkan berdasarkan tetangga terjauh.

  • Average (Rataan): Menggabungkan berdasarkan rata-rata jarak seluruh anggota.

  • Centroid: Menggabungkan berdasarkan pusat gerombol.

  • Ward: Menggabungkan dengan meminimalkan varians dalam gerombol.

library(cluster)
library(fpc)
library(factoextra)

metode_pautan <- c("single", "complete", "average", "centroid", "ward.D2")
nama_tampilan <- c("Tunggal", "Lengkap", "Rataan", "Centroid", "Ward")
nilai_coph <- numeric(length(metode_pautan))

for(i in 1:length(metode_pautan)) {

  hc_temp <- hclust(dist_object, method = metode_pautan[i])
  
  coph_dist <- cophenetic(hc_temp)
  nilai_coph[i] <- cor(dist_object, coph_dist)
}

hasil_evaluasi <- data.frame(Metode = nama_tampilan, 
                             Kode_R = metode_pautan,
                             Cophenetic = nilai_coph)
print("=== HASIL EVALUASI PAUTAN ===")
## [1] "=== HASIL EVALUASI PAUTAN ==="
print(hasil_evaluasi[order(-hasil_evaluasi$Cophenetic), ])
##     Metode   Kode_R Cophenetic
## 3   Rataan  average  0.8244066
## 2  Lengkap complete  0.8224773
## 4 Centroid centroid  0.8049175
## 5     Ward  ward.D2  0.7884391
## 1  Tunggal   single  0.7734681
best_index <- which.max(nilai_coph)
best_method_name <- metode_pautan[best_index]
best_method_label <- nama_tampilan[best_index]

cat("\nMetode Terbaik Terpilih:", best_method_label, "\n")
## 
## Metode Terbaik Terpilih: Rataan
cat("Menyimpan model terbaik ke 'hc_result'...\n")
## Menyimpan model terbaik ke 'hc_result'...
hc_result <- hclust(dist_object, method = best_method_name)

Sesuai dengan kriteria evaluasi kemiripan, metode dengan nilai koefisien yang paling mendekati 1 dianggap sebagai metode terbaik. Artinya, dendrogram yang dihasilkan oleh metode Average Linkage mampu merepresentasikan struktur jarak asli (MDDTW) antarprovinsi dengan tingkat akurasi paling tinggi dibandingkan empat metode lainnya. Metode Pautan Rataan (Average Linkage) menghasilkan nilai Koefisien Korelasi Cophenetic tertinggi, yaitu sebesar 0.8244066. Sehingga pautan rataan dipilih sebagai metode penggerombolan final.

5.4 Menentukan Jumlah Cluster Optimal

Kode ini bertujuan untuk menentukan Jumlah Gerombol Optimal (\(k\)). Dalam Unsupervised Learning (seperti clustering), kita tidak tahu sejak awal berapa banyak kelompok yang sebaiknya dibentuk. Oleh karena itu, kita mencoba memotong pohon dendrogram dari \(k=2\) hingga \(k=10\), lalu menghitung skor kualitasnya menggunakan tiga metode statistik berbeda:

  • Silhouette Index: Mengukur seberapa pas suatu objek berada di gerombolnya sendiri dibandingkan gerombol tetangga.

  • Calinski-Harabasz Index (CHI): Mengukur rasio antara penyebaran antar-gerombol (separation) dengan kepadatan dalam-gerombol (compactness)

  • Elbow Method (WSS): Mengukur total varians dalam gerombol. Kita mencari titik di mana penurunan varians mulai melandai (seperti siku tangan).

k_range <- 2:10  
metrics <- data.frame(k = k_range, 
                      Silhouette = NA, 
                      CHI = NA, 
                      WSS = NA)

cat("\nMenghitung Indeks Validasi (Silhouette, CHI, Elbow)...\n")
## 
## Menghitung Indeks Validasi (Silhouette, CHI, Elbow)...
for(i in 1:length(k_range)) {
  k <- k_range[i]
  
  cluster_labels <- cutree(hc_result, k = k)
  
  sil <- silhouette(cluster_labels, dist_object)
  metrics$Silhouette[i] <- summary(sil)$avg.width
  
  stats <- cluster.stats(dist_object, cluster_labels)
  metrics$CHI[i] <- stats$ch
  metrics$WSS[i] <- stats$within.cluster.ss
}

print(metrics)
##    k Silhouette      CHI       WSS
## 1  2  0.6391421 53.31532 11102.424
## 2  3  0.3721286 56.40828  6380.396
## 3  4  0.3425486 40.37126  5876.409
## 4  5  0.2570780 31.84684  5488.975
## 5  6  0.2598906 28.49491  4861.757
## 6  7  0.2128291 28.25164  4067.000
## 7  8  0.2254591 26.50400  3638.315
## 8  9  0.2186942 25.90586  3186.287
## 9 10  0.2009179 23.87112  2974.397
par(mfrow = c(1, 3)) 


plot(metrics$k, metrics$Silhouette, type = "b", pch = 19, col = "blue",
     main = "Metode Silhouette", xlab = "Jumlah Gerombol (k)", ylab = "Avg Silhouette Width")

abline(v = metrics$k[which.max(metrics$Silhouette)], col = "red", lty = 2)


plot(metrics$k, metrics$CHI, type = "b", pch = 19, col = "green",
     main = "Calinski-Harabasz Index", xlab = "Jumlah Gerombol (k)", ylab = "CHI Value")

abline(v = metrics$k[which.max(metrics$CHI)], col = "red", lty = 2)


plot(metrics$k, metrics$WSS, type = "b", pch = 19, col = "orange",
     main = "Metode Elbow", xlab = "Jumlah Gerombol (k)", ylab = "Total WSS")

par(mfrow = c(1, 1)) 
  • Metode Silhouette. Semakin tinggi nilainya (mendekati 1), semakin sempurna pengelompokannya. Output metode ini menyarankan \(k=2\)
  • Calinski-Harabasz Index. Nilai yang tinggi menunjukkan gerombol-gerombol tersebut terpisah dengan jelas satu sama lain. Output metode ini menyarankan \(k=3\).
  • Elbow Method. Cari titik di mana garis yang tadinya turun curam tiba-tiba menjadi landai. Titik belokan itulah \(k\) optimal. Di skripsi referensi, siku terbentuk jelas pada \(k=3\)

Berdasarkan hasil ketiga metode validasi internal, jumlah gerombol optimal untuk data harga bawang merah yang digunakan adalah 3 gerombol. Meskipun metode Silhouette menyarankan 2 gerombol, namun metode Calinski-Harabasz dan Elbow Method keduanya sepakat pada 3 gerombol. Oleh karena itu, untuk analisis selanjutnya, kita akan menggunakan k=3 sebagai jumlah cluster final.

5.5 Visualisas dendogram

library(factoextra)

fviz_dend(hc_result, 
          k = 3,                 
          cex = 0.6,             
          
          # --- PENGATURAN WARNA ---
          # Urutan: Cluster 1 (Biru), Cluster 2 (Kuning), Cluster 3 (Merah)
          k_colors = c("#2E9FDF", "#E7B800", "#FC4E07"), 
          
          color_labels_by_k = TRUE, 
          rect = TRUE, 
          rect_fill = TRUE, # Opsional: memberi warna latar belakang kotak transparan
          main = "Dendrogram Penggerombolan Harga Bawang Merah",
          xlab = "Provinsi", ylab = "Jarak MDDTW")

Dendrogram ini adalah visualisasi “pohon kekerabatan” harga bawang merah, di mana tinggi rendahnya garis vertikal menunjukkan tingkat kemiripan; semakin pendek garisnya, semakin “kembar” pola harganya, sedangkan semakin tinggi garisnya, semakin berbeda karakteristik pasarnya. Pembagian menjadi tiga warna (kluster) menegaskan bahwa Indonesia terbagi menjadi tiga zona perilaku harga yang berbeda. Percabangan dalam diagram ini menceritakan detail hubungan tersebut, di mana provinsi yang menyatu di bagian bawah memiliki sinkronisasi harga yang sangat kuat.

Secara spesifik pada kluster biru, Maluku Utara dan Papua menyatu lebih dulu karena keduanya memiliki pola lonjakan harga yang paling identik dan ekstrem, sedangkan Papua Barat memisah (bergabung di garis yang lebih tinggi) karena meskipun sama-sama mahal, ia memiliki sedikit perbedaan pola atau waktu lonjakan dibanding dua lainnya. Sementara itu, pada kluster kuning dan merah, percabangannya tampak jauh lebih pendek dan rapat-rapat, yang menandakan bahwa provinsi-provinsi di dalamnya (seperti Jawa, Sumatera, dan Kalimantan) memiliki pasar yang sangat terintegrasi dan kompak; artinya, jika harga berubah di satu provinsi, provinsi tetangganya akan langsung mengikuti dengan pola yang nyaris seragam tanpa perbedaan yang signifikan.

5.6 Penggerombolan Final

k_optimal <- 3 

final_clusters <- cutree(hc_result, k = k_optimal)
df_clustered <- data.frame(NamaProv = rownames(data_matrix), 
                           Cluster = final_clusters)
print(df_clustered %>% arrange(Cluster))
##                                            NamaProv Cluster
## Aceh                                           Aceh       1
## Bali                                           Bali       1
## Banten                                       Banten       1
## Bengkulu                                   Bengkulu       1
## DI Yogyakarta                         DI Yogyakarta       1
## Jambi                                         Jambi       1
## Jawa Barat                               Jawa Barat       1
## Jawa Tengah                             Jawa Tengah       1
## Jawa Timur                               Jawa Timur       1
## Kalimantan Selatan               Kalimantan Selatan       1
## Lampung                                     Lampung       1
## Nusa Tenggara Barat             Nusa Tenggara Barat       1
## Nusa Tenggara Timur             Nusa Tenggara Timur       1
## Riau                                           Riau       1
## Sulawesi Barat                       Sulawesi Barat       1
## Sulawesi Selatan                   Sulawesi Selatan       1
## Sumatera Barat                       Sumatera Barat       1
## Sumatera Selatan                   Sumatera Selatan       1
## Sumatera Utara                       Sumatera Utara       1
## DKI Jakarta                             DKI Jakarta       2
## Gorontalo                                 Gorontalo       2
## Kalimantan Barat                   Kalimantan Barat       2
## Kalimantan Tengah                 Kalimantan Tengah       2
## Kalimantan Timur                   Kalimantan Timur       2
## Kalimantan Utara                   Kalimantan Utara       2
## Kepulauan Bangka Belitung Kepulauan Bangka Belitung       2
## Kepulauan Riau                       Kepulauan Riau       2
## Maluku                                       Maluku       2
## Sulawesi Tengah                     Sulawesi Tengah       2
## Sulawesi Tenggara                 Sulawesi Tenggara       2
## Sulawesi Utara                       Sulawesi Utara       2
## Maluku Utara                           Maluku Utara       3
## Papua                                         Papua       3
## Papua Barat                             Papua Barat       3
df_long_with_cluster <- data_bawang_prov %>%
  left_join(df_clustered, by = "NamaProv")
prototypes <- df_long_with_cluster %>%
  group_by(Cluster, Tanggal) %>%
  summarise(Harga_Prototype = mean(Harga_Mean, na.rm = TRUE), .groups = 'drop')

library(ggplot2)
ggplot(prototypes, aes(x = as.Date(Tanggal), y = Harga_Prototype, color = as.factor(Cluster))) +
  geom_line(size = 1.2) +
  
  scale_color_manual(values = c("1" = "#E7B800",  # Kuning (Gelap agar terlihat jelas)
                                "2" = "#FC4E07",  # Merah
                                "3" = "#2E9FDF"), # Biru
                     labels = c("1 (Kuning)", "2 (Merah)", "3 (Biru)")) +
  
  labs(title = "Prototype Deret Waktu Harga Bawang Merah per Gerombol",
       x = "Periode", y = "Harga Rataan (Rp)", color = "Gerombol") +
  theme_minimal() +
  theme(legend.position = "bottom")

Hasil visualisasi prototipe deret waktu harga bawang merah menunjukkan bahwa ketiga gerombol memiliki pola pergerakan harga yang serupa, tetapi dengan tingkat harga rata-rata yang berbeda secara konsisten. Gerombol 3 (biru) terlihat memiliki harga rata-rata tertinggi sepanjang periode 2022–2025, menandakan bahwa provinsi-provinsi di dalam klaster ini cenderung mengalami harga bawang merah yang lebih mahal. Kenaikan tajam pada awal tahun 2022 terlihat paling jelas pada gerombol ini, kemudian diikuti pola fluktuasi yang relatif stabil namun tetap berada pada tingkat harga yang tinggi dibandingkan dua gerombol lainnya.

Gerombol 2 (merah) menunjukkan posisi harga menengah. Polanya cukup mirip dengan gerombol biru, termasuk lonjakan harga besar pada awal 2022 dan pertengahan 2024, tetapi tingkat harganya berada di bawah gerombol biru. Hal ini menunjukkan bahwa provinsi dalam gerombol merah menghadapi dinamika harga yang lebih moderat, kemungkinan dipengaruhi oleh kondisi pasokan yang lebih baik dibandingkan klaster 3, namun tetap rentan terhadap fluktuasi musiman dan perubahan distribusi antar wilayah. Secara umum, tren harga pada gerombol merah mengalami peningkatan bertahap hingga 2025.

Gerombol 1 (kuning) merupakan kelompok dengan harga rata-rata terendah di antara ketiganya. Meskipun mengalami lonjakan yang sama pada awal 2022 dan pertengahan 2024, level harga pada klaster ini tetap paling rendah dan cenderung mengalami fluktuasi lebih tajam pada beberapa titik waktu. Perilaku ini mengindikasikan bahwa provinsi dalam gerombol kuning memiliki kondisi pasar yang lebih kompetitif atau memiliki akses pasokan yang relatif lebih stabil sehingga harga berada pada tingkat yang lebih rendah. Secara keseluruhan, perbandingan ketiga klaster menegaskan adanya perbedaan struktural dalam pola harga antar wilayah, yang perlu dipertimbangkan dalam evaluasi kebijakan stabilisasi harga bawang merah.

5.7 Visualisasi Time Series per Cluster

library(ggplot2)
library(dplyr)

# --- KONFIGURASI WARNA IDENTITAS CLUSTER ---
# 1=Kuning, 2=Merah, 3=Biru (Sesuai request Anda)
warna_cluster <- c("1" = "#E7B800", 
                   "2" = "#FC4E07", 
                   "3" = "#2E9FDF")

# --- LOOPING UNTUK MEMBUAT 3 PLOT ---
for (k in 1:3) {
  
  # 1. Filter Data Provinsi Anggota Cluster k
  data_anggota <- df_long_with_cluster %>% 
    filter(Cluster == k)
  
  # 2. Filter Data Prototype Cluster k (untuk garis tebal)
  data_proto <- prototypes %>% 
    filter(Cluster == k)
  
  # 3. Buat Plot
  p <- ggplot() +
    
    # Layer 1: Garis Tipis (Setiap Provinsi Anggota)
    # Diberi warna beda-beda tiap provinsi agar bisa dibedakan
    geom_line(data = data_anggota, 
              aes(x = as.Date(Tanggal), y = Harga_Mean, group = NamaProv, color = NamaProv), 
              size = 0.7, alpha = 0.7) +
    
    # Layer 2: Garis Tebal (Prototype/Rataan Cluster)
    # Warnanya disesuaikan dengan identitas cluster (Kuning/Merah/Biru)
    geom_line(data = data_proto, 
              aes(x = as.Date(Tanggal), y = Harga_Prototype), 
              color = warna_cluster[as.character(k)], # Ambil warna dari konfigurasi di atas
              size = 1.5, linetype = "dashed") + # Putus-putus agar beda
    
    # Label dan Judul
    labs(title = paste("Dinamika Harga Anggota Gerombol", k),
         subtitle = paste("Garis Putus-putus = Rataan (Prototype) Cluster", k),
         x = "Periode", 
         y = "Harga (Rp/kg)", 
         color = "Provinsi Anggota") +
    
    theme_minimal() +
    theme(legend.position = "right", # Legenda provinsi di kanan
          plot.title = element_text(face = "bold", size = 14))
  
  # Tampilkan Plot
  print(p)
}

Gambar dinamika harga untuk Gerombol 1 menunjukkan bahwa seluruh provinsi dalam klaster ini memiliki pola pergerakan harga yang sangat mirip sepanjang periode 2022–2025. Meskipun terdapat variasi antarprovinsi, keseluruhan anggota klaster cenderung mengikuti naik-turunnya harga pada waktu yang sama. Hal ini terlihat dari pergerakan garis yang saling berdekatan dan bergerak paralel, terutama ketika terjadi lonjakan harga besar pada awal 2022 serta kenaikan kembali pada pertengahan 2024.

Secara umum, provinsi dalam Gerombol 1 merupakan kelompok dengan tingkat harga bawang merah terendah dibandingkan klaster lainnya, sesuai dengan prototipe (garis putus-putus). Meskipun harganya relatif lebih rendah, fluktuasi musiman tetap terlihat jelas, terutama pada periode panen raya dan periode ketika pasokan menurun. Setelah penurunan harga yang cukup tajam pada awal 2023 dan awal 2024, sebagian besar provinsi di klaster ini kembali menunjukkan pola kenaikan harga yang konsisten menuju 2025.

Pola yang seragam ini mengindikasikan bahwa seluruh provinsi di Gerombol 1 kemungkinan memiliki struktur pasar dan sumber pasokan yang relatif mirip, sehingga respon harga terhadap perubahan pasokan dan permintaan juga bergerak serentak. Kemiripan ini memperkuat alasan pengelompokan mereka ke dalam satu klaster, karena dinamika harga antarprovinsi di dalamnya cenderung berjalan searah dan berada pada kisaran harga yang sama.

Gambar dinamika harga untuk Gerombol 2 menunjukkan bahwa provinsi-provinsi dalam klaster ini memiliki pola pergerakan harga yang seragam, meskipun tingkat harga antarprovinsi tidak selalu identik. Secara umum, seluruh anggota mengalami puncak harga yang sama pada awal tahun 2022 dan kembali menampilkan pola fluktuasi yang hampir paralel hingga tahun 2025. Hal ini terlihat dari garis-garis yang bergerak naik dan turun pada waktu yang sama, menandakan adanya pengaruh musiman atau faktor pasokan yang dirasakan serentak oleh provinsi dalam klaster ini.

Jika dibandingkan dengan Gerombol 1, tingkat harga pada Gerombol 2 berada pada kisaran menengah, sesuai dengan nilai prototipe (ditunjukkan oleh garis putus-putus). Meskipun begitu, beberapa provinsi seperti Maluku dan Kalimantan Timur kadang berada sedikit di atas rataannya, sementara provinsi seperti Gorontalo atau Kepulauan Bangka Belitung berada sedikit di bawah. Perbedaan kecil ini masih dalam rentang wajar dan tidak mengubah karakter umum bahwa seluruh provinsi dalam klaster ini mengikuti pola dinamika harga yang hampir sama.

Kemiripan pola yang kuat menunjukkan bahwa provinsi dalam Gerombol 2 memiliki struktur pasar, pola permintaan, dan keterhubungan distribusi yang relatif searah. Fluktuasi besar pada awal 2024 dan 2025 juga terlihat bergerak serempak di semua provinsi, menandakan bahwa klaster ini cukup sensitif terhadap perubahan pasokan nasional. Dengan demikian, karakteristik Gerombol 2 dapat dipahami sebagai kelompok provinsi dengan harga menengah dan respons harga yang bergerak bersama terhadap kejadian pasar yang sama.

Gambar dinamika harga untuk Gerombol 3 justru memperlihatkan ketidakseragaman (heterogenitas) yang paling tinggi dibandingkan gerombol lainnya. Berbeda dengan gerombol stabil yang garis-garisnya saling menempel, garis-garis harga provinsi pada Gerombol 3 terlihat menyebar lebar dan “berantakan”, terutama saat terjadi kenaikan harga. Jarak (gap) antara provinsi dengan harga tertinggi dan terendah dalam satu waktu sangat jauh; ada provinsi yang harganya sudah tembus Rp 100.000 (puncak tertinggi), sementara anggota lain di waktu yang sama mungkin masih di Rp 60.000.

Secara karakteristik, ini adalah Gerombol Volatilitas Ekstrem. Garis rataan (prototype) putus-putus berada di tengah, namun garis-garis asli anggotanya bergerak liar di atas dan di bawahnya. Hal ini menandakan bahwa meskipun provinsi-provinsi ini sama-sama memiliki tren harga mahal (rata-rata tinggi), perilaku pasarnya tidak terintegrasi dengan baik. Kenaikan harga di satu provinsi tidak selalu diikuti dengan irama yang sama oleh provinsi lain dalam gerombol ini.

Pola yang “renggang” dan penuh lonjakan tajam ini sangat khas merepresentasikan Wilayah Defisit/Timur (seperti Papua, Maluku). Keregangan garis-garis ini mengindikasikan bahwa setiap provinsi seolah memiliki masalah logistiknya masing-masing yang unik—satu daerah mungkin telat kapal sehingga harga meroket sendirian, sementara daerah tetangganya aman. Ini adalah bukti visual dari inefisiensi pasar dan tingginya risiko ketidakpastian harga di wilayah tersebut.

6 Kesimpulan

Visualisasi dinamika harga dari ketiga gerombol memberikan bukti empiris yang kuat bahwa pasar bawang merah di Indonesia terfragmentasi secara struktural ke dalam tiga rezim pasar yang berbeda. Metode clustering berbasis MDDTW berhasil memetakan gradasi efisiensi pasar, mulai dari Gerombol 1 yang merepresentasikan wilayah sentra produksi dengan pasar yang sangat terintegrasi dan harga terendah, hingga Gerombol 3 yang mencerminkan wilayah defisit logistik dengan inefisiensi pasar yang parah dan volatilitas harga ekstrem. Di antara keduanya, Gerombol 2 hadir sebagai wilayah transisi yang perilakunya mengikuti tren sentra produksi namun dengan margin harga distribusi.

Temuan ini menegaskan bahwa disparitas harga antarwilayah bukan sekadar masalah perbedaan nominal rupiah, melainkan perbedaan mendasar dalam perilaku dan integrasi pasar. Tingginya heterogenitas pada Gerombol 3 menunjukkan bahwa risiko ketidakpastian harga di wilayah timur bersifat lokal dan unik (akibat kendala logistik spesifik), berbeda dengan Gerombol 1 dan 2 yang resikonya bersifat sistemik (mengikuti siklus panen nasional). Oleh karena itu, analisis ini menyimpulkan bahwa intervensi kebijakan tunggal (one-size-fits-all) tidak akan efektif; diperlukan pendekatan manajemen stok untuk wilayah stabil (Gerombol 1 & 2) dan perbaikan infrastruktur distribusi fisik untuk wilayah fluktuatif (Gerombol 3).

7 Referensi

Kereh, P. F. F. 2025. Time Series Clustering dengan Mahalanobis Distance-Based Dynamic Time Warping pada Peramalan Tingkat Gerombol Harga Bawang Merah Provinsi di Indonesia. Skripsi. Program Studi Sarjana Statistika dan Sains Data, Sekolah Sains Data, Matematika dan Informatika, Institut Pertanian Bogor, Bogor.