1 Ekplorasi Data

1.1 Library

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.4.2
## Warning: package 'ggplot2' was built under R version 4.4.3
## Warning: package 'tidyr' was built under R version 4.4.2
## Warning: package 'readr' was built under R version 4.4.2
## Warning: package 'purrr' was built under R version 4.4.2
## Warning: package 'stringr' was built under R version 4.4.3
## Warning: package 'forcats' was built under R version 4.4.2
## Warning: package 'lubridate' was built under R version 4.4.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(factoextra)
## Warning: package 'factoextra' was built under R version 4.4.3
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa

1.2 Data

data_tpg <- read.csv("C:/Users/Resea/Documents/Semester 5/TPG/TUGAS INDIVIDU/0_df_long.csv", sep = ";", header = TRUE)

head (data_tpg)
##            KabKot Tahun   Bulan                 Produk Harga Kategori KodeBPS
## 1 Kab. Aceh Barat  2022 Januari          Beras Premium 11429    Beras   11.05
## 2 Kab. Aceh Barat  2022 Januari           Beras Medium  9979    Beras   11.05
## 3 Kab. Aceh Barat  2022 Januari           Bawang Merah 31000   Bawang   11.05
## 4 Kab. Aceh Barat  2022 Januari Bawang Putih (Bonggol) 28636   Bawang   11.05
## 5 Kab. Aceh Barat  2022 Januari   Cabai Merah Keriting 19409    Cabai   11.05
## 6 Kab. Aceh Barat  2022 Januari      Cabai Rawit Merah 45706    Cabai   11.05
##   KodeProv NamaProv
## 1       11     Aceh
## 2       11     Aceh
## 3       11     Aceh
## 4       11     Aceh
## 5       11     Aceh
## 6       11     Aceh
str(data_tpg) # Melihat struktur data
## '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" ...
data_tpg <- data_tpg %>%
  mutate(
    Tahun    = as.integer(Tahun),
    Harga    = as.numeric(Harga),
    KodeProv = as.numeric(KodeProv)
  )
produk_hewani <- c("Daging Ayam Ras", "Daging Sapi Murni", "Telur Ayam Ras")

1.3 Filter Data

data_jabar_hewani_2025 <- data_tpg %>%
  filter(
    Tahun == 2025,
    NamaProv == "Jawa Barat",
    Produk %in% produk_hewani
  )

head(data_jabar_hewani_2025)
##         KabKot Tahun    Bulan            Produk  Harga Kategori KodeBPS
## 1 Kab. Bandung  2025  Januari Daging Sapi Murni 125300  Protein   32.04
## 2 Kab. Bandung  2025  Januari   Daging Ayam Ras  35672  Protein   32.04
## 3 Kab. Bandung  2025  Januari    Telur Ayam Ras  27803  Protein   32.04
## 4 Kab. Bandung  2025 Februari Daging Sapi Murni 126342  Protein   32.04
## 5 Kab. Bandung  2025 Februari   Daging Ayam Ras  35147  Protein   32.04
## 6 Kab. Bandung  2025 Februari    Telur Ayam Ras  27929  Protein   32.04
##   KodeProv   NamaProv
## 1       32 Jawa Barat
## 2       32 Jawa Barat
## 3       32 Jawa Barat
## 4       32 Jawa Barat
## 5       32 Jawa Barat
## 6       32 Jawa Barat

1.4 Rata-rata untuk Imputasi

Menggunakan hirarki imputasi seperti ini: 1. Harga asli 2. Rata-rata per Kab/Kota & Produk (di Jawa Barat 2025) 3. Rata-rata Jawa Barat per Produk (2025) 4. Rata-rata provinsi tetangga Jawa Barat (DKI Jakarta, Banten, Jawa Tengah) 5. Rata-rata nasional per Produk (2025)

# Rata-rata Kab/Kota per Produk Jawa Barat 2025
mean_kab_prod_jabar <- data_jabar_hewani_2025 %>%
  group_by(KabKot, Produk) %>%
  summarise(mean_kab = mean(Harga, na.rm = TRUE), .groups = "drop") 
