# ==============================================================================
# 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] "=============================================================================="