# ==============================================================================
# PIPELINE INTEGRAL KDD FINAL: 47 KECAMATAN, EKSPOR EXCEL & GRAFIK TIAP TAHAP
# ==============================================================================

# Mengaktifkan seluruh pustaka esensial
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.5.2
## Warning: package 'readr' was built under R version 4.5.2
## Warning: package 'forcats' was built under R version 4.5.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.6
## ✔ forcats   1.0.1     ✔ stringr   1.5.2
## ✔ ggplot2   4.0.0     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.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(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(writexl)     
library(leaflet)
library(sf)
## Linking to GEOS 3.13.1, GDAL 3.11.0, PROJ 9.6.0; sf_use_s2() is TRUE
library(readxl)      # WAJIB UNTUK MEMBACA FILE EXCEL (.XLSX)
library(cluster)     # <--- WAJIB UNTUK MENGHITUNG VALIDITAS SILHOUETTE
library(rsconnect)
library(packrat) 
## Warning: package 'packrat' was built under R version 4.5.3
# TAHAP 1: LOAD DATA & PEMBERSIHAN (PREPROCESSING)
print("=== TAHAP 1: PREPROCESSING ===")
## [1] "=== TAHAP 1: PREPROCESSING ==="
print("Silakan pilih file dataset IKM Anda pada jendela pop-up Windows!")
## [1] "Silakan pilih file dataset IKM Anda pada jendela pop-up Windows!"
file_path <- file.choose()

# Membaca data menggunakan fungsi khusus Excel
data_mentah <- read_excel(file_path)
## Warning: Expecting numeric in A5623 / R5623C1: got '2+K59+A5930:L5934'
# ==========================================================================
# PENYELARASAN NAMA KOLOM AMAN (MENGABAIKAN HURUF BESAR/KECIL & SPASI)
# ==========================================================================
nama_kolom <- colnames(data_mentah)

idx_tk <- grep("TK|TENAGA KERJA|PEKERJA", toupper(nama_kolom))
if(length(idx_tk) > 0) nama_kolom[idx_tk[1]] <- "Jumlah TK"

idx_sektor <- grep("SEKTOR|AGRO|KATEGORI", toupper(nama_kolom))
if(length(idx_sektor) > 0) nama_kolom[idx_sektor[1]] <- "Agro / Non Agro"

idx_kec <- grep("KECAMATAN|KEC|DAERAH|WILAYAH", toupper(nama_kolom))
if(length(idx_kec) > 0) nama_kolom[idx_kec[1]] <- "Kecamatan"

idx_modal <- grep("MODAL|INVESTASI", toupper(nama_kolom))
if(length(idx_modal) > 0) nama_kolom[idx_modal[1]] <- "Modal Usaha"

colnames(data_mentah) <- nama_kolom
# ==========================================================================

print("Memproses Seleksi Atribut dan Pembersihan Noise Masif...")
## [1] "Memproses Seleksi Atribut dan Pembersihan Noise Masif..."
data_bersih <- data_mentah %>%
  # [PERBAIKAN BUG]: Mengubah `Modal Usaha (Rp)` menjadi `Modal Usaha` agar sesuai dengan rename di atas
  select(Kecamatan, TK = `Jumlah TK`, Sektor = `Agro / Non Agro`, Modal = `Modal Usaha`) %>%
  filter(!is.na(Kecamatan) & Kecamatan != "") %>%
  mutate(
    Jml_Unit = 1, 
    Kecamatan = str_to_upper(str_trim(Kecamatan)),
    Sektor = str_to_upper(str_trim(Sektor)),
    TK = as.numeric(TK),
    Modal = as.numeric(Modal)
  ) %>%
  
  # 2. PETA KOREKSI TYPO TOTAL (Mengunci Tepat 47 Kecamatan)
  mutate(
    Kecamatan = case_when(
      Kecamatan %in% c("BOOJONGGENTENG", "BOONGGENTENG", "BOJONG GENTENG") ~ "BOJONGGENTENG",
      Kecamatan %in% c("JAMPANG KULON", "JAMPANG KULON ", "JAMPANGKULON ") ~ "JAMPANGKULON",
      Kecamatan %in% c("JAMPANG TENGAH", "JAMPANGT ENGAH", "JAMPANG TENGAH ") ~ "JAMPANGTENGAH",
      Kecamatan %in% c("GUNUNG GURUH", "GUNUNGGURUH ") ~ "GUNUNGGURUH",
      Kecamatan == "CICURUG " ~ "CICURUG",
      Kecamatan == "CISAAT " ~ "CISAAT",
      Kecamatan == "PARUNG KUDA" ~ "PARUNGKUDA",
      Kecamatan == "TEGAL BULEUD" ~ "TEGALBULEUD",
      Kecamatan == "PALABUHANRATU" ~ "PELABUHANRATU",
      
      # Alokasi Koreksi Data Desa yang salah masuk Kolom Kecamatan
      Kecamatan == "GUNUNGGJAYA" ~ "CISAAT",        
      Kecamatan == "MARGALUYU"  ~ "SAGARANTEN",    
      Kecamatan == "PANJALU"    ~ "SUKABUMI",      
      TRUE ~ Kecamatan 
    )
  ) %>%
  filter(!is.na(Modal) & Modal > 0) %>% # Hapus missing value pada Modal Usaha
  filter(!is.na(TK) & TK > 0) %>%
  
  # 3. FILTER EXCLUSION: Membuang wilayah luar daerah (Cianjur & Kota Sukabumi)
  filter(!Kecamatan %in% c(
    "CIANJUR", "BAROS", "CIBEUREUM", "CIKOLE", "CITAMIANG", "GUNUNGPUYUH", "LEMBURSITU", "WARUDOYONG"
  ))
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `Modal = as.numeric(Modal)`.
## Caused by warning:
## ! NAs introduced by coercion
# [A] Ekspor luaran bersih Tahap 1 ke format Excel
write_xlsx(data_bersih, "1_Data_IKM_Bersih.xlsx")
print("-> Sukses mengekspor berkas '1_Data_IKM_Bersih.xlsx'")
## [1] "-> Sukses mengekspor berkas '1_Data_IKM_Bersih.xlsx'"
# [B] GRAFIK TAHAP 1: Perbandingan Jumlah Data Sebelum vs Sesudah Pembersihan Noise
df_perbandingan <- tibble(
  Kondisi = c("Data Mentah Instansi", "Data Bersih (Pasca-Filter KDD)"),
  Jumlah_Baris = c(nrow(data_mentah), nrow(data_bersih))
)