# Rata-rata Jawa Barat per Produk 2025
mean_jabar_prod <- data_jabar_hewani_2025 %>%
  group_by(Produk) %>%
  summarise(mean_jabar = mean(Harga, na.rm = TRUE), .groups = "drop")
# Rata-rata Provinsi Tetangga
neighbor_jabar <- c("DKI Jakarta", "Banten", "Jawa Tengah")

mean_tetangga <- data_tpg %>%
  filter(
    Tahun == 2025,
    NamaProv %in% neighbor_jabar,
    Produk %in% produk_hewani
  ) %>%
  group_by(Produk) %>%
  summarise(mean_neighbor = mean(Harga, na.rm = TRUE), .groups = "drop")
# Rata-rata Nasional per Produk Hewani 2025
mean_nasional <- data_tpg %>%
  filter(
    Tahun == 2025,
    Produk %in% produk_hewani
  ) %>%
  group_by(Produk) %>%
  summarise(mean_nasional = mean(Harga, na.rm = TRUE), .groups = "drop")

1.5 Bar chart

library(dplyr)
library(ggplot2)

data_jabar_hewani_2025 <- data_jabar_hewani_2025 |>
  mutate(
    Bulan = factor(
      Bulan,
      levels = c("Januari","Februari","Maret","April","Mei","Juni",
                 "Juli","Agustus","September","Oktober","November","Desember")
    )
  )

data_jabar_hewani_2025 |>
  group_by(Produk) |>
  summarise(mean_harga = mean(Harga, na.rm = TRUE)) |>
  ggplot(aes(x = reorder(Produk, mean_harga), y = mean_harga)) +
  geom_col() +
  coord_flip() +
  labs(
    title = "Rata-rata harga komoditas hewani\nJawa Barat, 2025",
    x = "Produk",
    y = "Rata-rata Harga"
  )

Grafik tersebut menunjukkan bahwa pada tahun 2025 di Jawa Barat, rata-rata harga komoditas hewani paling tinggi adalah daging sapi murni, disusul oleh daging ayam ras, sementara telur ayam ras memiliki harga rata-rata paling rendah; artinya, untuk memenuhi kebutuhan protein hewani, masyarakat cenderung lebih mudah menjangkau telur dan daging ayam ras dibandingkan daging sapi karena perbedaan harga yang cukup besar.

1.6 Box plot

ggplot(data_jabar_hewani_2025,
       aes(x = Produk, y = Harga)) +
  geom_boxplot() +
  coord_flip() +
  labs(
    title = "Distribusi harga komoditas hewani\nJawa Barat, 2025",
    x = "Produk",
    y = "Harga"
  )
## Warning: Removed 1 row containing non-finite outside the scale range
## (`stat_boxplot()`).

Grafik boxplot tersebut menunjukkan bahwa pada tahun 2025 di Jawa Barat, telur ayam ras memiliki harga paling rendah dan penyebaran yang relatif sempit sehingga harganya cukup stabil, daging ayam ras berharga lebih tinggi dengan rentang harga yang sedikit lebih lebar dan terdapat beberapa nilai ekstrem yang menunjukkan variasi harga di beberapa pasar, sedangkan daging sapi murni memiliki tingkat harga paling tinggi dengan sebaran yang juga cukup sempit meskipun ada titik-titik pencilan, sehingga secara umum harga daging sapi konsisten mahal namun masih terjadi variasi di beberapa lokasi.

2 Penanganan Missing Value

2.1 Imputasi dengan Harga Rata-rata Hierarki

data_jabar_hewani_2025_imp <- data_jabar_hewani_2025 %>%
  # Join mean kab/kota
  left_join(mean_kab_prod_jabar, by = c("KabKot", "Produk")) %>%
  # Join mean Jabar
  left_join(mean_jabar_prod, by = "Produk") %>%
  # Join mean tetangga
  left_join(mean_tetangga, by = "Produk") %>%
  # Join mean nasional
  left_join(mean_nasional, by = "Produk") %>%
  mutate(
    Harga_imputed = case_when(
      !is.na(Harga) ~ Harga,                            
      is.na(Harga) & !is.na(mean_kab) ~ mean_kab,       
      is.na(Harga) & is.na(mean_kab) & !is.na(mean_jabar) ~ mean_jabar,
      is.na(Harga) & is.na(mean_kab) & is.na(mean_jabar) & !is.na(mean_neighbor) ~ mean_neighbor,
      TRUE ~ mean_nasional                  
    )
  )

