Analisis Perbandingan Klaster Harga Beras Antaprovinsi Tahun 2024 Menggunakan Metode K-Means dan K-Medoids

Tengku Muhammad Hafizh Fadhlah (G1401231063)

A. Library

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(readxl)
library(tidyr)    
library(ggplot2)    
library(cluster)     
## Warning: package 'cluster' was built under R version 4.5.2
library(factoextra) 
## Warning: package 'factoextra' was built under R version 4.5.2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(NbClust)     
## Warning: package 'NbClust' was built under R version 4.5.2
library(readr)       
library(reshape2) 
## 
## Attaching package: 'reshape2'
## The following object is masked from 'package:tidyr':
## 
##     smiths
library(fmsb)  
## Warning: package 'fmsb' was built under R version 4.5.2
library(cluster)
library(tibble)

B. Persiapan dan Import Data

df <- read_excel("C:\\Users\\Hafizh Fadhlah\\OneDrive\\Documents\\0_df_long.xlsx")
str(df)
## tibble [340,620 × 9] (S3: tbl_df/tbl/data.frame)
##  $ KabKot  : chr [1:340620] "Kab. Aceh Barat" "Kab. Aceh Barat" "Kab. Aceh Barat" "Kab. Aceh Barat" ...
##  $ Tahun   : num [1:340620] 2022 2022 2022 2022 2022 ...
##  $ Bulan   : chr [1:340620] "Januari" "Januari" "Januari" "Januari" ...
##  $ Produk  : chr [1:340620] "Beras Premium" "Beras Medium" "Bawang Merah" "Bawang Putih (Bonggol)" ...
##  $ Harga   : chr [1:340620] "11429" "9979" "31000" "28636" ...
##  $ Kategori: chr [1:340620] "Beras" "Beras" "Bawang" "Bawang" ...
##  $ KodeBPS : chr [1:340620] "11.05" "11.05" "11.05" "11.05" ...
##  $ KodeProv: num [1:340620] 11 11 11 11 11 11 11 11 11 11 ...
##  $ NamaProv: chr [1:340620] "Aceh" "Aceh" "Aceh" "Aceh" ...
head(df)
## # A tibble: 6 × 9
##   KabKot          Tahun Bulan   Produk  Harga Kategori KodeBPS KodeProv NamaProv
##   <chr>           <dbl> <chr>   <chr>   <chr> <chr>    <chr>      <dbl> <chr>   
## 1 Kab. Aceh Barat  2022 Januari Beras … 11429 Beras    11.05         11 Aceh    
## 2 Kab. Aceh Barat  2022 Januari Beras … 9979  Beras    11.05         11 Aceh    
## 3 Kab. Aceh Barat  2022 Januari Bawang… 31000 Bawang   11.05         11 Aceh    
## 4 Kab. Aceh Barat  2022 Januari Bawang… 28636 Bawang   11.05         11 Aceh    
## 5 Kab. Aceh Barat  2022 Januari Cabai … 19409 Cabai    11.05         11 Aceh    
## 6 Kab. Aceh Barat  2022 Januari Cabai … 45706 Cabai    11.05         11 Aceh

C. Filtering Data Beras Tahun 2024

Data yang akan digunakan pada analisis ini hanya data beras di setiap provinsi pada tahun 2024

df_beras <- df %>%
  filter(Tahun == 2024,
         grepl("Beras", Kategori, ignore.case = TRUE))