grafik_tahap1 <- ggplot(df_perbandingan, aes(x = Kondisi, y = Jumlah_Baris, fill = Kondisi)) +
  geom_bar(stat = "identity", width = 0.4, color = "black") +
  geom_text(aes(label = Jumlah_Baris), vjust = -0.5, fontface = "bold") +
  scale_fill_manual(values = c("#e74c3c", "#2ecc71")) +
  labs(title = "Grafik Tahap 1: Eliminasi Data Noise & Outlier Wilayah",
       x = "Kondisi Data", y = "Total Baris Data (Record)") +
  theme_minimal() +
  theme(legend.position = "none")

print(grafik_tahap1)

ggsave("Grafik_Tahap1_Pembersihan_Data.png", plot = grafik_tahap1, width = 7, height = 5, dpi = 300)
# TAHAP 2: TRANSFORMASI DATA (AGREGASI WILAYAH & NORMALISASI)
print("=== TAHAP 2: TRANSFORMATION ===")
## [1] "=== TAHAP 2: TRANSFORMATION ==="
library(tidyverse)
library(writexl)

data_filter_sektor <- data_bersih %>% 
  filter(!is.na(Sektor) & Sektor %in% c("AGRO", "NON AGRO"))

# 1) Agregasi Struktural (Penambahan Variabel Modal Usaha)
data_grouped <- data_filter_sektor %>%
  group_by(Kecamatan, Sektor) %>%
  summarise(
    Total_Unit = sum(Jml_Unit, na.rm = TRUE),
    Total_TK = sum(TK, na.rm = TRUE),
    Total_Modal = sum(Modal, na.rm = TRUE), 
    .groups = 'drop'
  )

# Pivoting ke bentuk matriks lebar
data_wide <- data_grouped %>%
  pivot_wider(
    names_from = Sektor, 
    values_from = c(Total_Unit, Total_TK, Total_Modal),
    values_fill = list(Total_Unit = 0, Total_TK = 0, Total_Modal = 0)
  )

# Restrukturisasi & Penguncian Kolom 6 Variabel (X1 sampai X6)
data_agregat_final <- data_wide %>%
  select(
    Kecamatan,
    Unit_Agro = `Total_Unit_AGRO`,
    Unit_NonAgro = `Total_Unit_NON AGRO`,
    TK_Agro = `Total_TK_AGRO`,
    TK_NonAgro = `Total_TK_NON AGRO`,
    Modal_Agro = `Total_Modal_AGRO`,       
    Modal_NonAgro = `Total_Modal_NON AGRO` 
  )