# Cek apakah masih ada NA
sum(is.na(data_jabar_hewani_2025_imp$Harga_imputed))
## [1] 0

Hasilnya sudah 0 maka semua missing value sudah ditangani.

2.2 Matriks Kab/Kota × Produk Hewani

kab_produk_hewani_jabar <- data_jabar_hewani_2025_imp %>%
  group_by(KabKot, Produk) %>%
  summarise(
    mean_harga = mean(Harga_imputed, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  tidyr::pivot_wider(
    names_from  = Produk,
    values_from = mean_harga
  )

head(kab_produk_hewani_jabar)
## # A tibble: 6 × 4
##   KabKot             `Daging Ayam Ras` `Daging Sapi Murni` `Telur Ayam Ras`
##   <chr>                          <dbl>               <dbl>            <dbl>
## 1 Kab. Bandung                  34679              131454.           27782.
## 2 Kab. Bandung Barat            33448.             125424            27903.
## 3 Kab. Bekasi                   41574.             132942.           29067.
## 4 Kab. Bogor                    36507.             126500.           28030.
## 5 Kab. Ciamis                   38696.             135300.           28216.
## 6 Kab. Cianjur                  35699              133027.           29159.

2.3 Eksplorasi Data Setelah Imputasi

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

kab_long <- kab_produk_hewani_jabar |>
  pivot_longer(
    cols = -KabKot,          
    names_to  = "Produk",
    values_to = "Harga"
  )

head(kab_long)
## # A tibble: 6 × 3
##   KabKot             Produk              Harga
##   <chr>              <chr>               <dbl>
## 1 Kab. Bandung       Daging Ayam Ras    34679 
## 2 Kab. Bandung       Daging Sapi Murni 131454.
## 3 Kab. Bandung       Telur Ayam Ras     27782.
## 4 Kab. Bandung Barat Daging Ayam Ras    33448.
## 5 Kab. Bandung Barat Daging Sapi Murni 125424 
## 6 Kab. Bandung Barat Telur Ayam Ras     27903.

2.3.1 Bar chart

kab_long |>
  group_by(Produk) |>
  summarise(mean_harga = mean(Harga, na.rm = TRUE)) |>
  ggplot(aes(x = reorder(Produk, mean_harga), y = mean_harga)) +
  geom_col() +
  coord_flip() +
  labs(
    title = "Rata-rata harga komoditas hewani\nJawa Barat setelah imputasi",
    x = "Produk",
    y = "Rata-rata harga"
  )

Setelah dilakukan imputasi terhadap data yang hilang, grafik rata-rata harga komoditas hewani di Jawa Barat menunjukkan bahwa pola harganya tetap sama: daging sapi murni masih memiliki rata-rata harga paling tinggi, daging ayam ras berada di posisi menengah, dan telur ayam ras tetap menjadi komoditas dengan rata-rata harga paling rendah. Perbedaan ketiga rata-rata tersebut kini mencerminkan estimasi yang lebih lengkap dan andal karena sudah mempertimbangkan nilai-nilai yang sebelumnya kosong dalam data.

2.3.2 Box plot

ggplot(kab_long,
       aes(x = Produk, y = Harga)) +
  geom_boxplot() +
  labs(
    title = "Distribusi harga komoditas hewani\nantar kabupaten di Jawa Barat setelah imputasi",
    x = "Produk",
    y = "Harga"
  )

Setelah imputasi, boxplot menunjukkan bahwa distribusi harga komoditas hewani antar kabupaten di Jawa Barat tetap mempertahankan pola awal: daging sapi murni memiliki harga paling tinggi dengan sebaran yang cukup lebar menandakan perbedaan harga yang cukup besar antar kabupaten; daging ayam ras berada di tingkat harga menengah dengan rentang yang relatif lebih sempit sehingga variasi harganya moderat; sedangkan telur ayam ras tetap menjadi komoditas termurah dengan sebaran harga paling sempit, menunjukkan bahwa harga telur relatif paling stabil di berbagai kabupaten meskipun data yang hilang telah diimputasi.

kabkot <- kab_produk_hewani_jabar$KabKot

X <- kab_produk_hewani_jabar %>%
  select(-KabKot) %>%
  as.data.frame()

# Cek kalau masih ada NA
colSums(is.na(X))
##   Daging Ayam Ras Daging Sapi Murni    Telur Ayam Ras 
##                 0                 0                 0

Sudah terisi semua.

2.4 Standardisasi Data (Scaling)

Supaya semua produk hewani punya skala sebanding:

X_scaled <- scale(X)

summary(X_scaled)
##  Daging Ayam Ras    Daging Sapi Murni Telur Ayam Ras    
##  Min.   :-1.75238   Min.   :-1.8349   Min.   :-2.13354  
##  1st Qu.:-0.62506   1st Qu.:-0.6120   1st Qu.:-0.62708  
##  Median :-0.04615   Median :-0.1069   Median :-0.03067  
##  Mean   : 0.00000   Mean   : 0.0000   Mean   : 0.00000  
##  3rd Qu.: 0.66207   3rd Qu.: 0.4616   3rd Qu.: 0.48485  
##  Max.   : 2.23863   Max.   : 2.1900   Max.   : 2.57649

3 Clustering

3.1 Menentukan Jumlah Cluster

3.1.1 Elbow Method

fviz_nbclust(X_scaled, kmeans, method = "wss") +
  ggtitle("Elbow Method - Kab/Kota Jabar 2025 (Produk Hewani)")

Titik siku (elbow) yang paling terlihat: k = 3 atau k = 4.

3.1.2 Silhouette Method

fviz_nbclust(X_scaled, kmeans, method = "silhouette") +
  ggtitle("Silhouette Method - Kab/Kota Jabar 2025 (Produk Hewani)")

Menurut Silhouette, k = 7 adalah nilai terbaik.

3.1.3 Gap Statisic Method

library(cluster)
library(factoextra)

set.seed(123)

gap_stat <- clusGap(
  X_scaled,
  FUN = kmeans,
  nstart = 25,
  K.max = 10,   
  B = 100         
)

# Tampilkan tabel gap statistic
print(gap_stat, method = "firstmax")
## Clustering Gap statistic ["clusGap"] from call:
## clusGap(x = X_scaled, FUNcluster = kmeans, K.max = 10, B = 100, nstart = 25)
## B=100 simulated reference sets, k = 1..10; spaceH0="scaledPCA"
##  --> Number of clusters (method 'firstmax'): 1
##           logW   E.logW        gap     SE.sim
##  [1,] 2.679937 2.815722 0.13578469 0.07037081
##  [2,] 2.402820 2.516190 0.11336967 0.07072839
##  [3,] 2.229286 2.282945 0.05365886 0.07005051
##  [4,] 2.076377 2.106640 0.03026324 0.06920323
##  [5,] 1.932607 1.962026 0.02941820 0.07290277
##  [6,] 1.787801 1.831929 0.04412734 0.07405323
##  [7,] 1.625319 1.709929 0.08461061 0.07552367
##  [8,] 1.524241 1.591869 0.06762874 0.07823486
##  [9,] 1.404968 1.476145 0.07117735 0.08053665
## [10,] 1.299160 1.363439 0.06427820 0.08129567
# Visualisasikan
fviz_gap_stat(gap_stat) +
  ggtitle("Gap Statistic - Kab/Kota Jawa Barat 2025 (Produk Hewani)")

Menurut Gap Statistic nilai optimal k = 1 atau 2 karena memiliki gap tertinggi 0.1357846 dan 0.11336967.

3.1.4 Calinski-Harabasz Index

library(fpc)
## Warning: package 'fpc' was built under R version 4.4.3
set.seed(123)

k.max <- 10
ch <- numeric(k.max)

for (k in 2:k.max) {
  km <- kmeans(X_scaled, centers = k, nstart = 25)
  ch[k] <- calinhara(X_scaled, km$cluster, k)  # Calinski–Harabasz
}

# Lihat nilai CH tiap k
ch
##  [1]  0.00000 16.61720 15.69207 15.18459 15.29494 16.85159 18.55783 18.15479
##  [9] 18.39191 18.11041
# Plot
plot(2:k.max, ch[2:k.max], type = "b",
     xlab = "Number of clusters k",
     ylab = "Calinski–Harabasz Index",
     main = "CH Index - Kab/Kota Jabar 2025 (Produk Hewani)")

# k optimal (CH terbesar)
best_k <- which.max(ch)
best_k
## [1] 7

Calinski-Harabasz Index menunjukkan k = 7 sebagai nilai optimal.

3.1.5 NbClust dengan Calinski-Harabasz Index

library(NbClust)

set.seed(123)
res.ch <- NbClust(X_scaled,
                  distance = "euclidean",
                  min.nc = 2, max.nc = 10,
                  method = "kmeans",
                  index = "ch")

res.ch$Best.nc   
## Number_clusters     Value_Index 
##          7.0000         18.5578

Menurut NbClust dengan CH Index, k = 7 adalah nilai terbaik.

3.2 Clustering dengan K-Means k = 7

set.seed(123) 

k <- 7

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

# Lihat ukuran tiap cluster
km_7$size
## [1] 5 3 3 2 5 7 2
# Tambahkan label cluster ke kab/kota
cluster_kabkot_7 <- data.frame(
  KabKot  = kabkot,
  Cluster = factor(km_7$cluster)
)

# Urutkan biar rapi
cluster_kabkot_7 <- cluster_kabkot_7 %>%
  arrange(Cluster, KabKot)

cluster_kabkot_7
##                KabKot Cluster
## 1         Kab. Ciamis       1
## 2     Kab. Majalengka       1
## 3       Kab. Sumedang       1
## 4         Kota Bekasi       1
## 5          Kota Depok       1
## 6      Kab. Indramayu       2
## 7        Kota Bandung       2
## 8    Kota Tasikmalaya       2
## 9  Kab. Bandung Barat       3
## 10         Kab. Garut       3
## 11        Kota Banjar       3
## 12        Kab. Bekasi       4
## 13   Kab. Pangandaran       4
## 14       Kab. Cianjur       5
## 15      Kab. Karawang       5
## 16        Kab. Subang       5
## 17      Kab. Sukabumi       5
## 18        Kota Cimahi       5
## 19       Kab. Bandung       6
## 20         Kab. Bogor       6
## 21      Kab. Kuningan       6
## 22    Kab. Purwakarta       6
## 23   Kab. Tasikmalaya       6
## 24         Kota Bogor       6
## 25      Kota Sukabumi       6
## 26       Kab. Cirebon       7
## 27       Kota Cirebon       7

3.2.1 Lihat jumlah anggota per cluster

table(cluster_kabkot_7$Cluster)
## 
## 1 2 3 4 5 6 7 
## 5 3 3 2 5 7 2

3.2.2 Visualisasi Cluster dengan PCA

library(factoextra)

fviz_cluster(
  km_7,
  data  = X_scaled,
  geom  = "point",
  ellipse.type = "norm",
  repel = TRUE,
  main  = "Cluster 7 Kelompok\nKab/Kota Jawa Barat 2025 (Produk Hewani, K-Means)"
) +
  theme_minimal()
## Too few points to calculate an ellipse
## Too few points to calculate an ellipse
## Too few points to calculate an ellipse
## Too few points to calculate an ellipse

“Berdasarkan grafik PCA, terlihat bahwa pemisahan antar cluster cukup jelas, terutama pada Dimensi 1 (55.4%) yang menggambarkan tingkat harga produk hewani secara umum. Kabupaten/kota yang berada di sisi kanan grafik cenderung memiliki harga daging dan telur yang lebih tinggi, yang dikelompokkan dalam Cluster 1 dan Cluster 2. Sementara itu, wilayah yang berada di sisi kiri grafik—yang sebagian besar masuk dalam Cluster 4 dan Cluster 7—memiliki tingkat harga produk hewani yang lebih rendah.

Dimensi 2 (32.3%) memisahkan kabupaten/kota berdasarkan struktur harga antar komoditas, khususnya perbedaan pola harga antara daging sapi, daging ayam, dan telur ayam ras. Dengan demikian, kombinasi dua dimensi PCA mampu menjelaskan 87.7% keragaman data, menunjukkan bahwa pembentukan tujuh cluster melalui K-Means menghasilkan pengelompokan yang stabil dan terinterpretasi dengan baik.”

3.3 Profil Cluster (Rata-rata Harga Hewani)

profil_cluster_7 <- kab_produk_hewani_jabar %>%
  left_join(cluster_kabkot_7, by = "KabKot") %>%
  group_by(Cluster) %>%
  summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE)))

