Pendahuluan

Indonesia sebagai negara berkembang dengan jumlah penduduk yang besar menghadapi permasalahan ketenagakerjaan yang beragam antarwilayah. Perbedaan tingkat pembangunan dan struktur ekonomi menyebabkan karakteristik pasar kerja di setiap provinsi tidak sama. Oleh karena itu, perumusan kebijakan ketenagakerjaan yang efektif perlu mempertimbangkan kondisi spesifik masing-masing wilayah agar program yang diterapkan lebih tepat sasaran.

Penelitian ini bertujuan untuk mengelompokkan 38 provinsi di Indonesia berdasarkan karakteristik ketenagakerjaan tahun 2024 menggunakan metode Fuzzy C-Means Clustering. Variabel yang digunakan meliputi Tingkat Pengangguran Terbuka (TPT), Tingkat Partisipasi Angkatan Kerja (TPAK), presentase pekerja sektor informal, tingkat kerentanan kerja, dan rasio kesempatan kerja. Metode Fuzzy C-Means dipilih karena mampu memberikan derajat keanggotaan pada setiap provinsi dalam lebih dari satu klaster, sehingga dapat merepresentasikan kemiripan karakteristik secara lebih fleksibel.

Hasil penelitian ini diharapkan dapat memberikan gambaran mengenai pola pengelompokan provinsi berdasarkan kondisi ketenagakerjaan serta mengidentifikasi karakteristik utama dari setiap klaster. Informasi tersebut dapat menjadi dasar pertimbangan dalam penyusunan kebijakan dan program ketenagakerjaan yang lebih terarah sesuai dengan karakteristik masing-masing kelompok wilayah.

Library

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.1     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.2.0     
## ── 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(e1071)
## 
## Attaching package: 'e1071'
## 
## The following object is masked from 'package:ggplot2':
## 
##     element
library(sf)
## Linking to GEOS 3.13.1, GDAL 3.11.4, PROJ 9.7.0; sf_use_s2() is TRUE
library(ggplot2)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthdata)
## 
## Attaching package: 'rnaturalearthdata'
## 
## The following object is masked from 'package:rnaturalearth':
## 
##     countries110

Impor Data

Data diimpor ke dalam R menggunakan fungsi read.csv() karena tersedia dalam format .csv. Selanjutnya, variabel Provinsi dipisahkan agar proses klasterisasi hanya dilakukan pada variabel numerik.

data <- read.csv("D:\\SEMESTER 6\\KOMPUTASI STATISTIKA LANJUT\\KETENAGAKERJAAN.csv")

prov <- data$Provinsi
X <- data %>% select (-Provinsi)

head(data)
##           Provinsi Tingkat.Partisipasi.Angkatan.Kerja..TPAK.
## 1             Aceh                                     64.15
## 2   Sumatera Utara                                     70.30
## 3   Sumatera Barat                                     70.44
## 4             Riau                                     65.75
## 5            Jambi                                     67.09
## 6 Sumatera Selatan                                     69.75
##   Tingkat.Pengangguran.Terbuka..TPT. Pekerja.Sektor.Informal
## 1                               5.56                   63.19
## 2                               5.10                   57.58
## 3                               5.79                   63.76
## 4                               3.85                   53.08
## 5                               4.45                   60.80
## 6                               3.97                   63.41
##   Tingkat.Kerentanan.Kerja Rasio.Kesempatan.Kerja
## 1                     8.61                  60.58
## 2                     7.15                  66.72
## 3                     9.86                  66.36
## 4                    10.30                  63.22
## 5                     9.73                  64.11
## 6                     3.76                  66.98

Pre-Processing Data

1. Cek Missing Values

Pengecekan missing values dilakukan untuk memastikan tidak terdapat data yang hilang pada setiap variabel.