# [A] Ekspor data agregat murni 47 Kecamatan ke Excel
write_xlsx(data_agregat_final, "2_Data_IKM_Agregat_FIX.xlsx")
print("-> Sukses mengekspor berkas '2_Data_IKM_Agregat_FIX.xlsx'")
## [1] "-> Sukses mengekspor berkas '2_Data_IKM_Agregat_FIX.xlsx'"
# [B] GRAFIK TAHAP 2A: Visualisasi Komposisi Unit Usaha
data_2A <- data_agregat_final %>%
  mutate(Total_Unit_Keseluruhan = Unit_Agro + Unit_NonAgro) %>%
  top_n(10, Total_Unit_Keseluruhan)

# Mengubah format ke long agar stacking ggplot berfungsi sempurna
data_2A_long <- data_2A %>%
  pivot_longer(cols = c(Unit_Agro, Unit_NonAgro), names_to = "Sektor", values_to = "Jumlah") %>%
  mutate(Sektor = factor(Sektor, levels = c("Unit_Agro", "Unit_NonAgro"), labels = c("Sektor Agro", "Sektor Non-Agro")))

grafik_tahap2a_agregasi <- ggplot(data_2A_long, aes(x = reorder(Kecamatan, Total_Unit_Keseluruhan), y = Jumlah, fill = Sektor)) +
  geom_col(position = "stack") + # Menggunakan geom_col untuk stacked bar otomatis
  coord_flip() +
  labs(title = "Grafik Tahap 2A: Akumulasi Variabel Unit Usaha",
       x = "Kecamatan", y = "Jumlah Total Unit Usaha", fill = "Kategori Sektor") +
  scale_fill_manual(values = c("#3498db", "#f1c40f")) +
  theme_minimal()

print(grafik_tahap2a_agregasi)

ggsave("Grafik_Tahap2A_Agregasi_Wilayah.png", plot = grafik_tahap2a_agregasi, width = 8, height = 5, dpi = 300)


# [C] GRAFIK TAHAP 2B: Visualisasi Komposisi Tenaga Kerja
data_2B_tk <- data_agregat_final %>%
  mutate(Total_TK_Keseluruhan = TK_Agro + TK_NonAgro) %>%
  top_n(10, Total_TK_Keseluruhan)

data_2B_tk_long <- data_2B_tk %>%
  pivot_longer(cols = c(TK_Agro, TK_NonAgro), names_to = "Sektor", values_to = "Total_TK") %>%
  mutate(Sektor = factor(Sektor, levels = c("TK_Agro", "TK_NonAgro"), labels = c("Sektor Agro", "Sektor Non-Agro")))

grafik_tahap2b_tk <- ggplot(data_2B_tk_long, aes(x = reorder(Kecamatan, Total_TK_Keseluruhan), y = Total_TK, fill = Sektor)) +
  geom_col(position = "stack") +
  coord_flip() +
  labs(title = "Grafik Tahap 2B: Akumulasi Variabel Serapan Tenaga Kerja",
       x = "Kecamatan", y = "Total Tenaga Kerja", fill = "Kategori Sektor") +
  scale_fill_manual(values = c("#2ecc71", "#9b59b6")) + 
  theme_minimal()

print(grafik_tahap2b_tk)

# PERBAIKAN: plot yang dipanggil sekarang benar (grafik_tahap2b_tk)
ggsave("Grafik_Tahap2B_Agregasi_Wilayah.png", plot = grafik_tahap2b_tk, width = 8, height = 5, dpi = 300)


# [D] GRAFIK TAHAP 2C: Visualisasi Komposisi Modal Usaha
data_2C_modal <- data_agregat_final %>%
  mutate(Total_Modal_Keseluruhan = Modal_Agro + Modal_NonAgro) %>%
  top_n(10, Total_Modal_Keseluruhan)

data_2C_modal_long <- data_2C_modal %>%
  pivot_longer(cols = c(Modal_Agro, Modal_NonAgro), names_to = "Sektor", values_to = "Total_Modal") %>%
  mutate(Sektor = factor(Sektor, levels = c("Modal_Agro", "Modal_NonAgro"), labels = c("Sektor Agro", "Sektor Non-Agro")))

grafik_tahap2c_modal <- ggplot(data_2C_modal_long, aes(x = reorder(Kecamatan, Total_Modal_Keseluruhan), y = Total_Modal, fill = Sektor)) +
  geom_col(position = "stack") +
  coord_flip() +
  labs(title = "Grafik Tahap 2C: Akumulasi Variabel Modal Usaha",
       x = "Kecamatan", y = "Total Modal (Rupiah)", fill = "Kategori Sektor") +
  scale_fill_manual(values = c("#e74c3c", "#34495e")) + 
  theme_minimal()

print(grafik_tahap2c_modal)

# TAMBAHAN: Menyimpan grafik 2C ke dalam file PNG
ggsave("Grafik_Tahap2C_Agregasi_Wilayah.png", plot = grafik_tahap2c_modal, width = 8, height = 5, dpi = 300)