profil_cluster_7
## # A tibble: 7 × 4
##   Cluster `Daging Ayam Ras` `Daging Sapi Murni` `Telur Ayam Ras`
##   <fct>               <dbl>               <dbl>            <dbl>
## 1 1                  39060.             136487.           28442.
## 2 2                  35201.             141531.           28230.
## 3 3                  32565.             125724.           28052.
## 4 4                  40054.             131491.           29363.
## 5 5                  35390.             131824.           28781.
## 6 6                  35385.             129889.           28135.
## 7 7                  31864.             132948.           27428.
  • Cluster 1: Harga ayam dan telur tinggi, sapi menengah
  • Cluster 2: Harga daging tertinggi
  • Cluster 3: Harga paling terendah (ayam dan sapi termurah)
  • Cluster 4: Harga paling mahal untuk semua produk hewani
  • Cluster 5: Harga menengah–tinggi (stabil ke arah tinggi)
  • Cluster 6: Harga menengah (normal)
  • Cluster 7: Harga telur paling murah, ayam rendah, sapi menengah

3.4 Silhoutte Score

library(cluster)

sil_7 <- silhouette(km_7$cluster, dist(X_scaled))
summary(sil_7)
## Silhouette of 27 units in 7 clusters from silhouette.default(x = km_7$cluster, dist = dist(X_scaled)) :
##  Cluster sizes and average silhouette widths:
##         5         3         3         2         5         7         2 
## 0.4126583 0.3425422 0.4052472 0.1256450 0.2663435 0.3793063 0.4914122 
## Individual silhouette widths:
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.02807 0.27336 0.38139 0.35288 0.47488 0.61904

