Deskripsi Singkat

R-Markdown ini berisi tentang tahapan melakukan analisis clustering data menggunakan metode Fuzzy C-Means Clustering. Cara kerja algoritma Fuzzy C-Means yaitu dengan menggeser pusat cluster tidak terlalu banyak dan dapat memberikan hasil cluster. Fuzzy C-Means merupakan suatu metode mengelompokkan data yang keberadaan titik datanya ditentukan berdasarkan derajat keanggotaan dengan skala antara 0 sampai 1.

Analisis ini menggunakan data dari dataset berikut (data bersumber dari data sekunder di website BPS Provinsi Jateng):
DATA PENDIDIKAN

Berdasarkan dataset tersebut, akan dilakukan clustering 34 kabupaten/kota di Provinsi Jawa Tengah berdasarkan Indikator Pendidikan dan Literasi Tahun 2024, dengan variabel yang digunakan dalam analisis sebagai berikut:
1. RLS (Rata-Rata Lama Sekolah)
2. IPLM (Indeks Pembangunan Literasi Masyarakat)
3. TKM (Tingkat Kegemaran Membaca)

Tahapan analisis clustering dengan metode Fuzzy C-Means Clustering di R sebagai berikut:
1. Memanggil Package yang dibutuhkan
2. Mengimpor data
3. Mendeskripsikan data menggunakan statistika deskriptif
4. Checking outlier dan Menangani Outlier
5. Menyamakan skala data dengan Normalisasi Min-Max
6. Melakukan uji sampel representatif menggunakan KMO
7. Melakukan uji non-multikolinearitas dengan nilai VIF
8. Melakukan analisis dengan Fuzzy C-Means Clustering
9. Melakukan validasi cluster dengan Indeks Xie Beni, Partition Coefficient, Modified Partition Coefficient
10. Evaluasi jumlah cluster optimal dan m-fuzzier terbaik
11. Profilisasi serta interpretasi karakteristik wilayah masing-masing cluster
12. Visualisasi Cluster

1. Load Package

# Memuat library yang dibutuhkan
library(readxl)      
library(dplyr)      
library(psych)      
library(car)        
library(ppclust)   
library(factoextra)
library(e1071)
library(sf)
library(geodata)
library(ggplot2)

2. Input Data

# Membaca data
data <- read_excel("D:\\SEMESTER 6\\KOMLAN\\DATA PENDIDIKAN.xlsx")
data <- as.data.frame(data)
head(data)
##   Kabupaten/Kota  RLS  IPLM   TKM
## 1        Cilacap 7.40 64.41 68.99
## 2       Banyumas 7.91 67.88 67.39
## 3    Purbalingga 7.36 70.33 63.98
## 4   Banjarnegara 6.87 73.11 63.85
## 5        Kebumen 7.87 78.81 80.77
## 6      Purworejo 8.65 78.32 70.24
# Menyimpan nama Kabupaten/Kota sebagai rownames agar mempermudah analisis
rownames(data) <- data[[1]] 

# Mengambil hanya variabel numerik (RLS, IPLM, TKM)
data_num <- data[, c("RLS", "IPLM", "TKM")]

3. Statistika Deskriptif

# Menampilkan statistika deskriptif 
summary(data_num)
##       RLS              IPLM             TKM       
##  Min.   : 6.410   Min.   : 44.56   Min.   :63.11  
##  1st Qu.: 7.380   1st Qu.: 61.75   1st Qu.:67.09  
##  Median : 7.870   Median : 66.65   Median :72.56  
##  Mean   : 8.321   Mean   : 68.82   Mean   :73.13  
##  3rd Qu.: 9.270   3rd Qu.: 74.98   3rd Qu.:78.17  
##  Max.   :11.480   Max.   :100.00   Max.   :88.43

Keterangan:
Output tersebut memberikan gambaran umum mengenai statistika deskriptif masing-masing variabel (mean, median, nilai minimum, nilai maksimum, kuartil) sehingga dapat dilihat rentang nilai tiap variabel sebelum diproses lebih lanjut.

4. Cek dan Penanganan Outlier (Mahalanobis & Winsorize)

Outlier adalah sebuah titik yang terletak sangat jauh dari rata-rata variabel random pada umumnya yang berkorelasi dengan titik tersebut. Jarak antara outlier dengan nilai variabel random lain diukur terhadap ambang batas yang diberikan, biasanya beberapa kali dari standar deviasi. Pada klasterisasi, adanya outlier dapat menghasilkan kelompok klaster yang berbeda dengan jumlah outlier biasanya sedikit.

