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"
  )

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

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

ggplot(kab_long,
       aes(x = KabKot, y = Harga, fill = Produk)) +
  geom_col(position = "dodge") +   # ganti "stack" kalau mau ditumpuk
  coord_flip() +
  labs(
    title = "Harga komoditas hewani per kabupaten/kota\nJawa Barat",
    x = "Kabupaten/Kota",
    y = "Harga"
  )

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",
    x = "Produk",
    y = "Rata-rata harga"
  )

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",
    x = "Produk",
    y = "Harga"
  )

ggplot(kab_long,
       aes(x = KabKot, y = Harga)) +
  geom_boxplot() +
  coord_flip() +
  labs(
    title = "Distribusi harga komoditas hewani\nper kabupaten/kota di Jawa Barat",
    x = "Kabupaten/Kota",
    y = "Harga"
  )

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,     # jumlah cluster maksimum yang diuji
  B = 100         # jumlah simulasi bootstrap (lebih besar = lebih akurat)
)

# 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 = 7 karena memiliki gap tertinggi 0.08461061.

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 menangah-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.
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, dan (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) dan 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 menunjukkan bahwa: - Harga murah tidak berlaku untuk semua komoditas - Ada kabupaten/kota yang hanya murah untuk ayam tapi tidak untuk daging sapi - Atau 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.