3.5 Clustering dengan K-Medoids k = 7

library(cluster)
library(factoextra)

set.seed(123)
pam_7 <- pam(X_scaled, k = 7)

3.5.1 Hasil cluster

table(pam_7$clustering)
## 
## 1 2 3 4 5 6 7 
## 5 3 6 3 2 3 5

3.5.2 Visualisasi PCA K-Medoids

fviz_cluster(
  pam_7,
  geom = "point",
  ellipse.type = "norm",
  repel = TRUE,
  main = "Clustering K-Medoids (k = 7) - Kab/Kota Jabar 2025"
)
## Too few points to calculate an ellipse
## Too few points to calculate an ellipse
## Too few points to calculate an ellipse
## Too few points to calculate an ellipse

3.5.3 Profil Cluster K-Medoids

# Buat dataframe hasil cluster PAM
cluster_pam_7 <- data.frame(
  KabKot  = kabkot,
  Cluster = factor(pam_7$clustering)
)

# Hitung rata-rata harga produk hewani untuk setiap cluster
profil_cluster_pam7 <- kab_produk_hewani_jabar %>%
  left_join(cluster_pam_7, by = "KabKot") %>%
  group_by(Cluster) %>%
  summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE))) %>%
  arrange(Cluster)

profil_cluster_pam7
## # A tibble: 7 × 4
##   Cluster `Daging Ayam Ras` `Daging Sapi Murni` `Telur Ayam Ras`
##   <fct>               <dbl>               <dbl>            <dbl>
## 1 1                  35303.             129387.           28061.
## 2 2                  32565.             125724.           28052.
## 3 3                  39479              135896.           28546.
## 4 4                  36079.             130700.           29255.
## 5 5                  31864.             132948.           27428.
## 6 6                  35201.             141531.           28230.
## 7 7                  35685.             131869.           28487.