# 2) Handling Missing District (Validasi Penguncian Tepat 47 Kecamatan Resmi)
print("Mengeksekusi Validasi Handling Missing District...")
## [1] "Mengeksekusi Validasi Handling Missing District..."
daftar_47_kecamatan <- tibble(Kecamatan = c(
  "BANTARGADUNG", "BOJONGGENTENG", "CARINGIN", "CIAMBAR", "CIBADAK", "CIBITUNG", 
  "CICANTAYAN", "CICURUG", "CIRACAP", "CIDADAP", "CIDAHU", "CIDOLOG", "CIEMAS", 
  "CIKIDANG", "CIKAKAK", "CIKEMBAR", "CIMANGGU", "CIREUNGHAS", "CISAAT", "CISOLOK", 
  "CURUGKEMBAR", "GEGERBITUNG", "GUNUNGGURUH", "JAMPANGKULON", "JAMPANGTENGAH", "KABANDUNGAN", 
  "KADUDAMPIT", "KALAPANUNGGAL", "KALIBUNDER", "KEBONPEDES", "LENGKONG", "NAGRAK", 
  "NYALINDUNG", "PABUARAN", "PELABUHANRATU", "PARAKANSALAK", "PARUNGKUDA", "PURABAYA", 
  "SAGARANTEN", "SIMPENAN", "SUKABUMI", "SUKALARANG", "SUKARAJA", "SURADE", 
  "TEGALBULEUD", "WALURAN", "WARUNGKIARA"
)) %>% distinct(Kecamatan)

# Gabungkan secara komplet (Kecamatan yang kosong otomatis diisi nilai 0)
data_agregat_final <- daftar_47_kecamatan %>%
  left_join(data_agregat_final, by = "Kecamatan") %>%
  mutate(
    Unit_Agro = replace_na(Unit_Agro, 0),
    Unit_NonAgro = replace_na(Unit_NonAgro, 0),
    TK_Agro = replace_na(TK_Agro, 0),
    TK_NonAgro = replace_na(TK_NonAgro, 0),
    Modal_Agro = replace_na(Modal_Agro, 0),       # Imputasi 0 untuk atribut Modal baru
    Modal_NonAgro = replace_na(Modal_NonAgro, 0)  # Imputasi 0 untuk atribut Modal baru
  )

# 3) Normalisasi Skala dengan teknik Min-Max Scaling (Merujuk Persamaan 2.1 di Bab 2)
normalize <- function(x) {
  if(max(x) == min(x)) return(rep(0, length(x)))
  return ((x - min(x)) / (max(x) - min(x)))
}

data_normal_final <- data_agregat_final
# Terapkan normalisasi pada kolom 2 hingga 7 (Karena ada 6 variabel sekarang)
data_normal_final[2:7] <- as.data.frame(lapply(data_normal_final[2:7], normalize))

# [C] Ekspor data hasil normalisasi skala desimal ke Excel
write_xlsx(data_normal_final, "3_Data_IKM_Normalized_FIX.xlsx")
print("-> Sukses mengekspor berkas '3_Data_IKM_Normalized_FIX.xlsx'")
## [1] "-> Sukses mengekspor berkas '3_Data_IKM_Normalized_FIX.xlsx'"
# [D] GRAFIK TAHAP 2B: Visualisasi Bukti Keberhasilan Penyetaraan Skala (Density Plot)
data_norm_long <- data_normal_final %>%
  pivot_longer(cols = c(Unit_Agro, Unit_NonAgro, TK_Agro, TK_NonAgro, Modal_Agro, Modal_NonAgro),
               names_to = "Variabel", values_to = "Nilai_Skala")

grafik_tahap2b_normalisasi <- ggplot(data_norm_long, aes(x = Nilai_Skala, fill = Variabel)) +
  geom_density(alpha = 0.4) +
  scale_x_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.2)) +
  labs(title = "Grafik Tahap 2B: Distribusi Densitas Atribut Pasca Normalisasi",
       subtitle = "Seluruh 6 dimensi variabel sukses disetarakan adil pada batas skala 0 hingga 1",
       x = "Nilai Skala Desimal Terstandarisasi", y = "Tingkat Kepadatan Data (Density)") +
  theme_minimal()

print(grafik_tahap2b_normalisasi)

ggsave("Grafik_Tahap2B_Hasil_Normalisasi.png", plot = grafik_tahap2b_normalisasi, width = 8, height = 5, dpi = 300)