Deteksi Outlier
Salah satu metode deteksi outlier adalah pendeteksian outlier dengan jarak Mahalanobis. Pendeteksian outlier dengan jarak Mahalanobis merupakan pendeteksian outlier multivariat dengan mempertimbangkan hubungan dua atau lebih variabel secara bersamaan. Jarak Mahalanobis adalah jarak antara titik data dengan centroid dari sekelompok data dengan memperhitungkan korelasi antarvariabel. \[D^2 = (x - \mu)^T \Sigma^{-1} (x - \mu)\] Keterangan:
\(D^2\) : Jarak Mahalanobis
\(x\) : Vektor observasi (data)
\(\mu\) : Vektor rata-rata (centroid) dari data
\(\Sigma^{-1}\) : Invers dari matriks kovarians

Data yang memiliki nilai \(D^2\) lebih besar dari nilai kritis Chi-Square (\(\chi^2\)) akan diidentifikasi sebagai outlier.

Penanganan Outlier
Salah satu metode penanganan outlier adalah dengan menggunakan Winsorization. Winsorization merupakan metode penanganan outlier dengan mengganti nilai outlier dengan persentil tertentu sehingga distribusi data dan jumlah sampel masih lengkap dan mengurangi pengaruh outlier.

# 1. Menghitung Jarak Mahalanobis
center <- colMeans(data_num)
cov_matrix <- cov(data_num)
jarak_mah <- mahalanobis(data_num, center, cov_matrix)

# 2. Menentukan nilai kritis (alpha = 0.05, df = jumlah variabel)
cutoff <- qchisq(p = 0.95, df = ncol(data_num))
outliers <- which(jarak_mah > cutoff)

cat("Batas kritis Chi-Square:", cutoff, "\n")
## Batas kritis Chi-Square: 7.814728
cat("Terdapat", length(outliers), "kabupaten/kota yang terdeteksi sebagai outlier multivariat:\n")
## Terdapat 2 kabupaten/kota yang terdeteksi sebagai outlier multivariat:
if(length(outliers) > 0) {
  print(rownames(data_num)[outliers])
}
## [1] "Karanganyar"   "Kota Magelang"
# 3. Melakukan Capping / Winsorize (Batas 5% bawah dan 95% atas)
winsorize <- function(x, lower = 0.05, upper = 0.95) {
  q_lower <- quantile(x, lower)
  q_upper <- quantile(x, upper)
  x[x < q_lower] <- q_lower
  x[x > q_upper] <- q_upper
  return(x)
}

# 4. Menerapkan winsorize pada seluruh kolom numerik
data_num <- as.data.frame(lapply(data_num, winsorize))
rownames(data_num) <- rownames(data) # Kembalikan nama baris

cat("\n--- Statistika Deskriptif Setelah Winsorize ---\n")
## 
## --- Statistika Deskriptif Setelah Winsorize ---
summary(data_num)
##       RLS              IPLM            TKM       
##  Min.   : 6.777   Min.   :45.44   Min.   :63.94  
##  1st Qu.: 7.380   1st Qu.:61.75   1st Qu.:67.09  
##  Median : 7.870   Median :66.65   Median :72.56  
##  Mean   : 8.329   Mean   :68.85   Mean   :73.05  
##  3rd Qu.: 9.270   3rd Qu.:74.98   3rd Qu.:78.17  
##  Max.   :11.304   Max.   :99.89   Max.   :84.78

Keterangan:
Berdasarkan pengecekan outlier menggunakan jarak mahalanobis, didapatkan batas kritis Chi-Square pada data sebesar 7.8147, data yang memiliki jarak mahalanobis yang lebih besar daripada nilai Chi-Square tersebut dianggap outlier. Setelah dilakukan pengujian, terdapat 2 wilayah yang terdeteksi sebagai outlier yaitu Karanganyar dan Kota Magelang. Outlier ditangani dengan menggunakan metode winsorization dengan batas bawah 5% dan batas atas 95%.

5. Normalisasi Min-Max

Normalisasi diperlukan ketika variabel-variabel dalam model memiliki satuan atau skala yang berbeda karena variabel dengan skala yang lebih besar akan memiliki pengaruh dominan dan menyebabkan terjadinya bias dalam proses analisis. Salah satu metode normalisasi yang sering digunakan adalah Normalisasi Min-Max. Normalisasi Min-Max adalah metode yang mentransformasikan nilai variabel sehingga berada dalam rentang [0,1] \[X_{norm} = \frac{X - X_{min}}{X_{max} - X_{min}}\] Keterangan:
\(X_{norm}\) : Nilai hasil normalisasi
\(X\) : Nilai data asli
\(X_{min}\) : Nilai minimum pada variabel tersebut
\(X_{max}\) : Nilai maksimum pada variabel tersebut

# Normalisasi Min Max
normalize_minmax <- function(x) {
  return ((x - min(x)) / (max(x) - min(x)))
}

data_norm <- as.data.frame(lapply(data_num, normalize_minmax))
rownames(data_norm) <- rownames(data_num)