3.5.4 Silhouette Score K-Medoids

sil_pam7 <- silhouette(pam_7$clustering, dist(X_scaled))
summary(sil_pam7)
## Silhouette of 27 units in 7 clusters from silhouette.default(x = pam_7$clustering, dist = dist(X_scaled)) :
##  Cluster sizes and average silhouette widths:
##          5          3          6          3          2          3          5 
## 0.23251163 0.37212633 0.25571152 0.01690787 0.48335611 0.37968385 0.35604491 
## Individual silhouette widths:
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## -0.2184  0.2333  0.3444  0.2870  0.4125  0.5565

4 Tabel Perbandingan K-Means vs K-Medoids

5 Tabel Gabungan K-Means

library(dplyr)
library(factoextra)

# Silhouette per cluster dari K-Means (k = 7)
sil_km_df <- fviz_silhouette(sil_7)$data %>%
  group_by(cluster) %>%
  summarise(
    size          = n(),
    ave.sil.width = mean(sil_width)
  )
##   cluster size ave.sil.width
## 1       1    5          0.41
## 2       2    3          0.34
## 3       3    3          0.41
## 4       4    2          0.13
## 5       5    5          0.27
## 6       6    7          0.38
## 7       7    2          0.49
# Gabungkan dengan profil cluster
tabel_kmeans <- profil_cluster_7 %>%
  rename(cluster = Cluster) %>%
  left_join(sil_km_df, by = "cluster") %>%
  mutate(Metode = "K-Means") %>%
  select(Metode, cluster, size, ave.sil.width,
         `Daging Ayam Ras`, `Daging Sapi Murni`, `Telur Ayam Ras`)