# ==============================================================================
# TAHAP 3: DATA MINING (PENERAPAN ALGORITMA K-MEANS & PCA)
# ==============================================================================
print("=== TAHAP 3: DATA MINING ===")
## [1] "=== TAHAP 3: DATA MINING ==="
# Konversi kolom Kecamatan menjadi row names agar plot visualisasi memuat label teks wilayah
data_matriks <- as.data.frame(data_normal_final)
rownames(data_matriks) <- data_matriks$Kecamatan
data_matriks$Kecamatan <- NULL 
# 1. PENENTUAN JUMLAH KLASTER (ELBOW METHOD)
# Merujuk pada Persamaan (2.4) di Bab II
grafik_tahap3a_elbow <- fviz_nbclust(data_matriks, kmeans, method = "wss") +
  geom_vline(xintercept = 3, linetype = 2, color = "red", linewidth = 1) + 
  labs(title = "Grafik TAHAP 3A: Penentuan Jumlah Klaster Optimal (Elbow Method)",
       subtitle = "Titik belok sudut siku melandai secara konsisten pada koordinat k = 3",
       x = "Jumlah Klaster (Nilai k)",
       y = "Total Within-Cluster Sum of Squares (WSS)") +
  theme_minimal()

print(grafik_tahap3a_elbow)

ggsave("Grafik_Tahap3A_Elbow_Method.png", plot = grafik_tahap3a_elbow, width = 7, height = 5, dpi = 300)
# 2. PERHITUNGAN JARAK GEOMETRIS DAN ITERASI KLASTER
# Merujuk pada Persamaan (2.2) dan (2.3) di Bab II
set.seed(123) 
hasil_kmeans <- kmeans(data_matriks, centers = 3, nstart = 25) 

# [PENTING] Urutkan klaster secara internal berdasarkan jumlah rata-rata centroid (rowSums)
# Agar pemetaan label selalu konsisten: 1=Sentra (Tertinggi), 2=Penyangga (Sedang), 3=Rintisan (Terendah)
skor_centroid <- rowSums(hasil_kmeans$centers)
urutan <- order(skor_centroid, decreasing = TRUE)

mapping_klaster <- integer(3)
mapping_klaster[urutan[1]] <- 1  
mapping_klaster[urutan[2]] <- 2  
mapping_klaster[urutan[3]] <- 3  

hasil_kmeans$cluster <- mapping_klaster[hasil_kmeans$cluster]
hasil_kmeans$centers <- hasil_kmeans$centers[urutan, ]
# 3. OUTPUT PELABELAN KLASTER & VISUALISASI PCA (PRINCIPAL COMPONENT ANALYSIS)
# Menggabungkan atribut pelabelan terkunci ke data asli
data_hasil_mining <- data_agregat_final %>%
  mutate(Cluster_Statistik = paste("Cluster", hasil_kmeans$cluster))

write_xlsx(data_hasil_mining, "4_Hasil_Data_Mining_KMeans.xlsx")
print("-> Sukses mengekspor berkas '4_Hasil_Data_Mining_KMeans.xlsx'")
## [1] "-> Sukses mengekspor berkas '4_Hasil_Data_Mining_KMeans.xlsx'"
print("Menghitung nilai pusat (Centroid) tiap klaster untuk Tabel 4.5...")
## [1] "Menghitung nilai pusat (Centroid) tiap klaster untuk Tabel 4.5..."
tabel_centroid <- data_hasil_mining %>%
  group_by(Cluster_Statistik) %>%
  summarise(
    Jumlah_Kecamatan = n(),
    Rata_Unit_Agro = round(mean(Unit_Agro), 2),
    Rata_Unit_NonAgro = round(mean(Unit_NonAgro), 2),
    Rata_TK_Agro = round(mean(TK_Agro), 2),
    Rata_TK_NonAgro = round(mean(TK_NonAgro), 2),
    Rata_Modal_Agro = round(mean(Modal_Agro), 0),
    Rata_Modal_NonAgro = round(mean(Modal_NonAgro), 0)
  )

# Ekspor Tabel Centroid ke Excel
write_xlsx(tabel_centroid, "5_Tabel_Centroid_KMeans.xlsx")
print("Berhasil! File '5_Tabel_Centroid_KMeans.xlsx' (Tabel 4.5) telah dibuat.")
## [1] "Berhasil! File '5_Tabel_Centroid_KMeans.xlsx' (Tabel 4.5) telah dibuat."
# Grafik Sebaran Geometris 47 Kecamatan menggunakan PCA (Sesuai Sub-bab 2.6 yang baru)
grafik_tahap3b_klaster <- fviz_cluster(
  hasil_kmeans, 
  data = data_matriks,
  ellipse.type = "convex", 
  palette = c("#2ecc71", "#f1c40f", "#e74c3c"), 
  ggtheme = theme_minimal(),
  main = "Grafik TAHAP 3B: Distribusi Geometris Hasil Klasterisasi IKM",
  subtitle = "Hasil Pemetaan Reduksi Dimensi PCA Terhadap 47 Kecamatan Resmi Kabupaten Sukabumi"
) +
  theme(
    legend.position = "right",
    text = element_text(size = 10),
    axis.text = element_text(size = 8)
  )