df_beras
## # A tibble: 11,964 × 9
##    KabKot          Tahun Bulan   Produk Harga Kategori KodeBPS KodeProv NamaProv
##    <chr>           <dbl> <chr>   <chr>  <chr> <chr>    <chr>      <dbl> <chr>   
##  1 Kab. Aceh Barat  2024 Januari Beras… 14655 Beras    11.05         11 Aceh    
##  2 Kab. Aceh Barat  2024 Januari Beras… 12811 Beras    11.05         11 Aceh    
##  3 Kab. Aceh Barat  2024 Februa… Beras… 14658 Beras    11.05         11 Aceh    
##  4 Kab. Aceh Barat  2024 Februa… Beras… 13233 Beras    11.05         11 Aceh    
##  5 Kab. Aceh Barat  2024 Maret   Beras… 14631 Beras    11.05         11 Aceh    
##  6 Kab. Aceh Barat  2024 Maret   Beras… 13249 Beras    11.05         11 Aceh    
##  7 Kab. Aceh Barat  2024 April   Beras… 14670 Beras    11.05         11 Aceh    
##  8 Kab. Aceh Barat  2024 April   Beras… 13104 Beras    11.05         11 Aceh    
##  9 Kab. Aceh Barat  2024 Mei     Beras… 14797 Beras    11.05         11 Aceh    
## 10 Kab. Aceh Barat  2024 Mei     Beras… 13532 Beras    11.05         11 Aceh    
## # ℹ 11,954 more rows
length(df)
## [1] 9

D. Penanganan Missing Value (Imputasi NA)

df_beras <- df_beras %>%
  group_by(NamaProv) %>%
  mutate(Harga = ifelse(is.na(Harga),
                        mean(Harga, na.rm = TRUE),
                        Harga)) %>%
  ungroup()

Penanganan missing value yang dilakukan adalah mengganti nilai NA pada variabel harga sebuah provinsi dengan nilai rata-rata harga beras pada provinsi tersebut.

sum(is.na(df_beras$Harga))
## [1] 0

Dilakukan pengecekan kembali untuk memastikan tidak ada lagi nilai NA dan terbukti jumlah nilai NA adalah 0

E. Pembersihan Format Harga

df_beras <- df_beras %>%
  mutate(Harga = gsub("[^0-9]", "", Harga),   # hapus karakter non-digit
         Harga = as.numeric(Harga))
str(df_beras$Harga)
##  num [1:11964] 14655 12811 14658 13233 14631 ...

Dilakukan pembersihan pada kolom Harga agar semua data menjadi numerik. Data-data yang memiliki simbol atau huruf akan dibersihkan dan menyisakan numeriknya saja

F. Menghitung Rata-Rata Harga per Provinsi

df_cluster <- df_beras %>%
  group_by(NamaProv) %>%
  summarise(Rata_Harga = mean(Harga, na.rm = TRUE))
df_cluster
## # A tibble: 34 × 2
##    NamaProv      Rata_Harga
##    <chr>              <dbl>
##  1 Aceh              13975.
##  2 Bali              14765.
##  3 Banten            13979.
##  4 Bengkulu          14410.
##  5 DI Yogyakarta     13995.
##  6 DKI Jakarta       14952.
##  7 Gorontalo         14631 
##  8 Jambi             13972.
##  9 Jawa Barat        14072.
## 10 Jawa Tengah       14153.
## # ℹ 24 more rows

Pada perhitungan rata-rata di atas, terdapat satu provinsi yang bernama NA. Setelah dicek satu persatu sesuai dengan data 34 provinsi pertama di Indonesia, provinsi yang NA tersebut kemungkinan adalah Kalimantan Utara

df_cluster %>% filter(is.na(NamaProv))
## # A tibble: 0 × 2
## # ℹ 2 variables: NamaProv <chr>, Rata_Harga <dbl>
df_cluster %>% filter(NamaProv == "NA")
## # A tibble: 1 × 2
##   NamaProv Rata_Harga
##   <chr>         <dbl>
## 1 NA           16422.

Kembali dicek juga apakah masih ada nilai na pada data dan hasilnya adalah 0. Namun, terdapat data yang dinamakan NA dengan rata-rata harga 16422.42

df_cluster2 <- df_cluster %>%
  mutate(NamaProv = if_else(NamaProv == "NA",
                            "Kalimantan Utara",
                            NamaProv))