tabel_kmeans
## # A tibble: 7 × 7
##   Metode  cluster  size ave.sil.width `Daging Ayam Ras` `Daging Sapi Murni`
##   <chr>   <fct>   <int>         <dbl>             <dbl>               <dbl>
## 1 K-Means 1           5         0.413            39060.             136487.
## 2 K-Means 2           3         0.343            35201.             141531.
## 3 K-Means 3           3         0.405            32565.             125724.
## 4 K-Means 4           2         0.126            40054.             131491.
## 5 K-Means 5           5         0.266            35390.             131824.
## 6 K-Means 6           7         0.379            35385.             129889.
## 7 K-Means 7           2         0.491            31864.             132948.
## # ℹ 1 more variable: `Telur Ayam Ras` <dbl>

5.1 Tabel Gabungan K-Medoids

# Silhouette per cluster dari K-Medoids (k = 7)
sil_pam_df <- fviz_silhouette(sil_pam7)$data %>%
  group_by(cluster) %>%
  summarise(
    size          = n(),
    ave.sil.width = mean(sil_width)
  )
##   cluster size ave.sil.width
## 1       1    5          0.23
## 2       2    3          0.37
## 3       3    6          0.26
## 4       4    3          0.02
## 5       5    2          0.48
## 6       6    3          0.38
## 7       7    5          0.36
# Gabungkan dengan profil cluster PAM
tabel_pam <- profil_cluster_pam7 %>%
  rename(cluster = Cluster) %>%
  left_join(sil_pam_df, by = "cluster") %>%
  mutate(Metode = "K-Medoids") %>%
  select(Metode, cluster, size, ave.sil.width,
         `Daging Ayam Ras`, `Daging Sapi Murni`, `Telur Ayam Ras`)

tabel_pam
## # A tibble: 7 × 7
##   Metode    cluster  size ave.sil.width `Daging Ayam Ras` `Daging Sapi Murni`
##   <chr>     <fct>   <int>         <dbl>             <dbl>               <dbl>
## 1 K-Medoids 1           5        0.233             35303.             129387.
## 2 K-Medoids 2           3        0.372             32565.             125724.
## 3 K-Medoids 3           6        0.256             39479              135896.
## 4 K-Medoids 4           3        0.0169            36079.             130700.
## 5 K-Medoids 5           2        0.483             31864.             132948.
## 6 K-Medoids 6           3        0.380             35201.             141531.
## 7 K-Medoids 7           5        0.356             35685.             131869.
## # ℹ 1 more variable: `Telur Ayam Ras` <dbl>