print(grafik_tahap3b_klaster)

ggsave("Grafik_Tahap3B_Cluster_PCA.png", plot = grafik_tahap3b_klaster, width = 8, height = 6, dpi = 300)
# 4. EVALUASI VALIDITAS INTERNAL (SILHOUETTE COEFFICIENT)
# Merujuk pada Persamaan (2.5) di Bab II
print("Menghitung Nilai Validitas Silhouette Coefficient...")
## [1] "Menghitung Nilai Validitas Silhouette Coefficient..."
jarak_euclidean <- dist(data_matriks, method = "euclidean")
skor_silhouette <- silhouette(hasil_kmeans$cluster, jarak_euclidean)

grafik_tahap3c_silhouette <- fviz_silhouette(skor_silhouette, print.summary = FALSE) +
  scale_fill_manual(values = c("1" = "#2ecc71", "2" = "#f1c40f", "3" = "#e74c3c")) + 
  scale_color_manual(values = c("1" = "#2ecc71", "2" = "#f1c40f", "3" = "#e74c3c")) +
  labs(title = "Grafik TAHAP 3C: Validasi Klaster dengan Silhouette Coefficient",
       subtitle = "Semakin nilai mendekati 1.0, semakin valid hasil pengelompokan klaster",
       x = "Nomor Urut Kecamatan",
       y = "Nilai Silhouette (Tingkat Validitas)") +
  theme_minimal()
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the factoextra package.
##   Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
print(grafik_tahap3c_silhouette)

ggsave("Grafik_Tahap3C_Validasi_Silhouette.png", plot = grafik_tahap3c_silhouette, width = 8, height = 5, dpi = 300)

rata_rata_sil <- mean(skor_silhouette[, 3])
print("======================================================")
## [1] "======================================================"
print(paste(">>> NILAI RATA-RATA SILHOUETTE SCORE:", round(rata_rata_sil, 4), "<<<"))
## [1] ">>> NILAI RATA-RATA SILHOUETTE SCORE: 0.582 <<<"
print("======================================================")
## [1] "======================================================"
# ==============================================================================
# TAHAP 4: KNOWLEDGE PRESENTATION (SINKRONISASI SHAPEFILE LOKAL & VISUALISASI)
# ==============================================================================
print("=== TAHAP 4: KNOWLEDGE PRESENTATION ===")
## [1] "=== TAHAP 4: KNOWLEDGE PRESENTATION ==="
print("Silakan pilih file Shapefile (.shp) Peta Sukabumi Anda pada jendela pop-up Windows!")
## [1] "Silakan pilih file Shapefile (.shp) Peta Sukabumi Anda pada jendela pop-up Windows!"
nama_file_shp <- file.choose()

print("Membaca data spasial Shapefile asli...")
## [1] "Membaca data spasial Shapefile asli..."
shp_mentah <- st_read(nama_file_shp, quiet = TRUE)

print("Melakukan pemotongan yurisdiksi Kabupaten Sukabumi & Agregasi Spasial...")
## [1] "Melakukan pemotongan yurisdiksi Kabupaten Sukabumi & Agregasi Spasial..."
sukabumi_sf <- shp_mentah %>%
  mutate(
    Kab_Upper = str_to_upper(WADMKK),
    Kec_Upper = str_to_upper(str_trim(WADMKC))
  ) %>%
  filter(str_detect(Kab_Upper, "SUKABUMI") & !str_detect(Kab_Upper, "KOTA")) %>%
  group_by(Kecamatan_Spasial = Kec_Upper) %>%
  summarise(.groups = "drop") %>%
  st_transform(crs = 4326)

print("Menghitung Bobot Persentase Kontribusi Sektoral (Termasuk Modal Usaha)...")
## [1] "Menghitung Bobot Persentase Kontribusi Sektoral (Termasuk Modal Usaha)..."
total_unit_agro_kab <- sum(data_hasil_mining$Unit_Agro, na.rm = TRUE)
total_unit_non_kab  <- sum(data_hasil_mining$Unit_NonAgro, na.rm = TRUE)
total_tk_agro_kab   <- sum(data_hasil_mining$TK_Agro, na.rm = TRUE)
total_tk_non_kab    <- sum(data_hasil_mining$TK_NonAgro, na.rm = TRUE)
total_modal_agro_kab <- sum(data_hasil_mining$Modal_Agro, na.rm = TRUE)
total_modal_non_kab  <- sum(data_hasil_mining$Modal_NonAgro, na.rm = TRUE)

# Fungsi pembantu untuk memformat Rupiah
format_rupiah <- function(angka) {
  paste0("Rp", formatC(angka, format="f", big.mark=".", digits=0))
}