sum(is.na(data))
## [1] 0
colSums(is.na(data))
##                                  Provinsi 
##                                         0 
## Tingkat.Partisipasi.Angkatan.Kerja..TPAK. 
##                                         0 
##        Tingkat.Pengangguran.Terbuka..TPT. 
##                                         0 
##                   Pekerja.Sektor.Informal 
##                                         0 
##                  Tingkat.Kerentanan.Kerja 
##                                         0 
##                    Rasio.Kesempatan.Kerja 
##                                         0

Berdasarkan hasil pengecekan, diperoleh bahwa tidak terdapat missing values pada data sehingga seluruh variabel dapat digunakan dalam proses analisis selanjutnya.

2. Deteksi Outlier

Deteksi outlier dilakukan menggunakan boxplot untuk mengidentifikasi nilai ekstrem yang berpotensi memengaruhi pembentukan klaster.

# Pilih hanya variabel numerik
X <- data %>% select(
  Tingkat.Partisipasi.Angkatan.Kerja..TPAK.,
  Tingkat.Pengangguran.Terbuka..TPT.,
  Pekerja.Sektor.Informal,
  Tingkat.Kerentanan.Kerja,
  Rasio.Kesempatan.Kerja,
)

# Layout 3 baris 2 kolom -> cukup untuk 5 variabel
par(mfrow = c(3,2))

for(i in 1:ncol(X)){
  boxplot(X[[i]],
          main = paste("Boxplot -", colnames(X)[[i]]), col = "pink", horizontal = TRUE)
}

Berdasarkan boxplot, variabel TPAK, Pekerja Sektor Informal, dan Rasio Kesempatan Kerja teridentifikasi memiliki outlier, sehingga dilakukan penanganan menggunakan metode winsorizing.

3. Penanganan Outlier

Penanganan outlier dilakukan menggunakan metode winsorizing, yaitu dengan membatasi nilai ekstrem berdasarkan batas bawah dan batas atas yang ditentukan menggunakan metode IQR (Interquartile Range).

# Fungsi Winsorizing
winsorize <- function(X){
  Q1 <- quantile(X, 0.25, na.rm = TRUE)
  Q3 <- quantile(X, 0.75, na.rm = TRUE)
  IQR <- Q3 - Q1
  
  lower_bound <- Q1 - 1.5 * IQR
  upper_bound <- Q3 + 1.5 * IQR
  
# Ganti nilai di bawah lower_bound dan di atas upper_bound
  X[X < lower_bound] <- lower_bound
  X[X > upper_bound] <- upper_bound
  
  return(X)
}

# Terapkan winsorizing pada semua variabel numerik
X_winsor <- as.data.frame(lapply(X, winsorize))

# Gabungkan kembali dengan Provinsi
data_winsor <- cbind(Provinsi = data$Provinsi, X_winsor)

par(mfrow = c(3,2))
for(i in 1:ncol(X_winsor)){
  boxplot(X_winsor[[i]],
          main = paste("Boxplot After Winsorizing -", colnames(X_winsor)[i]), col = "skyblue", horizontal = TRUE)
}
par(mfrow = c(1,1))

Setelah winsorizing, boxplot menunjukkan bahwa tidak terdapat lagi outlier pada variabel yang dianalisis.

4. Standarisasi Data

Standarisasi dilakukan untuk menyamakan skala antarvariabel agar tidak ada variabel yang mendominasi dalam perhitungan jarak pada metode Fuzzy C-Means.

X_scaled <- scale(X_winsor)

Statistik Deskriptif

Statistik deskriptif digunakan untuk memberikan gambaran umum mengenai karakteristik masing-masing variabel yang digunakan dalam penelitian. Ukuran yang ditampilkan meliputi nilai minimum, maksimum, rata-rata, dan standar deviasi.