df_cluster2
## # A tibble: 34 × 2
##    NamaProv      Rata_Harga
##    <chr>              <dbl>
##  1 Aceh              13975.
##  2 Bali              14765.
##  3 Banten            13979.
##  4 Bengkulu          14410.
##  5 DI Yogyakarta     13995.
##  6 DKI Jakarta       14952.
##  7 Gorontalo         14631 
##  8 Jambi             13972.
##  9 Jawa Barat        14072.
## 10 Jawa Tengah       14153.
## # ℹ 24 more rows

Kemudian, data yang dinamakan NA tadi diganti dengan Kalimantan Utara yang sebelumnya tidak ada di kolom NamaProv

df_clberas <- df_beras %>% 
  group_by(NamaProv) %>% 
  summarise(Rata_Harga = mean(Harga, na.rm = TRUE)) %>% 
  arrange(Rata_Harga) 
df_clharga <- df_cluster2 %>% 
  select(Rata_Harga) 
df_clberas 
## # A tibble: 34 × 2
##    NamaProv          Rata_Harga
##    <chr>                  <dbl>
##  1 Sumatera Selatan      13470.
##  2 Jawa Timur            13588.
##  3 Sulawesi Selatan      13734.
##  4 Sulawesi Tenggara     13894.
##  5 Lampung               13911.
##  6 Sulawesi Barat        13961.
##  7 Jambi                 13972.
##  8 Aceh                  13975.
##  9 Banten                13979.
## 10 DI Yogyakarta         13995.
## # ℹ 24 more rows
df_clharga
## # A tibble: 34 × 1
##    Rata_Harga
##         <dbl>
##  1     13975.
##  2     14765.
##  3     13979.
##  4     14410.
##  5     13995.
##  6     14952.
##  7     14631 
##  8     13972.
##  9     14072.
## 10     14153.
## # ℹ 24 more rows
df_scaled <- scale(df_clharga)
df_scaled
##        Rata_Harga
##  [1,] -0.71719091
##  [2,] -0.16549073
##  [3,] -0.71434467
##  [4,] -0.41350305
##  [5,] -0.70325164
##  [6,] -0.03472931
##  [7,] -0.25895436
##  [8,] -0.71941870
##  [9,] -0.64932925
## [10,] -0.59273542
## [11,] -0.98767212
## [12,]  0.62467360
## [13,] -0.23457504
## [14,]  0.58249274
## [15,]  1.06425289
## [16,]  0.21825284
## [17,]  0.12651147
## [18,] -0.76198377
## [19,]  0.83382949
## [20,]  1.17168531
## [21,]  0.99218896
## [22,] -0.61938172
## [23,] -0.02330028
## [24,]  1.36288200
## [25,]  4.11994311
## [26,] -0.02576411
## [27,] -0.72708089
## [28,] -0.88540703
## [29,] -0.33141406
## [30,] -0.77352816
## [31,] -0.29871699
## [32,]  0.88705760
## [33,] -1.06997921
## [34,] -0.27601859
## attr(,"scaled:center")
## Rata_Harga 
##   15001.78 
## attr(,"scaled:scale")
## Rata_Harga 
##    1431.83

G. Menentukan Jumlah Cluster (Elbow Method)

fviz_nbclust(df_scaled, kmeans, method = "wss") +
  ggtitle("Elbow Method untuk Menentukan Jumlah Cluster")

Dilihat dari grafik di atas, kemungkinan nilai k yang digunakan adalah k = 4. Angka tersebut diambil ketika garis grafik mulai melandai yang menandakan angka setelahnya akan menimbulkan masalah jika clusternya terlalu banyak

H. Clustering K-Means (coba K = 4, K = 3, dan K = 5)

K-Means dengan K = 4

k1 <- 4
set.seed(123)
km_model1 <- kmeans(df_clharga, centers = k1, nstart = 25)
km_model1$cluster
##  [1] 1 4 1 4 1 4 4 1 1 1 1 3 4 3 3 4 4 1 3 3 3 1 4 3 2 4 1 1 4 1 4 3 1 4
sil1 <- silhouette(km_model1$cluster, dist(df_clharga))
mean(sil1[, 3])
## [1] 0.6596401

