1 Import Library

library(readxl)
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
library(cluster)
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(purrr)
library(broom)
library(e1071)
## 
## Attaching package: 'e1071'
## The following object is masked from 'package:ggplot2':
## 
##     element

2 Import Data

Data yang digunakan adalah harga minyak goreng bulanan per kabupaten/kota di Sumatera Barat selama 10 bulan yaitu rentang Januari sampai Oktober 2025. Terdapat tiga 3 kategori minyak goreng yaitu minyak goreng kemasan, minyak goreng curah, dan minyakita. Kabupaten/kota di Sumatera Barat berjumlah 19.

Sehingga jumlah observasi yang akan dianalisis adalah 10 x 3 x 19 = 570 observasi.

datatpg <- read_excel("C:/Users/user/Documents/Semester 5/Teknik Peubah Ganda/Data Tugas Individu TPG.xlsx", sheet="Data Cleaning")
datatpg

3 Eksplorasi Data

3.1 Struktur Data

glimpse(datatpg)
## Rows: 570
## Columns: 4
## $ KabKot   <chr> "Kab. Agam", "Kab. Agam", "Kab. Agam", "Kab. Agam", "Kab. Aga…
## $ Periode  <dttm> 2025-01-01, 2025-01-01, 2025-01-01, 2025-02-01, 2025-02-01, …
## $ Kategori <chr> "Minyak Goreng Kemasan", "Minyak Goreng Curah", "Minyakita", …
## $ Harga    <chr> "20862", "16695", "18221", "20781", "16000", "18278", "21067"…
datatpg$Harga <- as.numeric(datatpg$Harga)
## Warning: NAs introduced by coercion
summary(datatpg$Harga)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   14400   16824   17759   17889   19508   21827      10

Dari summary data di atas, terlihat bahwa jumlah missing value (NA) pada data sebanyak 10 observasi. Namun kita akan mengecek di mana NA tersebut berada.

3.2 Imputasi Missing Value (NA)

colSums(is.na(datatpg))
##   KabKot  Periode Kategori    Harga 
##        0        0        0       10
datatpg %>% group_by(KabKot, Kategori) %>% summarise(NA_Harga = sum(is.na(Harga)))
## `summarise()` has grouped output by 'KabKot'. You can override using the
## `.groups` argument.

Terliha bahwa missing value ada pada data kategori “Minyakita” di Kab. Solok. Hal ini bisa saja terjadi jika kategori tersebut belum masuk ke daerah tersebut. Untuk mengimputasi, kita bisa mengisinya dengan rata-rata seluruh provinsi lain untuk kategori “Minyakita”

mean_prov <- datatpg %>%
  group_by(Kategori) %>%
  summarise(mean_provinsi = mean(Harga, na.rm = TRUE))

datatpg <- datatpg %>%
  left_join(mean_prov, by = "Kategori")

datatpg <- datatpg %>%
  mutate(Harga = ifelse(is.na(Harga), mean_provinsi, Harga))

Cek lagi jumlah NA

paste("Jumlah NA di kolom Harga =", sum(is.na(datatpg$Harga)))
## [1] "Jumlah NA di kolom Harga = 0"

3.3 Plot Time Series per Kategori

ggplot(datatpg, aes(x = Periode, y = Harga, color = KabKot)) +
  geom_line(alpha = 0.5) +
  facet_wrap(~Kategori, scales = "free_y") +
  theme_bw()

Interpretasi Plot Time Series untuk Tiap Kategori Minyak Goreng

  1. Minyak Goreng Curah

    Harga minyak goreng curah menunjukkan fluktuasi yang paling tinggi dibanding kategori lainnya, dengan beberapa daerah mengalami lonjakan tajam terutama pada periode awal tahun, lalu menurun kembali di pertengahan hingga akhir tahun. Variasi harga yang besar antardaerah menggambarkan sensitivitas minyak curah terhadap faktor faktor lain seperti gangguan distribusi, perubahan pasokan, maupun dinamika pasar lokal, sehingga wilayah dengan akses logistik lemah terlihat lebih cepat berfluktuasi dibanding kota-kota besar. Secara keseluruhan, pola ini menegaskan bahwa minyak curah merupakan komoditas yang paling rentan.

  2. Minyak Goreng Kemasan

    Berbeda dengan minyak goreng curah, harga minyak goreng kemasan cenderung lebih stabil, yaitu cenderung berpusat di 19.000-21.000 selama sebagian besar periode. Namun, beberapa daerah mencatat kenaikan signifikan di akhir tahun, yang kemungkinan dipicu faktor lain seperti permintaan yang tiba-tiba meningkat.

  3. Minyakita

    Minyakita adalah minyak goreng yang terlihat paling stabil harganya dibandingkan kategori lainnya, dengan harga yang cenderung berpusat di 17.000-19.000 dan tren menurun perlahan menuju akhir tahun. Terdapat beberapa lonjakan kecil di awal periode pada beberapa daerah, namun setelah itu harga jadi lebih stabil dan konsisten.