summary(X_winsor)
##  Tingkat.Partisipasi.Angkatan.Kerja..TPAK. Tingkat.Pengangguran.Terbuka..TPT.
##  Min.   :63.98                             Min.   :1.180                     
##  1st Qu.:67.10                             1st Qu.:3.255                     
##  Median :69.59                             Median :4.140                     
##  Mean   :69.68                             Mean   :4.368                     
##  3rd Qu.:71.60                             3rd Qu.:5.702                     
##  Max.   :78.35                             Max.   :7.020                     
##  Pekerja.Sektor.Informal Tingkat.Kerentanan.Kerja Rasio.Kesempatan.Kerja
##  Min.   :37.48           Min.   : 0.010           Min.   :60.16         
##  1st Qu.:54.15           1st Qu.: 3.535           1st Qu.:62.81         
##  Median :60.76           Median : 5.220           Median :66.55         
##  Mean   :60.18           Mean   : 6.144           Mean   :66.69         
##  3rd Qu.:65.26           3rd Qu.: 9.828           3rd Qu.:68.95         
##  Max.   :81.92           Max.   :12.130           Max.   :78.15

Penentuan Jumlah Klaster (Indeks Validitas)

Sebelum menerapkan metode Fuzzy C-Means, perlu ditentukan jumlah klaster (k) yang optimal. Penentuan jumlah klaster dilakukan dengan menguji beberapa nilai k dan mengevaluasinya menggunakan beberapa indeks validitas, yaitu Partition Coefficient (PC), Classification Entropy (CE), dan Xie-Beni Index (XB). Nilai k optimal dipilih berdasarkan nilai maksimum pada PC serta nilai minimum pada CE dan XB.

# Fungsi Menghitung Indeks Validitas Klaster
partition_coefficient <- function(U){
  sum(U^2) / nrow(U)
}

modified_PC <- function(U){
  c <- ncol(U)
  (partition_coefficient(U) - 1/c) / (1 - 1/c)
}

classification_entropy <- function(U){
  -sum(U * log(U)) / nrow(U)
}

xie_beni <- function(X, centers, U){
  dist <- as.matrix(dist(rbind(centers)))
  min.dist <- min(dist[dist > 0])
  
  d <- as.matrix(dist(rbind(X, centers)))[1:nrow(X), (nrow(X)+1):(nrow(X)+nrow(centers))]
  
  sum((U^2) * (d^2)) / (nrow(X) * min.dist^2)
}


# Loop Kombinasi Klaster = {2,3,4} dan Fuzzifier m = {1.5, 2.0, 2.5}
results <- data.frame()

for (c in c(2,3,4)){
  for (m in c(1.5, 2.0, 2.5)){
    
    fcm <- cmeans(X_scaled, centers = c, m = m, iter.max = 100, verbose = FALSE)
    U <- fcm$membership
    centers <- fcm$centers
    
    PC  <- partition_coefficient(U)
    MPC <- modified_PC(U)
    CE  <- classification_entropy(U)
    XB  <- xie_beni(X_scaled, centers, U)
    
    results <- rbind(results,
                     data.frame(Clusters=c, m=m,
                                PC=PC, MPC=MPC, CE=CE, XB=XB))
  }
}

print(results)
##   Clusters   m        PC       MPC        CE        XB
## 1        2 1.5 0.8420102 0.6840203 0.2739729 0.2955297
## 2        2 2.0 0.6635622 0.3271244 0.5134127 0.3278443
## 3        2 2.5 0.5808158 0.1616316 0.6083240 0.4332135
## 4        3 1.5 0.7786328 0.6679492 0.3913429 0.3083350
## 5        3 2.0 0.5491582 0.3237372 0.7715582 0.3338428
## 6        3 2.5 0.4068605 0.1102907 0.9898676 0.6719506
## 7        4 1.5 0.7434538 0.6579384 0.4754061 0.3183518
## 8        4 2.0 0.5082750 0.3443667 0.9208414 0.3457525
## 9        4 2.5 0.3796919 0.1729225 1.1506130 0.5861599

Berdasarkan hasil perhitungan indeks validitas, kombinasi jumlah klaster k = 2 dan parameter fuzzifier m = 1.5 menghasilkan nilai Partition Coefficient (PC) terbesar serta nilai Classification Entropy (CE) dan Xie-Beni (XB) terkecil dibandingkan kombinasi lainnya. Oleh karena itu, kombinasi tersebut dipilih sebagai model terbaik dalam penelitian ini.