data_hasil_mining_persen <- data_hasil_mining %>%
  mutate(
    Pct_Unit_Agro = round((Unit_Agro / max(total_unit_agro_kab, 1)) * 100, 2),
    Pct_Unit_Non  = round((Unit_NonAgro / max(total_unit_non_kab, 1)) * 100, 2),
    Pct_TK_Agro   = round((TK_Agro / max(total_tk_agro_kab, 1)) * 100, 2),
    Pct_TK_Non    = round((TK_NonAgro / max(total_tk_non_kab, 1)) * 100, 2),
    Pct_Modal_Agro = round((Modal_Agro / max(total_modal_agro_kab, 1)) * 100, 2),
    Pct_Modal_Non  = round((Modal_NonAgro / max(total_modal_non_kab, 1)) * 100, 2),
    
    Alasan_Klaster = case_when(
      Cluster_Statistik == "Cluster 1" ~ paste0("Zona Sentra (Klaster 1): Kontribusi investasi dan unit tinggi (Modal Agro ", Pct_Modal_Agro, "%, Non-Agro ", Pct_Modal_Non, "%)."),
      Cluster_Statistik == "Cluster 2" ~ paste0("Zona Penyangga (Klaster 2): Kontribusi Unit Non-Agro ", Pct_Unit_Non, "% dan serapan TK menengah."),
      Cluster_Statistik == "Cluster 3" ~ paste0("Zona Rintisan (Klaster 3): Aktivitas industri dan investasi di bawah rata-rata."),
      TRUE ~ "Tidak ada catatan aktivitas IKM resmi."
    )
  )

print("Menggabungkan Atribut Hasil Evaluasi Pola ke Objek Spasial...")
## [1] "Menggabungkan Atribut Hasil Evaluasi Pola ke Objek Spasial..."
peta_data_gabungan <- sukabumi_sf %>%
  mutate(Kec_Join = str_replace_all(Kecamatan_Spasial, " ", "")) %>%
  left_join(
    data_hasil_mining_persen %>% mutate(Kec_Join = str_replace_all(Kecamatan, " ", "")), 
    by = "Kec_Join"
  ) %>%
  filter(!is.na(Cluster_Statistik)) 

warnai_zona <- function(cluster_label) {
  case_when(
    cluster_label == "Cluster 1" ~ "#2ecc71", 
    cluster_label == "Cluster 2" ~ "#f1c40f", 
    cluster_label == "Cluster 3" ~ "#e74c3c"  
  )
}

peta_data_gabungan <- peta_data_gabungan %>%
  mutate(Warna_Klaster = warnai_zona(Cluster_Statistik))

print("Menghitung titik koordinat label teks kecamatan...")
## [1] "Menghitung titik koordinat label teks kecamatan..."
suppressWarnings({
  titik_label <- st_point_on_surface(st_geometry(peta_data_gabungan))
})
koordinat_matrix <- st_coordinates(titik_label)
peta_data_gabungan$lng_label <- koordinat_matrix[,1]
peta_data_gabungan$lat_label <- koordinat_matrix[,2]

# ==============================================================================
# SUB-TAHAP 4A: OUTPUT PETA STATIS (.PNG) 
# ==============================================================================
print("Menyusun Output Peta Statis Choropleth...")
## [1] "Menyusun Output Peta Statis Choropleth..."
peta_statis_skripsi <- ggplot(peta_data_gabungan) +
  geom_sf(aes(fill = Cluster_Statistik), color = "white", linewidth = 0.3) + 
  geom_sf_text(aes(label = Kecamatan_Spasial), size = 1.6, fontface = "bold", color = "#2c3e50", check_overlap = TRUE) +
  scale_fill_manual(
    values = c("Cluster 1" = "#2ecc71", "Cluster 2" = "#f1c40f", "Cluster 3" = "#e74c3c"),
    labels = c("Cluster 1: Zona Sentra", "Cluster 2: Zona Penyangga", "Cluster 3: Zona Rintisan")
  ) +
  labs(
    title = "Peta Hasil Penyebaran Klaster Potensi IKM",
    subtitle = "Analisis Spasial Kebijakan Regional Kabupaten Sukabumi",
    fill = "Legenda Klaster"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 12),
    legend.position = "bottom"
  )

print(peta_statis_skripsi)
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