3.4 Boxplot Harga per Kabupaten/Kota

ggplot(datatpg, aes(x = KabKot, y = Harga)) +
  geom_boxplot() +
  facet_wrap(~Kategori) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90))

Interpretasi Boxplot Harga Tiap Kategori Minyak per Kabupaten/Kota

  1. Minyak Goreng Curah

    Grafik menunjukkan variasi harga yang cukup lebar sepanjang periode, dengan beberapa titik harga yang sangat tinggi pada bulan tertentu.

  2. Minyak Goreng Kemasan

    Harga cenderung lebih stabil dibandingkan minyak goreng curah, meskipun ada beberapa fluktuasi yang signifikan di titik-titik tertentu sepanjang tahun.

  3. Minyakita

    Harga minyakita lebih konsisten dengan sedikit perubahan yang lebih kecil, meskipun tetap menunjukkan kenaikan dan penurunan dalam rentang waktu tertentu.

4 Pembentukan Fitur Time Series untuk Cluster

4.1 Urutkan Data

datatpg <- datatpg %>%
  arrange(KabKot, Kategori, Periode)

4.2 Reshape Menjadi Wide Format

datatpg_ts <- datatpg %>%
  select(KabKot, Kategori, Periode, Harga) %>%
  mutate(Periode = paste0("Periode_", Periode)) %>%
  pivot_wider(
    names_from = Periode,
    values_from = Harga
  )

4.3 Cek Hasil Wide

datatpg_ts

5 Fuzzy C-Means Clustering (3 Cluster)

datatpg_ts_numerik <- datatpg_ts %>%
  select(starts_with("Periode_")) %>%
  as.matrix()

set.seed(123)

fcm_result <- cmeans(
  datatpg_ts_numerik,
  centers = 3,  # jumlah cluster
  m = 2         # fuzziness degree
)

datatpg_ts$cluster <- fcm_result$cluster

# Membership fuzzy maksimal
datatpg_ts$fuzzy_membership <- apply(fcm_result$membership, 1, which.max)

# Tambahkan nilai membership penuh
datatpg_ts <- cbind(datatpg_ts, fcm_result$membership)

5.1 Cluster Centers (Centroid Time Series)

fcm_result$centers
##   Periode_2025-01-01 Periode_2025-02-01 Periode_2025-03-01 Periode_2025-04-01
## 1           16125.51           15840.78           15885.09           15980.14
## 2           19436.19           19695.61           19844.47           19949.37
## 3           17518.61           17651.39           17751.82           17735.89
##   Periode_2025-05-01 Periode_2025-06-01 Periode_2025-07-01 Periode_2025-08-01
## 1           15801.36           15482.58           15313.84           15464.33
## 2           19987.90           19955.19           19966.28           19958.84
## 3           17753.28           17657.85           17489.09           17516.99
##   Periode_2025-09-01 Periode_2025-10-01
## 1           15455.05           15449.43
## 2           19923.23           19829.89
## 3           17514.85           17501.71

Keterangan :

  • Cluster 1 = harga minyak goreng terendah sepanjang tahun
  • Cluster 2 = harga minyak goreng tertinggi sepanjang tahun
  • Cluster 3 = harga minyak goreng menengah & cenderung stabil / fluktuasi kecil

5.2 Membership Matrix (Keanggotaan Fuzzy)