# Cek hasil normalisasi 
summary(data_norm)
##       RLS              IPLM             TKM        
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.1332   1st Qu.:0.2996   1st Qu.:0.1514  
##  Median :0.2414   Median :0.3896   Median :0.4137  
##  Mean   :0.3428   Mean   :0.4300   Mean   :0.4371  
##  3rd Qu.:0.5507   3rd Qu.:0.5426   3rd Qu.:0.6829  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000

Keterangan:
Dikarenakan setiap variabel (RLS, IPLM, TKM) memiliki skala pengukuran yang berbeda, maka dilakukan normalisasi min-max untuk menyetarakan skala data sehingga data memiliki rentang [0,1]. Hal ini dilakukan untuk mencegah adanya bias.

6. Uji Asumsi Sampel Representatif (Uji KMO)

Data yang digunakan dalam analisis cluster harus dapat menggambarkan kondisi sebenarnya dari populasi sehingga hasil yang didapatkan dapat maksimal. Pengujian asumsi dapat dilakukan dengan uji Kaiser Mayer-Olkin (KMO). Sampel dianggap dapat mewakili populasi jika nilai KMO lebih besar dari 0,5 maka sampel mewakili populasi sehingga layak untuk dilakukan analisis cluster. \[ \text{KMO} = \frac{\sum_{j=1}^{m} \sum_{k=1, k \neq j}^{m} r^2_{x_j x_k}}{\sum_{j=1}^{m} \sum_{k=1, k \neq j}^{m} r^2_{x_j x_k} + \sum_{j=1}^{m} \sum_{k=1, k \neq j}^{m} \rho^2_{x_j x_k}} \]

Keterangan:
\(\text{KMO}\) = nilai uji Kaiser-Mayer-Olkin
\(m\) = banyak variabel
\(r^2_{x_j x_k}\) = korelasi antara variabel \(x_j\) dan \(x_k\)
\(\rho^2_{x_j x_k}\) = korelasi parsial antara variabel \(x_j\) dan \(x_k\)

# Uji KMO
kmo_result <- KMO(data_norm)
print(kmo_result)
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = data_norm)
## Overall MSA =  0.58
## MSA for each item = 
##  RLS IPLM  TKM 
## 0.55 0.55 0.80

Keterangan:
Nilai KMO untuk setiap variabel > 0.5 sehingga sampel yang digunakan sudah representatif dan mampu mewakili populasi dengan baik.

7. Uji Asumsi Non Multikolinearitas

Multikolinieritas adalah adanya hubungan linier yang sempurna atau pasti di antara beberapa atau semua variabel. Cara untuk mengidentifikasi multikolinearitas adalah menghitung nilai Variance Inflation Factor (VIF). Jika nilai VIF < 10 dianggap tidak terdapat multikolinearitas dalam data. \[ VIF_j = \frac{1}{1 - R_j^2} \]

Keterangan:
\(VIF_j\) = nilai Variance Inflation Factor (VIF) pada variabel ke-j
\(R_j^2\) = Koefisien determinasi variabel \(X_j\) pada variabel bebas lainnya dalam model
\(j\) = \(1, 2, \dots, m\)

# Uji Asumsi Non Multikolinearitas
dummy_model <- lm(rnorm(nrow(data_norm)) ~ RLS + IPLM + TKM, data = data_norm)
vif_result <- vif(dummy_model)
print(vif_result)
##      RLS     IPLM      TKM 
## 1.773937 1.818769 1.110668

Keterangan:
Nilai VIF untuk setiap variabel < 10 sehingga tidak ada hubungan linear yang sempurna antarvariabel dan asumsi non multikolinearitas terpenuhi.

8. Mencari k dan m fuzzier terbaik dan Validasi Cluster

Diperlukan uji coba dengan beberapa nilai k cluster untuk mendapatkan hasil yang optimal. Sebagian besar literatur dan jurnal penelitian menggunakan m = 2 sebagai standar agar sifat fuzzy (keanggotaan probabilistik) dari algoritma ini tetap optimal. Hasil dari beberapa k cluster kemudian divalidasi untuk mendapatkan nilai k yang optimal.