K-Means dengan K = 3

k2 <- 3
set.seed(123)
km_model2 <- kmeans(df_clharga, centers = k2, nstart = 25)
km_model2$cluster
##  [1] 1 1 1 1 1 1 1 1 1 1 1 3 1 3 3 3 1 1 3 3 3 1 1 3 2 1 1 1 1 1 1 3 1 1
sil2 <- silhouette(km_model2$cluster, dist(df_clharga))
mean(sil2[, 3])
## [1] 0.6560839

K-Means dengan K = 5

k3 <- 5
set.seed(123)
km_model3 <- kmeans(df_clharga, centers = k3, nstart = 25)
km_model3$cluster
##  [1] 5 1 5 1 5 1 1 5 5 5 5 4 1 4 3 4 4 5 3 3 3 5 1 3 2 1 5 5 1 5 1 3 5 1
sil3 <- silhouette(km_model3$cluster, dist(df_clharga))
mean(sil3[, 3])
## [1] 0.5996905

Kemudian, dilakukan pengecekan dengan silhoutte score untuk melihat nilai tertinggi pada K yang dipilih. Dapat dilihat bahwa nili k = 4 lebih baik diantara 2 nilai yang mengapitnya, yaitu K = 3 dan 5. Hasil ini menunjukkan K = 4 menunjukkan kualitas cluster terbaik dengan nilai 0.6596401

plot(sil1,
     border = NA,
     main = "Plot Silhouette – K-Means Clustering")

Dapat dilihat juga plot dari silhoutte score. Untuk cluster 1, skornya = 0.76 Untuk cluster 2, skornya= 0.00 dikarenakan hanya ada 1 provinsi. Untuk cluster 3, skornya = 0.68. Dan untuk cluster 4, skornya 0.59. Plot ini berfungsi untuk melihat seberapa rapat data terhadap clusternya masing-masing dan seberapa jauh data tersebut dengan cluster lain

I. Visualisasi Cluster K-Means (Barplot)

df_cluster2$cluster <- as.factor(km_model1$cluster)
ggplot(df_cluster2, aes(x = reorder(NamaProv, Rata_Harga), y = Rata_Harga, fill = cluster)) +
  geom_col() +
  coord_flip() +
  scale_y_continuous(breaks = seq(0, 22000, by = 2000)) +
  labs(title = "Cluster Harga Beras 2024 - Bar Plot")

ggplot(df_cluster2, aes(x = Rata_Harga, 
                        y = cluster, 
                        color = cluster)) +
  geom_point(size = 3) +
  labs(title = "K-Means Clustering Berdasarkan Harga Beras 2024",
       x = "Rata-rata Harga Beras",
       y = "Cluster") +
  theme_minimal()

library(dplyr)

range_kmeans <- df_cluster2 %>%
  group_by(cluster) %>%
  summarise(
    min_harga = min(Rata_Harga, na.rm = TRUE),
    max_harga = max(Rata_Harga, na.rm = TRUE),
    mean_harga = mean(Rata_Harga, na.rm = TRUE),
    median_harga = median(Rata_Harga, na.rm = TRUE),
    .groups = "drop"
  )

range_kmeans
## # A tibble: 4 × 5
##   cluster min_harga max_harga mean_harga median_harga
##   <fct>       <dbl>     <dbl>      <dbl>        <dbl>
## 1 1          13470.    14153.     13909.       13972.
## 2 2          20901.    20901.     20901.       20901.
## 3 3          15836.    16953.     16348.       16347.
## 4 4          14410.    15314.     14797.       14715.