fcm_result$membership
##                 1           2           3
##  [1,] 0.934492279 0.013398035 0.052109686
##  [2,] 0.041659424 0.855751978 0.102588598
##  [3,] 0.036026666 0.056795371 0.907177963
##  [4,] 0.225785680 0.045854933 0.728359387
##  [5,] 0.005116904 0.977036662 0.017846434
##  [6,] 0.003773498 0.003443143 0.992783359
##  [7,] 0.380057539 0.045700571 0.574241890
##  [8,] 0.034271857 0.878315580 0.087412563
##  [9,] 0.010714863 0.009212050 0.980073087
## [10,] 0.976461534 0.004648449 0.018890017
## [11,] 0.006892103 0.971251000 0.021856897
## [12,] 0.150587179 0.039125842 0.810286980
## [13,] 0.077745866 0.600035072 0.322219062
## [14,] 0.013583136 0.936803074 0.049613790
## [15,] 0.028316589 0.039077318 0.932606094
## [16,] 0.918292206 0.011954519 0.069753275
## [17,] 0.024963904 0.856666394 0.118369702
## [18,] 0.011299532 0.012666930 0.976033538
## [19,] 0.973991051 0.005131242 0.020877707
## [20,] 0.001944937 0.990891591 0.007163472
## [21,] 0.032905955 0.014988649 0.952105396
## [22,] 0.031571271 0.038180779 0.930247950
## [23,] 0.038752045 0.860203828 0.101044127
## [24,] 0.040458186 0.021178732 0.938363081
## [25,] 0.960761897 0.006534215 0.032703888
## [26,] 0.015559573 0.929104225 0.055336203
## [27,] 0.065873034 0.247610915 0.686516051
## [28,] 0.029802401 0.047691340 0.922506259
## [29,] 0.084270664 0.118978922 0.796750414
## [30,] 0.003680077 0.002522239 0.993797685
## [31,] 0.681001619 0.036365511 0.282632870
## [32,] 0.002286993 0.990157417 0.007555590
## [33,] 0.047059004 0.098650062 0.854290935
## [34,] 0.189550496 0.281592266 0.528857239
## [35,] 0.080757375 0.507622253 0.411620372
## [36,] 0.096216245 0.031367332 0.872416423
## [37,] 0.954216695 0.009650454 0.036132851
## [38,] 0.012883564 0.932530082 0.054586354
## [39,] 0.110695442 0.030260010 0.859044548
## [40,] 0.767976646 0.027672720 0.204350634
## [41,] 0.029696248 0.849629116 0.120674635
## [42,] 0.035730670 0.020357096 0.943912235
## [43,] 0.982667394 0.003272778 0.014059829
## [44,] 0.002518815 0.989223161 0.008258024
## [45,] 0.103750238 0.034332103 0.861917659
## [46,] 0.934797220 0.014126334 0.051076446
## [47,] 0.051780896 0.635136514 0.313082590
## [48,] 0.187646810 0.038664708 0.773688482
## [49,] 0.957891131 0.008733081 0.033375788
## [50,] 0.001229497 0.994656308 0.004114194
## [51,] 0.096027161 0.030825872 0.873146968
## [52,] 0.776384207 0.026922541 0.196693252
## [53,] 0.001241253 0.994641576 0.004117171
## [54,] 0.010007090 0.010520279 0.979472631
## [55,] 0.988175515 0.002099407 0.009725078
## [56,] 0.003074490 0.986982743 0.009942767
## [57,] 0.003868868 0.002818836 0.993312297

5.3 Cluster Assignment per Kabupaten/Kota x Kategori

Tahap ini bertujuan mengelompokkan tiap kategori di tiap kabupaten/kota ke cluster clusternya.

datatpg_ts %>% select(KabKot, Kategori, cluster)

5.4 Plot Centroids Cluster

matplot(
  t(fcm_result$centers), type = "l", lty = 1, lwd = 2,
  xlab = "Periode", ylab = "Harga",
  main = "Pola Time Series Cluster (Fuzzy C-Means)"
)
legend("topleft", legend = paste("Cluster", 1:3), lty = 1, col = 1:3)

Grafik Pola Time Series Cluster (Fuzzy C-Means) menunjukkan tiga cluster yang menggambarkan pola harga dalam periode yang berbeda. Cluster 1 menunjukkan harga yang relatif stabil dengan sedikit fluktuasi, Cluster 2 mengalami sedikit peningkatan harga di awal periode, sedangkan Cluster 3 menunjukkan penurunan harga yang lebih stabil sepanjang waktu.