Validasi Cluster
Validasi cluster diperlukan untuk melakukan pengujian cluster yang optimal serta untuk menilai seberapa tepat suatu objek dikelompokkan dalam cluster tersebut. Beberapa validasi cluster yang dapat digunakan seperti, indeks Xie-Beni, indeks Partition Coefficient (PC), indeks Modified Partition Coefficient (MPC).
Indeks Xie-Beni
Indeks Xie-Beni didasarkan pada jarak intracluster yaitu kedekatan antar anggota di setiap cluster dan intercluster yaitu perbedaan antara suatu cluster dengan cluster lain. Banyak cluster optimum didapatkan dengan meminimalkan nilai indeks.
\[ XB = \frac{\sum_{i=1}^{n} \sum_{k=1}^{c} \left( \mu_{ik}^w \left[ \sum_{j=1}^{m} (X_{ij} - V_{kj})^2 \right] \right)}{n \min_{i \neq k} \left[ \sum_{j=1}^{m} (V_{ij} - V_{kj})^2 \right]} \] Dimana:
\(X_{ij}\) = objek data ke-i pada variabel ke-j,
\(V_{kj}\) = nilai pusat cluster ke-k pada variabel ke-j,
\(V_{ij}\) = nilai pusat cluster ke-i pada variabel ke-j,
\(\mu_{ik}\) = nilai keanggotaan objek data ke-i dengan pusat cluster ke-k
Indeks Partition Coefficient
Indeks Partition Coefficient digunakan untuk menguji nilai derajat keanggotaan, tanpa melihat nilai data yang biasanya mengandung informasi geometrik atau sebaran data. Jumlah cluster optimal diperoleh dengan melihat nilai indeks terbesar. \[ PC(c) = \frac{1}{n} \sum_{k=1}^{c} \sum_{i=1}^{n} \mu_{ik}^2 \] Indeks Modified Partition Coefficient (MPC)
Indeks Modified Partition Coefficient (MPC) merupakan modifikasi dari indeks Partition Coefficient (PC) yang mampu mengurangi perubahan yang monoton pada PC sehingga dengan diketahuinya indeks MPC maka dapat memvalidasi jumlah cluster yang tepat. Jumlah cluster optimal diperoleh dengan melihat nilai indeks terbesar. \[ MPC(c) = 1 - \frac{c}{c - 1} (1 - PC(c)) \]

# VALIDASI CLUSTER (MENCARI K DAN M TERBAIK)


eval_results <- data.frame()
data_mat <- as.matrix(data_norm)
n_data <- nrow(data_mat)

# Uji coba k dari 2 sampai 5, dengan m=2
for (k in 2:5) {
  for (m in c(2.0)) {
    
    # 1. Jalankan FCM
    fcm_eval <- cmeans(x = data_mat, centers = k, iter.max = 100, m = m)
    
    u_matrix <- fcm_eval$membership
    centroid_mat <- fcm_eval$centers
    
    # 2. Menghitung Indeks Validasi
    # A. Partition Coefficient (PC) -> Maximize (Mendekati 1 lebih baik)
    pc_val <- sum(u_matrix^2) / n_data
    
    # B. Modified Partition Coefficient (MPC) -> Maximize (Mendekati 1 lebih baik)
    mpc_val <- 1 - (k / (k - 1)) * (1 - pc_val)
    
    # C. Xie-Beni Index (XB) -> Minimize (Mendekati 0 lebih baik)
    # Hitung Pembilang
    d2 <- matrix(0, nrow = n_data, ncol = k)
    for(j in 1:k) {
      diff_mat <- sweep(data_mat, 2, centroid_mat[j, ], "-")
      d2[, j] <- rowSums(diff_mat^2)
    }
    xb_pembilang <- sum((u_matrix^m) * d2)
    
    # Hitung Penyebut
    dist_centers <- dist(centroid_mat)^2 
    xb_penyebut <- n_data * min(dist_centers)
    
    # Nilai XB Akhir
    xb_val <- xb_pembilang / xb_penyebut
    eval_results <- rbind(eval_results, data.frame(
      k = k, 
      m = m, 
      PC = pc_val,
      MPC = mpc_val,
      XB = xb_val
    ))
  }
}

print(eval_results)
##   k m        PC       MPC        XB
## 1 2 2 0.6810641 0.3621281 0.3182154
## 2 3 2 0.6589375 0.4884063 0.1979698
## 3 4 2 0.5520757 0.4027676 0.4439099
## 4 5 2 0.5119772 0.3899715 0.3828487

Keterangan:
Penentuan k dan m fuzzier terbaik dilakukan dengan melakukan algoritma Fuzzy C-Means Clustering secara berulang untuk beberapa nilai k dan m untuk mendapat hasil yang optimal. Cluster yang diuji (k) adalah k=2,3,4,5 serta m yang diuji hanya m=2 (sesuai standar literatur). Untuk memilih k terbaik digunakan validasi cluster dengan beberapa indeks,seperti Xie-Beni (XB), Partition Coefficient (PC), dan Modified Partition Coefficient (MPC). Kriteria memilih k terbaik sebagai berikut:
1. Nilai terbesar dari PC (semakin mendekati 1 semakin baik)
2. Nilai terbesar dari MPC (semakin mendekati 1 semakin baik)
3. Nilai terkecil dari XB (semakin mendekati 0 semakin baik)

Berdasarkan output validasi cluster, jumlah k-cluster terbaik adalah k=3 dengan nilai PC kedua tertinggi (0.6589), nilai MPC tertinggi (0.4884), dan nilai XB terendah (0.1979). Maka dari itu diperoleh k dan m-fuzzier paling optimal yaitu k=3 dan m=2 untuk analisis selanjutnya.