Cluster 1 (Merah) menunjukkan cluster dengan harga rendah, cluster 2 (Hijau) menunjukkan cluster dengan harga sangat tinggi, cluster 3 (Biru) menunjukkan harga cukup tinggi, dan cluster 4 (Ungu) menunjukkan harga menengah. Batas atas dan batas bawah masing-masing cluster juga dapat dilihat pada tebel yang ada diatas, dengan cluster 1 13469.75 sampai 14153.08, cluster 2 20900.84 (karena hanya 1 provinsi), cluster 3 15835.81 sampai 16953.19, dan cluster 4 14409.71 sampai 15314.28

J. Clustering Menggunakan K-Medoids (PAM)

fviz_nbclust(df_clharga, pam, method = "silhouette")

pam_model <- pam(df_scaled, k = 4)
df_cluster2$Cluster_PAM <- as.factor(pam_model$clustering)
sil_pam <- silhouette(pam_model)
sil_pam
##    cluster neighbor sil_width
## 27       1        2 0.8263118
## 8        1        2 0.8251105
## 1        1        2 0.8241083
## 18       1        2 0.8220078
## 3        1        2 0.8219862
## 30       1        2 0.8176369
## 5        1        2 0.8102085
## 28       1        2 0.7572002
## 9        1        2 0.7278501
## 11       1        2 0.6957827
## 22       1        2 0.6635740
## 33       1        2 0.6413946
## 10       1        2 0.5893063
## 6        2        1 0.7291645
## 26       2        1 0.7280363
## 23       2        1 0.7271255
## 2        2        1 0.7096894
## 13       2        1 0.6717480
## 7        2        1 0.6470859
## 34       2        1 0.6219865
## 17       2        3 0.6178283
## 31       2        1 0.5768562
## 29       2        1 0.4897361
## 16       2        3 0.4536691
## 4        2        1 0.1565305
## 21       3        2 0.7907525
## 15       3        2 0.7861886
## 32       3        2 0.7693989
## 20       3        2 0.7569682
## 19       3        2 0.7412686
## 24       3        2 0.6790040
## 12       3        2 0.5151299
## 14       3        2 0.4371192
## 25       4        3 0.0000000
## attr(,"Ordered")
## [1] TRUE
## attr(,"call")
## pam(x = df_scaled, k = 4)
## attr(,"class")
## [1] "silhouette"
mean(sil_pam[,3])
## [1] 0.6596401

Sama seperti K-Means, nilai K yang digunakan untuk K-Medoids adalah K = 4

K. Visualisasi Cluster PAM (Barplot)

ggplot(df_cluster2, aes(x = reorder(NamaProv, Rata_Harga), 
                       y = Rata_Harga, 
                       fill = Cluster_PAM)) +
  geom_col() +
  coord_flip() +
  labs(title = "Visualisasi Cluster – K-Medoids (Barplot)",
       x = "Provinsi",
       y = "Harga Beras 2024") + scale_y_continuous(breaks = seq(0, 22000, by = 2000))

  theme_minimal()