5.2 Tabel Perbandingan

tabel_perbandingan <- bind_rows(tabel_kmeans, tabel_pam) %>%
  arrange(Metode, cluster)

tabel_perbandingan
## # A tibble: 14 × 7
##    Metode    cluster  size ave.sil.width `Daging Ayam Ras` `Daging Sapi Murni`
##    <chr>     <fct>   <int>         <dbl>             <dbl>               <dbl>
##  1 K-Means   1           5        0.413             39060.             136487.
##  2 K-Means   2           3        0.343             35201.             141531.
##  3 K-Means   3           3        0.405             32565.             125724.
##  4 K-Means   4           2        0.126             40054.             131491.
##  5 K-Means   5           5        0.266             35390.             131824.
##  6 K-Means   6           7        0.379             35385.             129889.
##  7 K-Means   7           2        0.491             31864.             132948.
##  8 K-Medoids 1           5        0.233             35303.             129387.
##  9 K-Medoids 2           3        0.372             32565.             125724.
## 10 K-Medoids 3           6        0.256             39479              135896.
## 11 K-Medoids 4           3        0.0169            36079.             130700.
## 12 K-Medoids 5           2        0.483             31864.             132948.
## 13 K-Medoids 6           3        0.380             35201.             141531.
## 14 K-Medoids 7           5        0.356             35685.             131869.
## # ℹ 1 more variable: `Telur Ayam Ras` <dbl>

Berdasarkan hasil perbandingan Silhouette Score:

Metode K-Means menunjukkan kualitas klasterisasi yang lebih baik daripada K-Medoids. K-Means menghasilkan nilai silhouette yang lebih tinggi dan lebih konsisten antar cluster (0.34–0.49). Sedangkan K-Medoids memiliki variasi yang lebih ekstrem, termasuk satu cluster dengan kualitas sangat rendah (0.016).

Hal ini menunjukkan bahwa struktur data harga produk hewani Kabupaten/Kota di Jawa Barat tahun 2025 lebih sesuai dikelompokkan menggunakan algoritma berbasis centroid daripada algoritma berbasis medoid. Oleh karena itu, K-Means dipilih sebagai metode utama dalam penelitian ini.

6 Identifikasi Masalah dari Data Hasil Analisis

6.1 Ketimpangan Harga Produk Hewani Antar Kabupaten/Kota di Jawa Barat Tinggi

Hasil clustering (7 cluster) menunjukkan:

  • Ada kelompok wilayah yang konsisten memiliki harga sangat tinggi (Cluster 1, Cluster 2, dan Cluster 4)
  • Ada wilayah dengan harga sangat rendah (Cluster 3 dan sebagian Cluster 7)
  • Ketimpangan harga ini cukup ekstrem, terutama pada daging sapi

Implikasi masalah: Kesenjangan daya beli masyarakat antar kabupaten/kota dan ketidakmerataan distribusi pasokan hewani.

6.2 Harga Daging Sapi Sangat Tidak Stabil dan Paling Berbeda Antar Wilayah

Daging sapi menunjukkan variasi terbesar dibanding ayam dan telur:

  • Cluster 2: harga sapi paling tinggi (141 ribu)
  • Cluster 3: harga sapi paling rendah (125 ribu)
  • Selisih bisa mencapai >15 ribu hanya antar kabupaten/kota

Ini mengindikasikan ketidakmerataan distribusi daging sapi, termasuk ongkos logistik berbeda dan perbedaan rantai pasok

6.3 Ada Wilayah dengan Harga Sangat Rendah Namun Tidak Merata

  • Cluster 3 dan 7: Harga murah tidak berlaku untuk semua komoditas
  • Ada kabupaten/kota yang hanya murah untuk ayam tapi tidak untuk daging sapi
  • Ada kabupaten/kota yang hanya murah untuk ayam tapi tidak untuk daging sapi
  • Hanya telur yang sangat murah

Ini mengindikasikan adanya ketidakseimbangan komposisi produksi ternak antar daerah, Pusat produksi telur berbeda dengan pusat produksi ayam atau sapi, dan kebijakan harus dilakukan berdasarkan komoditas, bukan hanya wilayah.