5.5 Plot Pola Cluster dengan Label Wilayah

# Mendeteksi kolom duplikat
duplicated_cols <- duplicated(colnames(datatpg_ts))

# Hapus kolom yang duplikat
datatpg_ts <- datatpg_ts[, !duplicated_cols]

# Membuat Plot
ggplot(datatpg_ts, aes(x = KabKot, y = cluster, color = Kategori)) +
  geom_point(size=3) +
  theme(axis.text.x = element_text(angle=90)) +
  labs(title="Cluster Kabupaten/Kota berdasarkan Kategori Minyak Goreng")

Grafik Cluster Kabupaten/Kota berdasarkan Kategori Minyak Goreng mengelompokkan berbagai daerah (Kabupaten/Kota) ke dalam tiga cluster berdasarkan harga minyak goreng. Cluster 1 menunjukkan daerah dengan harga Minyak Goreng Curah, Cluster 2 menunjukkan daerah dengan harga Minyak Goreng Kemasan, dan Cluster 3 menunjukkan daerah dengan harga Minyakita. Setiap cluster mengelompokkan beberapa kabupaten/kota, menunjukkan perbedaan harga antara jenis minyak goreng di masing-masing daerah.

5.6 Silhouette Index

Untuk menilai kualitas clustering, akan dicari silhouette index.

dist_mat <- dist(datatpg_ts_numerik)
sil <- silhouette(datatpg_ts$cluster, dist_mat)
plot(sil, main = "Silhouette Plot Fuzzy Clustering")

Grafik Silhouette Plot Fuzzy Clustering menunjukkan hasil evaluasi untuk tiga cluster yang dihasilkan oleh metode fuzzy clustering. Setiap cluster diwakili oleh sebuah bar horizontal yang menggambarkan nilai Silhouette width (si) untuk setiap elemen dalam cluster. Cluster 1 memiliki si tertinggi (0.64), diikuti oleh Cluster 2 (0.58), dan Cluster 3 (0.56), menunjukkan bahwa elemen-elemen di Cluster 1 lebih terdefinisi dengan baik, sementara Cluster 3 memiliki sedikit ketidakpastian dalam pembagian datanya. Rata-rata nilai Silhouette width untuk seluruh data adalah 0.58, yang menunjukkan bahwa clustering cukup baik, meskipun ada beberapa elemen yang lebih terpisah atau kurang terklaster dengan jelas.

6 Insights

Berdasarkan analisis harga minyak goreng, berikut adalah insight dan saran kebijakan untuk pemerintah:

  1. Terdapat fluktuasi harga yang tajam, terutama untuk Minyak Goreng Curah dan Minyak Goreng Kemasan, menunjukkan ketidakstabilan distribusi. Pemerintah perlu memperbaiki distribusi dan mengimplementasikan harga eceran tertinggi (HET) untuk menjaga stabilitas harga.

  2. Berdasarkan hasil clustering, Minyak Goreng Curah lebih murah namun fluktuatif, sementara Minyakita lebih stabil dan mahal. Subsidi untuk minyak goreng curah dapat membantu menjaga daya beli, dan memperluas distribusi Minyakita di seluruh daerah.

  3. Harga minyak goreng sangat bervariasi antar daerah, terutama untuk Minyak Goreng Curah. Pemerintah daerah harus meningkatkan distribusi untuk mengurangi perbedaan harga antar wilayah.

  4. Ketidakpastian harga dalam clustering menunjukkan adanya ketidakteraturan pasar. Pengawasan yang lebih ketat terhadap pasar dan pemberian sanksi untuk spekulan harga perlu diperkuat.

  5. Masyarakat perlu pemahaman lebih tentang perbedaan harga minyak goreng. Pemerintah harus melaksanakan program edukasi untuk membantu masyarakat memilih minyak goreng yang sesuai dengan anggaran dan kebutuhan mereka.

Kesimpulan: Pemerintah harus memastikan distribusi yang merata, pengawasan pasar yang lebih ketat, dan stabilitas harga untuk mendukung daya beli masyarakat.