## List of 136
##  $ line                            :List of 6
##   ..$ colour       : chr "black"
##   ..$ linewidth    : num 0.5
##   ..$ linetype     : num 1
##   ..$ lineend      : chr "butt"
##   ..$ arrow        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_line" "element"
##  $ rect                            :List of 5
##   ..$ fill         : chr "white"
##   ..$ colour       : chr "black"
##   ..$ linewidth    : num 0.5
##   ..$ linetype     : num 1
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_rect" "element"
##  $ text                            :List of 11
##   ..$ family       : chr ""
##   ..$ face         : chr "plain"
##   ..$ colour       : chr "black"
##   ..$ size         : num 11
##   ..$ hjust        : num 0.5
##   ..$ vjust        : num 0.5
##   ..$ angle        : num 0
##   ..$ lineheight   : num 0.9
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ title                           : NULL
##  $ aspect.ratio                    : NULL
##  $ axis.title                      : NULL
##  $ axis.title.x                    :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 2.75points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.x.top                :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 0
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 2.75points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.x.bottom             : NULL
##  $ axis.title.y                    :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : num 90
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 2.75points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.y.left               : NULL
##  $ axis.title.y.right              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : num -90
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 2.75points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text                       :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : chr "grey30"
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x                     :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 2.2points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x.top                 :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 0
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 2.2points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x.bottom              : NULL
##  $ axis.text.y                     :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 1
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 2.2points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.y.left                : NULL
##  $ axis.text.y.right               :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 2.2points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.theta                 : NULL
##  $ axis.text.r                     :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0.5
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 2.2points 0points 2.2points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.ticks                      : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ axis.ticks.x                    : NULL
##  $ axis.ticks.x.top                : NULL
##  $ axis.ticks.x.bottom             : NULL
##  $ axis.ticks.y                    : NULL
##  $ axis.ticks.y.left               : NULL
##  $ axis.ticks.y.right              : NULL
##  $ axis.ticks.theta                : NULL
##  $ axis.ticks.r                    : NULL
##  $ axis.minor.ticks.x.top          : NULL
##  $ axis.minor.ticks.x.bottom       : NULL
##  $ axis.minor.ticks.y.left         : NULL
##  $ axis.minor.ticks.y.right        : NULL
##  $ axis.minor.ticks.theta          : NULL
##  $ axis.minor.ticks.r              : NULL
##  $ axis.ticks.length               : 'simpleUnit' num 2.75points
##   ..- attr(*, "unit")= int 8
##  $ axis.ticks.length.x             : NULL
##  $ axis.ticks.length.x.top         : NULL
##  $ axis.ticks.length.x.bottom      : NULL
##  $ axis.ticks.length.y             : NULL
##  $ axis.ticks.length.y.left        : NULL
##  $ axis.ticks.length.y.right       : NULL
##  $ axis.ticks.length.theta         : NULL
##  $ axis.ticks.length.r             : NULL
##  $ axis.minor.ticks.length         : 'rel' num 0.75
##  $ axis.minor.ticks.length.x       : NULL
##  $ axis.minor.ticks.length.x.top   : NULL
##  $ axis.minor.ticks.length.x.bottom: NULL
##  $ axis.minor.ticks.length.y       : NULL
##  $ axis.minor.ticks.length.y.left  : NULL
##  $ axis.minor.ticks.length.y.right : NULL
##  $ axis.minor.ticks.length.theta   : NULL
##  $ axis.minor.ticks.length.r       : NULL
##  $ axis.line                       : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ axis.line.x                     : NULL
##  $ axis.line.x.top                 : NULL
##  $ axis.line.x.bottom              : NULL
##  $ axis.line.y                     : NULL
##  $ axis.line.y.left                : NULL
##  $ axis.line.y.right               : NULL
##  $ axis.line.theta                 : NULL
##  $ axis.line.r                     : NULL
##  $ legend.background               : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.margin                   : 'margin' num [1:4] 5.5points 5.5points 5.5points 5.5points
##   ..- attr(*, "unit")= int 8
##  $ legend.spacing                  : 'simpleUnit' num 11points
##   ..- attr(*, "unit")= int 8
##  $ legend.spacing.x                : NULL
##  $ legend.spacing.y                : NULL
##  $ legend.key                      : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.key.size                 : 'simpleUnit' num 1.2lines
##   ..- attr(*, "unit")= int 3
##  $ legend.key.height               : NULL
##  $ legend.key.width                : NULL
##  $ legend.key.spacing              : 'simpleUnit' num 5.5points
##   ..- attr(*, "unit")= int 8
##  $ legend.key.spacing.x            : NULL
##  $ legend.key.spacing.y            : NULL
##  $ legend.frame                    : NULL
##  $ legend.ticks                    : NULL
##  $ legend.ticks.length             : 'rel' num 0.2
##  $ legend.axis.line                : NULL
##  $ legend.text                     :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ legend.text.position            : NULL
##  $ legend.title                    :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ legend.title.position           : NULL
##  $ legend.position                 : chr "right"
##  $ legend.position.inside          : NULL
##  $ legend.direction                : NULL
##  $ legend.byrow                    : NULL
##  $ legend.justification            : chr "center"
##  $ legend.justification.top        : NULL
##  $ legend.justification.bottom     : NULL
##  $ legend.justification.left       : NULL
##  $ legend.justification.right      : NULL
##  $ legend.justification.inside     : NULL
##  $ legend.location                 : NULL
##  $ legend.box                      : NULL
##  $ legend.box.just                 : NULL
##  $ legend.box.margin               : 'margin' num [1:4] 0cm 0cm 0cm 0cm
##   ..- attr(*, "unit")= int 1
##  $ legend.box.background           : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.box.spacing              : 'simpleUnit' num 11points
##   ..- attr(*, "unit")= int 8
##   [list output truncated]
##  - attr(*, "class")= chr [1:2] "theme" "gg"
##  - attr(*, "complete")= logi TRUE
##  - attr(*, "validate")= logi TRUE
ggplot(df_cluster2, aes(x = Rata_Harga,
                        y = cluster,
                        color = Cluster_PAM)) +
  geom_point(size = 3) +
  labs(title = "K-Medoids (PAM) Clustering Berdasarkan Harga Beras 2024",
       x = "Rata-rata Harga Beras",
       y = "Cluster (PAM)") +
  theme_minimal()