Model Fuzzy C-Means Terbaik

fcm_final <- e1071::cmeans(X_scaled, centers = 2, m = 1.5, iter.max = 100)

Visualisasi Hasil Klasterisasi

Visualisasi PCA digunakan untuk memproyeksikan data ke dalam dua komponen utama sehingga memudahkan melihat pola pengelompokan provinsi berdasarkan karakteristik ketenagakerjaan.

1. PCA Plot

pca <- prcomp(X_scaled)

cluster_final <- apply(fcm_final$membership, 1, which.max)

pca_df <- data.frame(
  PC1 = pca$x[,1],
  PC2 = pca$x[,2],
  Cluster = factor(cluster_final)
)

ggplot(pca_df, aes(PC1, PC2, color = Cluster)) +
  geom_point(size = 3) +
  theme_minimal() +
  labs(title = "PCA Plot - Cluster Visualization")

Hasil PCA menunjukkan bahwa provinsi yang berada dalam klaster yang sama cenderung berada pada posisi yang berdekatan pada ruang komponen utama. Hal ini menunjukkan adanya kemiripan karakteristik ketenagakerjaan antarprovinsi dalam klaster tersebut.

2. Profil Klaster (Centroid)

Plot centroid menunjukkan nilai rata-rata terstandarisasi setiap variabel pada masing-masing klaster sehingga dapat menggambarkan karakteristik ketenagakerjaan yang membedakan antar klaster.

# Centroid Plot
centroid_df <- as.data.frame(fcm_final$centers)
centroid_df$Cluster <- factor(1:nrow(centroid_df))

centroid_long <- centroid_df %>%
  pivot_longer(cols = -Cluster, names_to = "Variable", values_to = "Value")

ggplot(centroid_long, aes(x = Variable, y = Value, fill = Cluster)) + geom_bar(stat = "identity", position = "dodge") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(title = "Cluster Centroids",
       x = "Variables",
       y = "Standardized Value")

Berdasarkan nilai centroid, klaster 2 menunjukkan karakteristik provinsi dengan tingkat partisipasi angkatan kerja (TPAK), rasio kesempatan kerja, dan persentase pekerja sektor informal yang relatif lebih tinggi. Hal ini mengindikasikan bahwa provinsi dalam klaster ini memiliki tingkat penyerapan tenaga kerja yang lebih besar, meskipun sebagian besar berada pada sektor informal. Sebaliknya, klaster 1 ditandai dengan nilai Tingkat Pengangguran Terbuka (TPT) yang lebih tinggi, yang menunjukkan bahwa provinsi dalam klaster ini menghadapi permasalahan pengangguran yang relatif lebih besar dibandingkan klaster lainnya.

3. Heatmap Derajat Keanggotaan

Heatmap derajat keanggotaan menunjukkan tingkat keanggotaan setiap provinsi pada masing-masing klaster, sehingga dapat terlihat kecenderungan provinsi lebih dominan berada pada klaster tertentu.

# Membership Matrix
membership_matrix <- fcm_final$membership
colnames(membership_matrix) <- paste("Cluster", 1:ncol(membership_matrix))
rownames(membership_matrix) <- prov

# Bar Plot - Membership Values
membership_df <- as.data.frame(membership_matrix) %>%
  tibble::rownames_to_column("Provinsi") %>%
  tidyr::pivot_longer(cols = -Provinsi, names_to = "Cluster", values_to = "Membership")

ggplot(membership_df, aes(x = Provinsi, y = Membership, fill = Cluster)) +
  geom_bar(stat = "identity", position = "dodge") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Membership Values per Province",
       x = "Province",
       y = "Membership Degree")

Semakin tinggi nilai keanggotaan suatu provinsi pada klaster tertentu, maka semakin besar kemungkinan provinsi tersebut memiliki karakteristik ketenagakerjaan yang serupa dengan klaster tersebut.