9. Pemodelan Fuzzy C-Means

Fuzzy C-Means (FCM) adalah algoritma clustering yang memungkinkan suatu data memiliki tingkat keanggotaan pada beberapa cluster sekaligus. Tingkat keanggotaan ini menunjukkan seberapa kuat hubungan data tersebut dengan setiap cluster, dengan nilai antara 0 dan 1. Dalam FCM, setiap data tidak hanya dimiliki oleh satu cluster, melainkan dapat menjadi bagian dari beberapa cluster dengan nilai keanggotaan yang berbeda. Jika jumlah cluster yang ditentukan adalah c, maka setiap data akan memiliki c nilai keanggotaan. Algoritma Fuzzy C-Means Clustering sebagai berikut:

  1. Memasukkan data yang akan dicluster ke dalam sebuah matriks \(X\) berukuran \(n \times m\), dengan \(n\) adalah banyaknya data yang akan dicluster dan \(m\) adalah variabel setiap data. \(X_{ij}\) = data ke-\(i\) (\(i=1,2,\dots,n\)), variabel ke-\(j\) (\(j=1,2,\dots,m\)). \[ X = \begin{bmatrix} x_{11} & x_{12} & \dots & x_{1m} \\ x_{21} & x_{22} & \dots & x_{2m} \\ \vdots & \vdots & \ddots & \vdots \\ x_{n1} & x_{n2} & \dots & x_{nm} \end{bmatrix} \]

  2. Menentukan beberapa parameter awal, sebagai berikut:

    1. Banyaknya cluster = \(c\); \(2 \leq c \leq n\) dengan \(n\) adalah banyaknya data
    2. Pangkat/pembobot = \(w\); \(w \geq 1\), menurut Klawonn dan Höppner (2003) \(w=2\) sering digunakan sebagai nilai pembobot karena dianggap paling halus
    3. Maksimum iterasi = MaksIter;
    4. Error yang diharapkan = \(\xi\);
    5. Fungsi Objektif awal = \(P_0 = 0\);
    6. Iterasi awal = \(t = 1\);
  3. Membangkitkan bilangan acak \(\mu_{ik}\) dengan \(i\) (\(i=1,2,\dots,n\)) merupakan banyak data dan \(k\) (\(k=1,2,\dots,c\)) merupakan banyak cluster sebagai elemen-elemen matriks partisi awal \(\mathbf{U}\). \[ U^{(0)} = \begin{bmatrix} \mu_{11} & \mu_{12} & \dots & \mu_{1c} \\ \mu_{21} & \mu_{22} & \dots & \mu_{2c} \\ \vdots & \vdots & \dots & \vdots \\ \mu_{n1} & \mu_{n2} & \dots & \mu_{nc} \end{bmatrix} \] Dengan syarat:
    \[\mu_{ik} \in [0,1]; (1 \leq i \leq n; 1 \leq k \leq c)\] \[\sum_{k=1}^{c} \mu_{ik} = 1 ; 1 \leq i \leq n\]

  4. Menghitung pusat cluster ke-k : \(V_{kj}\), dengan k=1,2,…c dan j=1,2,…m \[ V_{kj} = \frac{\sum_{i=1}^{n} (\mu_{ik})^w * X_{ij}}{\sum_{i=1}^{n} (\mu_{ik})^w} \]

  5. Menghitung fungsi objektif pada iterasi ke-t, \(P_t\): \[ P_t = \sum_{i=1}^{n} \sum_{k=1}^{c} \left( {\mu_{ik}}^w {d_{ik}}^2(X_{ij}, V_{kj}) \right) \] dengan \({d_{ik}}^2(X_{ij}, V_{kj})\) adalah jarak Euclidean antara data ke-i dengan pusat cluster ke k, sehingga diperoleh sebagai berikut: \[ P_t = \sum_{i=1}^{n} \sum_{k=1}^{c} \left( {\mu_{ik}}^w \left[ \sum_{j=1}^{m} (X_{ij} - V_{kj})^2 \right] \right) \]

  6. Menghitung perubahan derajat keanggotaan setiap data pada setiap cluster (memperbaiki matriks partisi (\(\mathbf{U}\)) dengan: \[ \mu_{ik} = \frac{\left[ \sum_{j=1}^{m} (X_{ij} - V_{kj})^2 \right]^{\frac{-1}{w-1}}}{\sum_{k=1}^{c} \left[ \sum_{j=1}^{m} (X_{ij} - V_{kj})^2 \right]^{\frac{-1}{w-1}}} \] dengan : \(i=1,2,\dots,n\) dan \(k=1,2,\dots,c\).

  7. Menyelidiki kondisi berhenti

    • Jika : \((|P_t - P_{t-1}| < \xi)\) atau \((t > \text{MaksIter})\) maka berhenti;
    • Jika : \((|P_t - P_{t-1}| \geq \xi)\) dan \((t < \text{MaksIter})\) maka \(t = t+1\) kemudian mengulangi langkah 4.
# Hasil kluster
k_best <- 3
m_best <- 2

set.seed(123) 
fcm_result <- fcm(data_norm, centers = k_best, m = m_best)

10. Hasil Cluster

10.1. Pusat Cluster

# 1. Pusat Kluster (Centroid pada data ternormalisasi)
print(fcm_result$v)
##                 RLS      IPLM       TKM
## Cluster 1 0.2717651 0.3667846 0.6852225
## Cluster 2 0.1777694 0.3295329 0.1508963
## Cluster 3 0.8798557 0.8564067 0.5682306
# Membuat tabel gabungan agar nama Kabupaten/Kota terlihat jelas
hasil_akhir <- data.frame(
  Kabupaten_Kota = rownames(data_norm),
  Jarak_C1       = fcm_result$d[, 1],
  Jarak_C2       = fcm_result$d[, 2],
  Jarak_C3       = fcm_result$d[, 3],
  Keanggotaan_C1 = fcm_result$u[, 1],
  Keanggotaan_C2 = fcm_result$u[, 2],
  Keanggotaan_C3 = fcm_result$u[, 3],
  Cluster_Akhir  = fcm_result$cluster
)
rownames(hasil_akhir) <- NULL

Keterangan:
Output di atas menunjukkan centroid (rata-rata) tiap cluster secara multivariat yang masih dalam bentuk data hasil transformasi normalisasi min max.

10.2. Jarak Anggota Cluster terhadap Centroid

# 2. Jarak anggota kluster terhadap masing-masing centroid
print(hasil_akhir[, c("Kabupaten_Kota", "Jarak_C1", "Jarak_C2","Jarak_C3")])
##     Kabupaten_Kota    Jarak_C1   Jarak_C2   Jarak_C3
## 1          Cilacap 0.214494257 0.01032729 0.91518246
## 2         Banyumas 0.272598134 0.01229489 0.75591286
## 3      Purbalingga 0.495573490 0.04088762 1.04429907
## 4     Banjarnegara 0.552632396 0.07940133 1.18257169
## 5          Kebumen 0.076458373 0.51569022 0.52424159
## 6        Purworejo 0.222970447 0.15385224 0.35176719
## 7         Wonosobo 0.088626639 0.34699712 0.84226747
## 8         Magelang 0.088205447 0.34857385 1.04593138
## 9         Boyolali 0.405842216 0.02697033 0.88165425
## 10          Klaten 0.081478460 0.42136177 0.39189192
## 11       Sukoharjo 0.352189537 0.36929411 0.17697606
## 12        Wonogiri 0.137946048 0.39200209 1.20560009
## 13     Karanganyar 0.678022032 0.26644606 1.16000370
## 14          Sragen 0.360255410 0.03737357 0.74624971
## 15        Grobogan 0.348219840 0.12275836 1.42167006
## 16           Blora 0.406680117 0.04927842 0.93062731
## 17         Rembang 0.025818204 0.15876071 0.74595436
## 18            Pati 0.012246914 0.39164106 0.64325522
## 19           Kudus 0.130432157 0.58726714 0.22932486
## 20          Jepara 0.009545637 0.31144068 0.63936096
## 21           Demak 0.023882288 0.17848227 0.55011767
## 22        Semarang 0.099234032 0.12902301 0.45762321
## 23      Temanggung 0.213352430 0.01177198 0.95034279
## 24          Kendal 0.100428888 0.71859671 0.84688502
## 25          Batang 0.083967181 0.12277809 0.90168938
## 26      Pekalongan 0.113169534 0.72508462 0.92937296
## 27        Pemalang 0.595767212 0.14795063 1.76301442
## 28           Tegal 0.389742784 0.02432857 1.22491012
## 29          Brebes 0.391989821 0.04105175 1.34819485
## 30   Kota Magelang 1.030372662 1.84656636 0.22147849
## 31  Kota Surakarta 0.986757666 1.17405270 0.05598545
## 32   Kota Salatiga 0.951857076 1.27839707 0.03575212
## 33   Kota Semarang 0.621521389 0.74087967 0.08277436
## 34 Kota Pekalongan 0.383286765 0.89972069 0.15524656
## 35      Kota Tegal 0.208258106 0.17477587 0.38595504

Keterangan:
Output di atas menunjukkan jarak antara setiap titik data dengan centroid tiap klaster. Jarak yang digunakan adalah jarak Euclidean dengan rumus:
\[ d(x_i, x_k) = \sqrt{\sum_{j=1}^{m} (x_{ij} - x_{kj})^2} \]

10.3. Derajat Keanggotaan

# 3. Nilai Derajat Keanggotaan 
print(hasil_akhir[, c("Kabupaten_Kota", "Keanggotaan_C1", "Keanggotaan_C2","Keanggotaan_C3")])
##     Kabupaten_Kota Keanggotaan_C1 Keanggotaan_C2 Keanggotaan_C3
## 1          Cilacap     0.04544624     0.94390238     0.01065138
## 2         Banyumas     0.04249482     0.94218065     0.01532453
## 3      Purbalingga     0.07355682     0.89153669     0.03490649
## 4     Banjarnegara     0.11866192     0.82588570     0.05545238
## 5          Kebumen     0.77273194     0.11456845     0.11269962
## 6        Purworejo     0.32434797     0.47006148     0.20559055
## 7         Wonosobo     0.73495141     0.18771416     0.07733443
## 8         Magelang     0.74773141     0.18921093     0.06305766
## 9         Boyolali     0.06057651     0.91153898     0.02788452
## 10          Klaten     0.71363330     0.13799482     0.14837188
## 11       Sukoharjo     0.25356746     0.24182299     0.50460954
## 12        Wonogiri     0.68197827     0.23998905     0.07803268
## 13     Karanganyar     0.24217838     0.61626838     0.14155324
## 14          Sragen     0.08991136     0.86668352     0.04340512
## 15        Grobogan     0.24500403     0.69498537     0.06001059
## 16           Blora     0.10320239     0.85169861     0.04509900
## 17         Rembang     0.83525835     0.13583254     0.02890910
## 18            Pati     0.95210010     0.02977289     0.01812700
## 19           Kudus     0.55838904     0.12401832     0.31759264
## 20          Jepara     0.95640706     0.02931382     0.01427912
## 21           Demak     0.84945841     0.11366401     0.03687758
## 22        Semarang     0.50353342     0.38727707     0.10918950
## 23      Temanggung     0.05168426     0.93671260     0.01160314
## 24          Kendal     0.79469578     0.11106426     0.09423996
## 25          Batang     0.56274116     0.38485522     0.05240362
## 26      Pekalongan     0.78256602     0.12214110     0.09529289
## 27        Pemalang     0.18640292     0.75060681     0.06299027
## 28           Tegal     0.05767630     0.92397222     0.01835149
## 29          Brebes     0.09225580     0.88092068     0.02682352
## 30   Kota Magelang     0.16102435     0.08985060     0.74912505
## 31  Kota Surakarta     0.05137235     0.04317699     0.90545066
## 32   Kota Salatiga     0.03525053     0.02624651     0.93850295
## 33   Kota Semarang     0.10698026     0.08974537     0.80327436
## 34 Kota Pekalongan     0.25674620     0.10937552     0.63387827
## 35      Kota Tegal     0.36614425     0.43628739     0.19756837

Keterangan:
Output di atas menunjukkan tingkat keanggotaan tiap titik data untuk masuk ke dalam suatu cluster. Fuzzy C-Means memungkinkan suatu data memiliki tingkat keanggotaan pada beberapa cluster sekaligus sehingga satu data bisa menjadi bagian dari beberapa cluster. Tingkat keanggotaan menunjukkan kuat hubungan data dengan setiap cluster.

10.4. Label Anggota Cluster

# 4. Label Kluster yang menjadi keanggotaan akhir
print(hasil_akhir[, c("Kabupaten_Kota", "Cluster_Akhir")])
##     Kabupaten_Kota Cluster_Akhir
## 1          Cilacap             2
## 2         Banyumas             2
## 3      Purbalingga             2
## 4     Banjarnegara             2
## 5          Kebumen             1
## 6        Purworejo             2
## 7         Wonosobo             1
## 8         Magelang             1
## 9         Boyolali             2
## 10          Klaten             1
## 11       Sukoharjo             3
## 12        Wonogiri             1
## 13     Karanganyar             2
## 14          Sragen             2
## 15        Grobogan             2
## 16           Blora             2
## 17         Rembang             1
## 18            Pati             1
## 19           Kudus             1
## 20          Jepara             1
## 21           Demak             1
## 22        Semarang             1
## 23      Temanggung             2
## 24          Kendal             1
## 25          Batang             1
## 26      Pekalongan             1
## 27        Pemalang             2
## 28           Tegal             2
## 29          Brebes             2
## 30   Kota Magelang             3
## 31  Kota Surakarta             3
## 32   Kota Salatiga             3
## 33   Kota Semarang             3
## 34 Kota Pekalongan             3
## 35      Kota Tegal             2

Keterangan:
Output di atas menunjukkan pengelompokkan tiap data ke dalam suatu cluster. Pengelompokkan didasarkan jarak terdekat dengan suatu cluster dan derajat keanggotaan tertinggi dengan suatu cluster.

11. Profilisasi Cluster

# profilisasi kluster
data_final <- cbind(data_num, Cluster = fcm_result$cluster)

profil_cluster <- data_final %>%
  group_by(Cluster) %>%
  summarise(
    Rata_RLS = mean(RLS),
    Rata_IPLM = mean(IPLM),
    Rata_TKM = mean(TKM),
    Jumlah_KabKota = n()
  )

print(profil_cluster)
## # A tibble: 3 × 5
##   Cluster Rata_RLS Rata_IPLM Rata_TKM Jumlah_KabKota
##     <int>    <dbl>     <dbl>    <dbl>          <int>
## 1       1     7.96      65.6     78.4             14
## 2       2     7.72      62.8     66.8             15
## 3       3    10.7       91.7     76.0              6

Keterangan:
Output di atas menunjukkan informasi mengenai rata-rata akhir tiap cluster (sudah didenormalisasi). Karakteristik setiap cluster sebagai berikut:

Cluster 1
Cluster 1 memiliki rata-rata RLS dan IPLM menengah walaupun nilainya tidak terlalu jauh dengan cluster 2 (RLS dan IPLM terendah). Akan tetapi, cluster 1 memiliki rata-rata TKM tertinggi. Cluster 1 terdiri atas 14 wilayah, yaitu Kebumen, Wonosobo, Magelang, Klaten, Wonogiri, Rembang, Pati, Kudus, Jepara, Demak, Semarang, Kendal, Batang, dan Pekalongan.

Cluster 2
Cluster 2 memiliki rata-rata RLS, IPLM, dan TKM paling rendah. Cluster 2 terdiri atas 15 wilayah, yaitu Cilacap, Banyumas, Purbalingga, Banjarnegara, Purworejo, Boyolali, Karanganyar, Sragen, Grobogan, Blora, Temanggung, Pemalang, Tegal, Brebes, dan Kota Tegal.

Cluster 3
Cluster 3 memiliki rata-rata RLS, IPLM tertinggi. Akan tetapi, rata-rata TKM menengah walaupun nilainya tidak terlalu jauh dengan cluster 1 (TKM tertinggi). Cluster 3 terdiri atas 6 wilayah, yaitu Sukoharjo, Kota Magelang, Kota Surakarta, Kota Salatiga, Kota Semarang, dan Kota Pekalongan.

12. Visualisasi Cluster

# visualisasi
fcm_kmeans <- list(cluster = fcm_result$cluster)
class(fcm_kmeans) <- "kmeans"

plot_cluster <- fviz_cluster(fcm_kmeans, 
                             data = data_norm, 
                             geom = c("point", "text"),
                             ellipse.type = "convex",
                             palette = "aaas",
                             repel = TRUE,
                             main = "Visualisasi Fuzzy C-Means Clustering Jawa Tengah")

print(plot_cluster)

Keterangan:
Output di atas menunjukkan visualisasi koordinat tiap titik data dengan centroid sehingga membentuk cluster-cluster dalam format 2 dimensi.

13. Visualisasi Peta Hasil Cluster

# A. Mengunduh data spasial peta Indonesia dari GADM (Otomatis)
indo_map <- gadm(country = "IDN", level = 2, path = getwd())

# B. Mengubah format ke 'sf' agar bisa dibaca oleh ggplot dan memfilter Jawa Tengah
indo_sf <- st_as_sf(indo_map)
jateng_sf <- indo_sf %>% filter(NAME_1 == "Jawa Tengah")

# C. Menyiapkan data hasil kluster dari langkah sebelumnya
df_peta <- data.frame(
  Nama_Daerah = rownames(data_norm),
  Cluster = as.factor(fcm_result$cluster) 
)

df_peta <- df_peta %>%
  mutate(Nama_Daerah = case_when(
    Nama_Daerah == "Kota Surakarta" ~ "Surakarta",
    Nama_Daerah == "Kota Salatiga" ~ "Salatiga",
    TRUE ~ Nama_Daerah 
  ))

# D. Menggabungkan (Join) data peta dengan data hasil kluster Anda
peta_cluster <- left_join(jateng_sf, df_peta, by = c("NAME_2" = "Nama_Daerah"))

# E. Membuat Plot Peta
plot_peta_jateng <- ggplot(data = peta_cluster) +
  geom_sf(aes(fill = Cluster), color = "white", size = 0.3) +
scale_fill_manual(values = c("1" = "#5DADE2", "2" = "#F5B041", "3" = "#48C9B0"), 
                    na.value = "grey90", name = "Kluster") +
  theme_minimal() +
  labs(title = "Peta Persebaran Cluster Pendidikan di Jawa Tengah",
       subtitle = "Berdasarkan RLS, IPLM, dan TKM menggunakan Fuzzy C-Means",
       caption = "Sumber data: BPS Jawa Tengah") +
  theme(
    plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    legend.position = "bottom",
    axis.text = element_blank(), 
    axis.ticks = element_blank(),
    panel.grid = element_blank()
  )

print(plot_peta_jateng)