ggsave("Peta_Zonasi_Statis_Bab4.png", plot = peta_statis_skripsi, width = 8, height = 6, dpi = 300)
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data
print("-> Sukses mengekspor gambar cetak 'Peta_Zonasi_Statis_Bab4.png'")
## [1] "-> Sukses mengekspor gambar cetak 'Peta_Zonasi_Statis_Bab4.png'"
# ==============================================================================
# SUB-TAHAP 4B: OUTPUT PETA INTERAKTIF WEB (LEAFLET) 
# ==============================================================================
print("Menyusun Formulasi Peta Interaktif Leaflet Web Engine...")
## [1] "Menyusun Formulasi Peta Interaktif Leaflet Web Engine..."
if (!requireNamespace("htmlwidgets", quietly = TRUE)) { install.packages("htmlwidgets") }

teks_popup <- paste0(
  "<div style='font-family: Arial, sans-serif; font-size: 11px; width: 260px;'>",
  "<strong>Kecamatan: </strong>", peta_data_gabungan$Kecamatan_Spasial, "<br/>",
  "<strong>Status Zonasi: </strong><span style='color:", peta_data_gabungan$Warna_Klaster, "; font-weight:bold;'>", peta_data_gabungan$Cluster_Statistik, "</span><br/>",
  "<hr style='margin: 4px 0;'/>",
  "<strong>Kontribusi thd Total Kabupaten:</strong><br/>",
  "- Unit Agro: ", peta_data_gabungan$Unit_Agro, " (<strong>", peta_data_gabungan$Pct_Unit_Agro, "%</strong>)<br/>",
  "- Unit Non-Agro: ", peta_data_gabungan$Unit_NonAgro, " (<strong>", peta_data_gabungan$Pct_Unit_Non, "%</strong>)<br/>",
  "- TK Agro: ", format(peta_data_gabungan$TK_Agro, big.mark="."), " Orang (<strong>", peta_data_gabungan$Pct_TK_Agro, "%</strong>)<br/>",
  "- TK Non-Agro: ", format(peta_data_gabungan$TK_NonAgro, big.mark="."), " Orang (<strong>", peta_data_gabungan$Pct_TK_Non, "%</strong>)<br/>",
  "- Modal Agro: ", format_rupiah(peta_data_gabungan$Modal_Agro), " (<strong>", peta_data_gabungan$Pct_Modal_Agro, "%</strong>)<br/>",
  "- Modal Non-Agro: ", format_rupiah(peta_data_gabungan$Modal_NonAgro), " (<strong>", peta_data_gabungan$Pct_Modal_Non, "%</strong>)<br/>",
  "<hr style='margin: 4px 0;'/>",
  "<p style='margin:0; text-align:justify; color:#555;'>", peta_data_gabungan$Alasan_Klaster, "</p>",
  "</div>"
)
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
## Warning in prettyNum(r, big.mark = big.mark, big.interval = big.interval, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
## Warning in prettyNum(r, big.mark = big.mark, big.interval = big.interval, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
peta_interaktif_web <- leaflet(peta_data_gabungan, options = leafletOptions(minZoom = 6, maxZoom = 18)) %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  setView(lng = 106.75, lat = -7.05, zoom = 9) %>%
  
  addPolygons(
    fillColor = ~Warna_Klaster, weight = 1.5, opacity = 1, color = "white", dashArray = "3", fillOpacity = 0.65,
    highlightOptions = highlightOptions(weight = 3, color = "#444", dashArray = "", fillOpacity = 0.85, bringToFront = TRUE),
    popup = teks_popup, label = ~Kecamatan_Spasial,
    labelOptions = labelOptions(noHide = FALSE, direction = 'auto', style = list("color" = "#2c3e50", "font-family" = "Arial", "font-weight" = "bold", "font-size" = "11px"))
  ) %>%
  
  addLegend(
    position = "bottomright", colors = c("#2ecc71", "#f1c40f", "#e74c3c"),
    labels = c(
      "Cluster 1: Zona Sentra (Industri & Investasi Tinggi)", 
      "Cluster 2: Zona Penyangga (Ekonomi Berkembang & Padat Karya)", 
      "Cluster 3: Zona Rintisan (Butuh Pembinaan Dinas)"
    ),
    title = "Zonasi Wilayah Potensi IKM", opacity = 0.8
  )

print("Mengekspor peta interaktif ke format HTML mandiri...")
## [1] "Mengekspor peta interaktif ke format HTML mandiri..."
htmlwidgets::saveWidget(peta_interaktif_web, "Peta_Interaktif_IKM_Sukabumi.html", selfcontained = TRUE)
utils::browseURL("Peta_Interaktif_IKM_Sukabumi.html")

print("==============================================================================")
## [1] "=============================================================================="
print("=== PIPELINE INTEGRAL KDD: 100% SUKSES DIEKSEKUSI HINGGA TAHAP PRESENTATION ===")
## [1] "=== PIPELINE INTEGRAL KDD: 100% SUKSES DIEKSEKUSI HINGGA TAHAP PRESENTATION ==="
print("==============================================================================")
## [1] "=============================================================================="