range_pam <- df_cluster2 %>%
  group_by(Cluster_PAM) %>%
  summarise(
    Min_Harga = min(Rata_Harga, na.rm = TRUE),
    Max_Harga = max(Rata_Harga, na.rm = TRUE),
    Avg_Harga = mean(Rata_Harga, na.rm = TRUE)
  )

range_pam
## # A tibble: 4 × 4
##   Cluster_PAM Min_Harga Max_Harga Avg_Harga
##   <fct>           <dbl>     <dbl>     <dbl>
## 1 1              13470.    14153.    13909.
## 2 2              14410.    15314.    14797.
## 3 3              15836.    16953.    16348.
## 4 4              20901.    20901.    20901.

Cluster 1 (Merah) menunjukkan cluster dengan harga rendah, cluster 2 (Hijau) menunjukkan cluster dengan harga menengah, cluster 3 (Biru) menunjukkan harga cukup tinggi, dan cluster 4 (Ungu) menunjukkan harga sangat tinggi.

L. Perbandingan Hasil K-Means dan PAM

df_cluster2$Cluster_Kmeans <- km_model1$cluster
df_cluster2$Cluster_PAM    <- pam_model$cluster
print(df_cluster2)
## # A tibble: 34 × 5
##    NamaProv      Rata_Harga cluster Cluster_PAM Cluster_Kmeans
##    <chr>              <dbl> <fct>         <int>          <int>
##  1 Aceh              13975. 1                 1              1
##  2 Bali              14765. 4                 2              4
##  3 Banten            13979. 1                 1              1
##  4 Bengkulu          14410. 4                 2              4
##  5 DI Yogyakarta     13995. 1                 1              1
##  6 DKI Jakarta       14952. 4                 2              4
##  7 Gorontalo         14631  4                 2              4
##  8 Jambi             13972. 1                 1              1
##  9 Jawa Barat        14072. 1                 1              1
## 10 Jawa Tengah       14153. 1                 1              1
## # ℹ 24 more rows

M. Interpretasi Keseluruhan

Masing-masing dari K-Means dan K-Medoids membagi cluster menjadi 4 dengan kategori, rendah (1), menengah (4), cukup tinggi (3), dan sangat tinggi (2). Pada kedua metode tersebut, terdapat beberapa provinsi yang berada di 2 cluster berbeda, contohnya adalah Kalimantan Selatan yang berada dicluster 4 pada K-Means dan berada di cluster 2 pada K-medoids. Hal tersebut dikarenakan perbedaan labeling pada kedua cluster