Final Klaster

fcm_final <- e1071::cmeans(X_scaled, centers = 2, m = 1.5, iter.max = 100)

data$Cluster <- apply(fcm_final$membership, 1, which.max)

final_assignment <- data %>%
  select(Provinsi, Cluster) %>%
  arrange(Cluster, Provinsi)

print(final_assignment)
##                Provinsi Cluster
## 1                  Aceh       1
## 2                Banten       1
## 3           DKI Jakarta       1
## 4                 Jambi       1
## 5            Jawa Barat       1
## 6      Kalimantan Barat       1
## 7    Kalimantan Selatan       1
## 8     Kalimantan Tengah       1
## 9      Kalimantan Timur       1
## 10     Kalimantan Utara       1
## 11 Kep. Bangka Belitung       1
## 12       Kepulauan Riau       1
## 13               Maluku       1
## 14         Maluku Utara       1
## 15                Papua       1
## 16     Papua Barat Daya       1
## 17        Papua Selatan       1
## 18                 Riau       1
## 19     Sulawesi Selatan       1
## 20       Sulawesi Utara       1
## 21       Sumatera Barat       1
## 22       Sumatera Utara       1
## 23                 Bali       2
## 24             Bengkulu       2
## 25        DI Yogyakarta       2
## 26            Gorontalo       2
## 27          Jawa Tengah       2
## 28           Jawa Timur       2
## 29              Lampung       2
## 30  Nusa Tenggara Barat       2
## 31  Nusa Tenggara Timur       2
## 32          Papua Barat       2
## 33     Papua Pegunungan       2
## 34         Papua Tengah       2
## 35       Sulawesi Barat       2
## 36      Sulawesi Tengah       2
## 37    Sulawesi Tenggara       2
## 38     Sumatera Selatan       2
library(rnaturalearthhires)

# Data cluster dari hasil FCM
final_assignment <- data.frame(
  Provinsi = prov,
  Cluster = apply(fcm_final$membership, 1, which.max)
)

# Ambil peta provinsi Indonesia
indo_map <- ne_states(country = "Indonesia", returnclass = "sf")
indo_map$Provinsi <- indo_map$name

# Gabungkan peta dengan data cluster
map_cluster <- left_join(indo_map, final_assignment, by = "Provinsi")

# Plot peta klaster
ggplot(map_cluster) +
  geom_sf(aes(fill = factor(Cluster)), color = "white", linewidth = 0.2) +
  scale_fill_manual(values = c("#c8efc8", "#0b6b2a"), na.value = "grey90") +
  labs(title = "Peta Sebaran Klaster 38 Provinsi di Indonesia",
       fill = "Cluster") +
  theme_minimal()

Hasil pengelompokan menunjukkan bahwa 38 provinsi di Indonesia terbagi ke dalam dua klaster berdasarkan karakteristik ketenagakerjaan. Peta sebaran klaster memperlihatkan distribusi provinsi pada masing-masing klaster sehingga memudahkan dalam melihat pola wilayah yang memiliki karakteristik ketenagakerjaan yang serupa.

Kesimpulan

Berdasarkan analisis klaster menggunakan metode Fuzzy C-Means, 38 provinsi di Indonesia dapat dikelompokkan menjadi dua klaster berdasarkan karakteristik ketenagakerjaan tahun 2024. Klaster pertama dicirikan oleh nilai Tingkat Pengangguran Terbuka (TPT) yang relatif lebih tinggi, sedangkan klaster kedua memiliki nilai Tingkat Partisipasi Angkatan Kerja (TPAK), rasio kesempatan kerja, dan persentase pekerja sektor informal yang lebih tinggi. Hasil pengelompokan ini menunjukkan adanya perbedaan karakteristik ketenagakerjaan antarwilayah sehingga kebijakan ketenagakerjaan yang diterapkan dapat disesuaikan dengan kondisi masing-masing klaster wilayah.