pkg_needed <- c(
  "readxl", "dplyr", "tidyr", "tibble", "stringr",
  "ggplot2", "ggrepel", "scales", "patchwork",
  "viridis", "RColorBrewer", "classInt",
  "knitr", "kableExtra",
  "deaR", "frontier",
  "sf", "spdep", "spatialreg", "lmtest", "spgwr"
)
pkg_missing <- pkg_needed[!pkg_needed %in% installed.packages()[, "Package"]]
if (length(pkg_missing) > 0) install.packages(pkg_missing, dependencies = TRUE)
invisible(lapply(pkg_needed, library, character.only = TRUE))

Persiapan Data

Load Data & Shapefile

# ══════════════════════════════════════════════════════════════════════
# ⚠  SESUAIKAN KEDUA PATH INI
# ══════════════════════════════════════════════════════════════════════
PATH_EXCEL <- "C:/Users/User/Documents/AKA/Projek Paper/Pustaka 2026/Data Olah Pusaka - 2025.xlsx"
PATH_SHP   <- "C:/Users/User/Documents/AKA/Projek Paper/Pustaka 2026/SHP Jateng/Kab_Jateng_proj.shp"
# ══════════════════════════════════════════════════════════════════════

data_raw <- read_excel(PATH_EXCEL) |>
  rename(jml_penduduk = `jumlah/penduduk`)

data_pusaka <- data_raw |>
  filter(kodekab >= 3301 & kodekab <= 3376) |>
  mutate(
    # kdkab: 2 digit terakhir kode BPS; diawali 7x = kota
    kdkab2  = kodekab %% 100,
    is_kota = (floor(kodekab %% 100 / 10) == 7) |
              (kodekab >= 3371 & kodekab <= 3376),
    # Label peta: "Kab. Cilacap" atau "Kota Semarang"
    label_peta = paste0(
      ifelse(is_kota, "Kota ", "Kab. "),
      str_to_title(nmkab)
    ),
    dmu_id = label_peta   # pakai label_peta sebagai DMU identifier
  )

cat("Observasi :", nrow(data_pusaka), "\n")
## Observasi : 35
cat("Kota      :", sum(data_pusaka$is_kota), "\n")
## Kota      : 6
cat("Kabupaten :", sum(!data_pusaka$is_kota), "\n")
## Kabupaten : 29
cat("Cek NA DEA:\n")
## Cek NA DEA:
data_pusaka |> select(x1_TFI, x2_TII, x3_TRI_sum, y1_TNLI_sum, y2_TSI) |>
  summarise(across(everything(), ~sum(is.na(.)))) |> print()
## # A tibble: 1 × 5
##   x1_TFI x2_TII x3_TRI_sum y1_TNLI_sum y2_TSI
##    <int>  <int>      <int>       <int>  <int>
## 1      0      0          0           0      0
shp_jateng <- st_read(PATH_SHP, quiet = TRUE) |>
  filter(kdkab != "00") |>
  mutate(kodekab = as.numeric(kodekab)) |>
  st_transform(crs = 4326)

shp_merged <- shp_jateng |>
  left_join(data_pusaka, by = "kodekab", suffix = c("_shp", "_data")) |>
  rename(nmkab = nmkab_data) |>
  select(-nmkab_shp)

cat("Baris shapefile :", nrow(shp_merged), "\n")
## Baris shapefile : 35
cat("Cek NA x1_TFI   :", sum(is.na(shp_merged$x1_TFI)), "\n")
## Cek NA x1_TFI   : 0
# ── Helper: centroid + label_peta untuk semua peta ──────────────────
get_centroid_labels <- function(shp) {
  shp |>
    st_centroid() |>
    mutate(
      lon = st_coordinates(geometry)[, 1],
      lat = st_coordinates(geometry)[, 2]
    ) |>
    as.data.frame() |>
    select(label_peta, lon, lat)
}

centroid_labels <- get_centroid_labels(shp_merged)

Statistik Deskriptif

data_pusaka |>
  select(
    `TFI (Kunjungan)`       = x1_TFI,
    `TII (Infrastruktur)`   = x2_TII,
    `TRI (POI Wisata)`      = x3_TRI_sum,
    `NTL (Nighttime Light)` = y1_TNLI_sum,
    `TSI (Rating)`          = y2_TSI,
    `Kepadatan Pddk`        = z_density,
    `PDRB Non-Wisata`       = z_pdrb_nonwisata,
    `Digitalisasi`          = reg_dig,
    `Aksesibilitas`         = reg_akses,
    `Elevasi (mdpl)`        = reg_elevasi,
    `Curah Hujan (mm)`      = x_hujan,
    `PDRB Tersier`          = X_pdrb3
  ) |>
  pivot_longer(everything(), names_to = "Variabel", values_to = "Nilai") |>
  group_by(Variabel) |>
  summarise(
    N    = n(),
    Min  = min(Nilai, na.rm = TRUE),
    Mean = mean(Nilai, na.rm = TRUE),
    Max  = max(Nilai, na.rm = TRUE),
    SD   = sd(Nilai, na.rm = TRUE),
    .groups = "drop"
  ) |>
  kable(digits = 3,
        caption = "Tabel 1. Statistik Deskriptif Variabel Penelitian (n = 35 kab/kota)") |>
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE)
Tabel 1. Statistik Deskriptif Variabel Penelitian (n = 35 kab/kota)
Variabel N Min Mean Max SD
Aksesibilitas 35 0.104 1.656 7.166 1.860
Curah Hujan (mm) 35 2217.609 112131.538 247206.025 64661.520
Digitalisasi 35 0.175 0.939 3.206 0.689
Elevasi (mdpl) 35 5.190 140.432 794.000 195.130
Kepadatan Pddk 35 467.000 2098.257 11324.000 2414.099
NTL (Nighttime Light) 35 761.887 7811.808 22130.805 4139.667
PDRB Non-Wisata 35 11908.269 53514.995 278832.768 47698.544
PDRB Tersier 35 8356.700 22207.418 126008.430 19750.073
TFI (Kunjungan) 35 290.067 6044.113 17342.431 5033.123
TII (Infrastruktur) 35 0.067 0.407 0.952 0.189
TRI (POI Wisata) 35 1915.446 6549.335 11031.689 2459.458
TSI (Rating) 35 4.464 4.561 4.685 0.059

T1: Three-Stage SBM-DEA

Catatan metodologis: Paket deaR dengan model_sbmeff(..., orientation = "oo") menghasilkan skor efisiensi θ_raw ∈ (0, 1]. Untuk memperkaya interpretasi, skor dikonversi ke Faktor Ekspansi Output (FEO = 1/θ_raw). FEO menunjukkan seberapa kali lipat output aktual perlu ditingkatkan agar mencapai frontier efisiensi:

  • FEO = 1 → efisien sempurna (output sudah optimal)
  • FEO = 1.48 → output perlu ditingkatkan ×1.48 (48% lebih tinggi)
  • FEO = 2.89 → output perlu ditingkatkan ×2.89 (189% lebih tinggi)

Semakin kecil FEO = semakin efisien. Dalam tabel dan visualisasi, peringkat 1 adalah daerah dengan FEO terkecil (paling efisien).

Stage 1 — Efisiensi Awal

df_dea <- data_pusaka |>
  select(dmu_id, x1_TFI, x2_TII, x3_TRI_sum, y1_TNLI_sum, y2_TSI) |>
  as.data.frame()

deadata_s1 <- make_deadata(
  datadea = df_dea, dmus = "dmu_id",
  inputs  = c("x1_TFI", "x2_TII", "x3_TRI_sum"),
  outputs = c("y1_TNLI_sum", "y2_TSI")
)

model_s1 <- model_sbmeff(deadata_s1, orientation = "oo", rts = "vrs")

# deaR mengembalikan θ_raw ∈ (0,1]. Konversi ke FEO = 1/θ_raw (≥1).
# FEO = 1 → efisien sempurna | FEO > 1 → butuh ekspansi output
eff_s1_raw  <- sapply(model_s1$DMU, function(x) x$efficiency)
eff_s1_norm <- 1 / eff_s1_raw   # FEO Stage 1

slack_input_s1  <- do.call(rbind, lapply(model_s1$DMU, function(x) x$slack_input))
slack_output_s1 <- do.call(rbind, lapply(model_s1$DMU, function(x) x$slack_output))
rownames(slack_input_s1)  <- data_pusaka$dmu_id
rownames(slack_output_s1) <- data_pusaka$dmu_id
colnames(slack_input_s1)  <- c("slack_TFI", "slack_TII", "slack_TRI")
colnames(slack_output_s1) <- c("slack_NTL", "slack_TSI")

cat("Stage 1 — FEO rata-rata (1/θ_raw):", round(mean(eff_s1_norm, na.rm = TRUE), 4), "\n")
## Stage 1 — FEO rata-rata (1/θ_raw): 1.2907
cat("  → Artinya rata-rata output perlu ×",
    round(mean(eff_s1_norm, na.rm = TRUE), 2), "untuk mencapai frontier\n")
##   → Artinya rata-rata output perlu × 1.29 untuk mencapai frontier
cat("DMU efisien (FEO = 1)  :", sum(eff_s1_norm <= 1.0001, na.rm = TRUE), "\n")
## DMU efisien (FEO = 1)  : 12
cat("Rentang FEO            :", round(min(eff_s1_norm, na.rm = TRUE), 4),
    "–", round(max(eff_s1_norm, na.rm = TRUE), 4), "\n")
## Rentang FEO            : 1 – 2.8913

Stage 2 — Purifikasi SFA (Fried et al., 2002)

df_stage2 <- data.frame(
  dmu_id           = data_pusaka$dmu_id,
  z_density        = data_pusaka$z_density,
  z_pdrb_nonwisata = data_pusaka$z_pdrb_nonwisata,
  x1_TFI           = data_pusaka$x1_TFI,
  x2_TII           = data_pusaka$x2_TII,
  x3_TRI_sum       = data_pusaka$x3_TRI_sum,
  slack_TFI        = slack_input_s1[, "slack_TFI"],
  slack_TII        = slack_input_s1[, "slack_TII"],
  slack_TRI        = slack_input_s1[, "slack_TRI"]
)

adjust_input_sfa <- function(slack_vec, z1, z2, x_vec, var_name) {
  df_sfa <- data.frame(slack = slack_vec, z1 = z1, z2 = z2)
  result <- tryCatch({
    fit_sfa <- sfa(slack ~ z1 + z2, data = df_sfa, ineffDecrease = FALSE)
    b       <- coef(fit_sfa)[c("(Intercept)", "z1", "z2")]
    env_hat <- as.numeric(b[1] + b[2] * z1 + b[3] * z2)
    v_hat   <- as.numeric(residuals(lm(slack ~ z1 + z2, data = df_sfa)))
    cat("  [SFA OK]:", var_name, "\n")
    list(env_hat = env_hat, v_hat = v_hat, metode = "SFA")
  }, error = function(e) {
    cat("  [OLS fallback]:", var_name, "\n")
    fit_ols <- lm(slack ~ z1 + z2, data = df_sfa)
    list(env_hat = as.numeric(fitted(fit_ols)),
         v_hat   = as.numeric(residuals(fit_ols)),
         metode  = "OLS fallback")
  })
  x_adj <- pmax(x_vec +
    (max(result$env_hat) - result$env_hat) +
    (max(result$v_hat)   - result$v_hat), 1e-6)
  list(x_adj = x_adj, metode = result$metode)
}

cat("Purifikasi Stage 2:\n")
## Purifikasi Stage 2:
adj_TFI <- adjust_input_sfa(df_stage2$slack_TFI, df_stage2$z_density,
                             df_stage2$z_pdrb_nonwisata, df_stage2$x1_TFI, "TFI")
##   [SFA OK]: TFI
adj_TII <- adjust_input_sfa(df_stage2$slack_TII, df_stage2$z_density,
                             df_stage2$z_pdrb_nonwisata, df_stage2$x2_TII, "TII")
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
## restarting with starting values multiplied by
## [1] 0.999
##   [SFA OK]: TII
adj_TRI <- adjust_input_sfa(df_stage2$slack_TRI, df_stage2$z_density,
                             df_stage2$z_pdrb_nonwisata, df_stage2$x3_TRI_sum, "TRI")
##   [SFA OK]: TRI
df_adj <- data.frame(
  dmu_id     = df_stage2$dmu_id,
  x1_TFI_adj = adj_TFI$x_adj,
  x2_TII_adj = adj_TII$x_adj,
  x3_TRI_adj = adj_TRI$x_adj
)
cat("Metode — TFI:", adj_TFI$metode, "| TII:", adj_TII$metode,
    "| TRI:", adj_TRI$metode, "\n")
## Metode — TFI: SFA | TII: SFA | TRI: SFA

Stage 3 — Efisiensi Murni

df_dea_s3 <- data.frame(
  dmu_id      = df_adj$dmu_id,
  x1_TFI_adj  = df_adj$x1_TFI_adj,
  x2_TII_adj  = df_adj$x2_TII_adj,
  x3_TRI_adj  = df_adj$x3_TRI_adj,
  y1_TNLI_sum = data_pusaka$y1_TNLI_sum,
  y2_TSI      = data_pusaka$y2_TSI
)

deadata_s3 <- make_deadata(
  datadea = df_dea_s3, dmus = "dmu_id",
  inputs  = c("x1_TFI_adj", "x2_TII_adj", "x3_TRI_adj"),
  outputs = c("y1_TNLI_sum", "y2_TSI")
)

model_s3 <- model_sbmeff(deadata_s3, orientation = "oo", rts = "vrs")

# FEO Stage 3 = efisiensi murni, bebas bias urban (setelah purifikasi SFA)
eff_s3_raw  <- sapply(model_s3$DMU, function(x) x$efficiency)
eff_s3_norm <- 1 / eff_s3_raw   # FEO Stage 3 (≥1; semakin kecil = semakin efisien)

slack_input_s3  <- do.call(rbind, lapply(model_s3$DMU, function(x) x$slack_input))
slack_output_s3 <- do.call(rbind, lapply(model_s3$DMU, function(x) x$slack_output))
rownames(slack_input_s3)  <- data_pusaka$dmu_id
rownames(slack_output_s3) <- data_pusaka$dmu_id
colnames(slack_input_s3)  <- c("slack_TFI", "slack_TII", "slack_TRI")
colnames(slack_output_s3) <- c("slack_NTL", "slack_TSI")

cat("=== Stage 3 (Efisiensi Murni) ===\n")
## === Stage 3 (Efisiensi Murni) ===
cat("FEO₃ rata-rata (DMU inefisien) :", round(mean(eff_s3_norm[eff_s3_norm > 1.0001],
    na.rm = TRUE), 4), "\n")
## FEO₃ rata-rata (DMU inefisien) : 1.438
cat("  → Rata-rata butuh ekspansi output ×",
    round(mean(eff_s3_norm[eff_s3_norm > 1.0001], na.rm = TRUE), 2),
    "(", round((mean(eff_s3_norm[eff_s3_norm > 1.0001], na.rm = TRUE)-1)*100, 1), "% lebih tinggi)\n")
##   → Rata-rata butuh ekspansi output × 1.44 ( 43.8 % lebih tinggi)
cat("DMU efisien (FEO = 1)   :", sum(eff_s3_norm <= 1.0001, na.rm = TRUE), "\n")
## DMU efisien (FEO = 1)   : 10
cat("Rentang FEO₃            :", round(min(eff_s3_norm, na.rm = TRUE), 4),
    "–", round(max(eff_s3_norm, na.rm = TRUE), 4), "\n")
## Rentang FEO₃            : 1 – 2.8913

Hasil T1 — Tabel & Visualisasi

df_hasil_dea <- data.frame(
  label_peta = data_pusaka$label_peta,
  nmkab      = data_pusaka$nmkab,
  kodekab    = data_pusaka$kodekab,
  Theta_S1   = round(as.numeric(eff_s1_norm), 4),   # FEO Stage 1 (≥1)
  Theta_S3   = round(as.numeric(eff_s3_norm), 4)    # FEO Stage 3 (≥1)
) |>
  mutate(
    Perubahan = round(Theta_S3 - Theta_S1, 4),
    # FEO TURUN setelah purifikasi = efisiensi NAIK (bias urban dihilangkan)
    # FEO NAIK setelah purifikasi  = efisiensi TURUN (kondisi lingkungan lebih berat)
    Arah = case_when(
      Perubahan < -0.01 ~ "↑ Naik",    # FEO₃ < FEO₁ → lebih efisien pasca purifikasi
      Perubahan >  0.01 ~ "↓ Turun",   # FEO₃ > FEO₁ → kurang efisien pasca purifikasi
      TRUE              ~ "≈ Tetap"
    )
  ) |>
  arrange(Theta_S3) |>          # ascending: FEO terkecil (paling efisien) di atas
  mutate(Rank = row_number())

median_eff <- median(df_hasil_dea$Theta_S3, na.rm = TRUE)
median_ntl <- median(data_pusaka$y1_TNLI_sum, na.rm = TRUE)

df_hasil_dea <- df_hasil_dea |>
  left_join(data_pusaka |> select(kodekab, y1_TNLI_sum), by = "kodekab") |>
  mutate(
    # FEO kecil (≤ median) = relatif efisien | FEO besar (> median) = relatif inefisien
    Kuadran = case_when(
      Theta_S3 <= median_eff & y1_TNLI_sum >= median_ntl ~ "Efficient-Elite",
      Theta_S3 <= median_eff & y1_TNLI_sum <  median_ntl ~ "Efficient-Transit",
      Theta_S3 >  median_eff & y1_TNLI_sum >= median_ntl ~ "Overwhelmed",
      TRUE                                                ~ "Critical"
    )
  )

# Gabungkan ke shapefile
shp_merged <- shp_merged |>
  left_join(
    df_hasil_dea |> select(kodekab, Theta_S1, Theta_S3, Kuadran, Rank),
    by = "kodekab"
  ) |>
  mutate(eff_s3 = Theta_S3)

pal_kuadran <- c(
  "Efficient-Elite"   = "#2196F3",
  "Efficient-Transit" = "#4CAF50",
  "Overwhelmed"       = "#FF9800",
  "Critical"          = "#F44336"
)

Tabel Perbandingan θ₁ vs θ₃

bind_rows(head(df_hasil_dea, 10), tail(df_hasil_dea, 5)) |>
  select(Rank, label_peta, Theta_S1, Theta_S3, Perubahan, Arah, Kuadran) |>
  kable(
    caption   = "Tabel 2. Perbandingan FEO Stage 1 vs Stage 3 — 10 Terefisien & 5 Terinefisien",
    col.names = c("Rank", "Kab/Kota", "FEO₁ Stage 1", "FEO₃ Stage 3", "Δ FEO", "Arah Efisiensi", "Kuadran")
  ) |>
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE) |>
  row_spec(which(head(bind_rows(head(df_hasil_dea,10), tail(df_hasil_dea,5)), 15)$Arah == "↑ Naik"),
           background = "#d4edda") |>
  row_spec(which(head(bind_rows(head(df_hasil_dea,10), tail(df_hasil_dea,5)), 15)$Arah == "↓ Turun"),
           background = "#f8d7da") |>
  footnote(general = "FEO (Faktor Ekspansi Output) = 1/θ_raw. FEO = 1 → efisien sempurna. FEO = 2.5 → output perlu ditingkatkan ×2.5 (150%) untuk mencapai frontier. Semakin kecil FEO, semakin efisien. ↑ Naik = FEO₃ < FEO₁ → efisiensi meningkat setelah purifikasi bias urban.")
Tabel 2. Perbandingan FEO Stage 1 vs Stage 3 — 10 Terefisien & 5 Terinefisien
Rank Kab/Kota FEO₁ Stage 1 FEO₃ Stage 3 Δ FEO Arah Efisiensi Kuadran
1 Kab. Cilacap 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Elite
2 Kab. Purworejo 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Transit
3 Kab. Grobogan 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Elite
4 Kab. Rembang 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Transit
5 Kab. Pati 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Elite
6 Kab. Demak 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Elite
7 Kab. Kendal 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Elite
8 Kota Magelang 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Transit
9 Kota Semarang 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Elite
10 Kota Tegal 1.0000 1.0000 0.0000 ≈ Tetap Efficient-Transit
31 Kab. Semarang 1.6628 1.6628 0.0000 ≈ Tetap Overwhelmed
32 Kab. Banjarnegara 1.7558 1.7683 0.0125 ↓ Turun Critical
33 Kab. Temanggung 2.0098 2.0101 0.0003 ≈ Tetap Critical
34 Kab. Magelang 2.0210 2.0210 0.0000 ≈ Tetap Critical
35 Kab. Wonosobo 2.8913 2.8913 0.0000 ≈ Tetap Critical
Note:
FEO (Faktor Ekspansi Output) = 1/θ_raw. FEO = 1 → efisien sempurna. FEO = 2.5 → output perlu ditingkatkan ×2.5 (150%) untuk mencapai frontier. Semakin kecil FEO, semakin efisien. ↑ Naik = FEO₃ < FEO₁ → efisiensi meningkat setelah purifikasi bias urban.

Tabel Peringkat Lengkap

df_hasil_dea |>
  select(Rank, label_peta, Theta_S1, Theta_S3, Kuadran) |>
  kable(
    caption   = "Tabel 3. Peringkat Efisiensi Murni Stay-Ready berdasarkan FEO₃ (n = 35)",
    col.names = c("Rank", "Kab/Kota", "FEO₁", "FEO₃", "Kuadran")
  ) |>
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE) |>
  footnote(general = "Rank 1 = FEO₃ terkecil = paling efisien. FEO = 1/θ_raw; nilai ≥ 1.")
Tabel 3. Peringkat Efisiensi Murni Stay-Ready berdasarkan FEO₃ (n = 35)
Rank Kab/Kota FEO₁ FEO₃ Kuadran
1 Kab. Cilacap 1.0000 1.0000 Efficient-Elite
2 Kab. Purworejo 1.0000 1.0000 Efficient-Transit
3 Kab. Grobogan 1.0000 1.0000 Efficient-Elite
4 Kab. Rembang 1.0000 1.0000 Efficient-Transit
5 Kab. Pati 1.0000 1.0000 Efficient-Elite
6 Kab. Demak 1.0000 1.0000 Efficient-Elite
7 Kab. Kendal 1.0000 1.0000 Efficient-Elite
8 Kota Magelang 1.0000 1.0000 Efficient-Transit
9 Kota Semarang 1.0000 1.0000 Efficient-Elite
10 Kota Tegal 1.0000 1.0000 Efficient-Transit
11 Kab. Brebes 1.0254 1.0369 Efficient-Elite
12 Kota Pekalongan 1.0000 1.0379 Efficient-Transit
13 Kab. Sragen 1.0089 1.0413 Efficient-Elite
14 Kab. Tegal 1.0700 1.0802 Efficient-Elite
15 Kab. Kudus 1.1091 1.0986 Efficient-Elite
16 Kab. Purbalingga 1.0923 1.1874 Efficient-Transit
17 Kab. Pekalongan 1.1720 1.1940 Efficient-Transit
18 Kota Surakarta 1.3371 1.2310 Efficient-Transit
19 Kab. Blora 1.1800 1.2612 Critical
20 Kab. Pemalang 1.2754 1.2851 Critical
21 Kab. Wonogiri 1.2901 1.2910 Overwhelmed
22 Kab. Jepara 1.2784 1.2933 Overwhelmed
23 Kab. Klaten 1.3073 1.3075 Overwhelmed
24 Kab. Boyolali 1.3152 1.3154 Overwhelmed
25 Kab. Kebumen 1.3522 1.3653 Critical
26 Kab. Sukoharjo 1.3763 1.4157 Overwhelmed
27 Kota Salatiga 1.0000 1.4394 Critical
28 Kab. Banyumas 1.4822 1.5240 Overwhelmed
29 Kab. Batang 1.5117 1.5388 Critical
30 Kab. Karanganyar 1.6515 1.6519 Overwhelmed
31 Kab. Semarang 1.6628 1.6628 Overwhelmed
32 Kab. Banjarnegara 1.7558 1.7683 Critical
33 Kab. Temanggung 2.0098 2.0101 Critical
34 Kab. Magelang 2.0210 2.0210 Critical
35 Kab. Wonosobo 2.8913 2.8913 Critical
Note:
Rank 1 = FEO₃ terkecil = paling efisien. FEO = 1/θ_raw; nilai ≥ 1.

Scatter θ₁ vs θ₃

ggplot(df_hasil_dea, aes(x = Theta_S1, y = Theta_S3, color = Kuadran)) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "grey55") +
  geom_point(size = 3.5, alpha = 0.85) +
  geom_text_repel(aes(label = label_peta), size = 2.7, max.overlaps = 25) +
  scale_color_manual(values = pal_kuadran) +
  labs(
    title    = "Gambar 1. Scatter Plot FEO₁ vs FEO₃ (SBM Output-Oriented, VRS)",
    subtitle = "Titik DI BAWAH diagonal = FEO₃ < FEO₁ → efisiensi NAIK setelah purifikasi bias urban",
    x = "FEO₁ — Stage 1 (sebelum purifikasi) | semakin kiri = efisien",
    y = "FEO₃ — Stage 3 (efisiensi murni) | semakin bawah = efisien",
    color = "Kuadran",
    caption = "FEO = 1/θ_raw (Faktor Ekspansi Output). FEO = 1 → frontier. FEO > 1 → butuh ekspansi output."
  ) +
  theme_minimal(base_size = 12) + theme(legend.position = "bottom")

Bar Chart Peringkat

df_hasil_dea |>
  mutate(label_peta = reorder(label_peta, -Theta_S3)) |>  # -FEO: efisien (FEO kecil) di atas
  ggplot(aes(x = label_peta, y = Theta_S3, fill = Kuadran)) +
  geom_col(alpha = 0.9) +
  geom_hline(yintercept = median_eff, linetype = "dashed", color = "grey40") +
  scale_fill_manual(values = pal_kuadran) +
  coord_flip() +
  labs(
    title    = "Gambar 2. Peringkat Efisiensi Murni FEO₃ per Kab/Kota (n = 35)",
    subtitle = "Garis putus = median FEO provinsi | Bar pendek (FEO kecil) = efisien | Bar panjang = butuh ekspansi output besar",
    x = NULL, y = "FEO₃ — Faktor Ekspansi Output (1/θ_raw) | nilai ≥ 1", fill = "Kuadran"
  ) +
  theme_minimal(base_size = 11) + theme(legend.position = "bottom")

Matriks Stay-Ready

df_hasil_dea |>
  ggplot(aes(x = Theta_S3, y = y1_TNLI_sum, color = Kuadran)) +
  geom_vline(xintercept = median_eff, linetype = "dashed", color = "grey50") +
  geom_hline(yintercept = median_ntl, linetype = "dashed", color = "grey50") +
  geom_point(size = 3.5, alpha = 0.85) +
  geom_text_repel(aes(label = label_peta), size = 2.7, max.overlaps = 25) +
  scale_color_manual(values = pal_kuadran) +
  labs(
    title    = "Gambar 3. Matriks Stay-Ready 4 Kuadran",
    subtitle = "Garis putus = median provinsi | X: FEO₃ (kiri = efisien, kanan = butuh ekspansi) | Y: NTL (proksi LoS)",
    x = "FEO₃ — Faktor Ekspansi Output (semakin kiri = semakin efisien | nilai = 1 → optimal)",
    y = "NTL Sum — Proksi LoS (aktivitas malam)", color = "Kuadran"
  ) +
  theme_minimal(base_size = 12) + theme(legend.position = "bottom")

Peta Efisiensi

centroid_t1 <- get_centroid_labels(shp_merged)

ggplot(shp_merged) +
  geom_sf(aes(fill = Theta_S3), color = "white", linewidth = 0.3) +
  scale_fill_viridis_c(option = "magma", direction = 1,
                       name = "FEO₃\n(1/θ_raw)") +
  geom_text_repel(
    data = centroid_t1,
    aes(x = lon, y = lat, label = label_peta),
    size = 2.3, max.overlaps = 30, color = "grey15",
    bg.color = "white", bg.r = 0.1
  ) +
  labs(
    title    = "Gambar 3b. Peta Faktor Ekspansi Output (FEO₃) per Kab/Kota Jawa Tengah",
    subtitle = "Warna terang (FEO ≈ 1) = efisien | Warna gelap (FEO tinggi) = butuh ekspansi output besar",
    caption  = "Sumber: Hasil analisis Three-Stage SBM-DEA | FEO = 1/θ_raw"
  ) +
  theme_void(base_size = 11) + theme(legend.position = "right",
  plot.title = element_text(face = "bold"))

Hasil Pembahasan T1

Ringkasan naratif — isikan dengan nilai aktual setelah menjalankan analisis.

Hasil analisis Three-Stage SBM-DEA output-oriented dengan VRS menghasilkan skor efisiensi murni yang direpresentasikan sebagai Faktor Ekspansi Output (FEO₃ = 1/θ_raw). Terdapat 10 daerah yang mencapai efisiensi sempurna (FEO₃ = 1), artinya output aktual (NTL dan TSI) sudah berada di frontier efisiensi.

Di antara daerah yang belum efisien, rata-rata FEO₃ adalah 1.438, mengindikasikan bahwa rata-rata output pariwisata aktual perlu ditingkatkan sebesar 43.8% (atau dilipatgandakan ×1.438) untuk mencapai frontier yang dicapai oleh daerah-daerah benchmark.

Proses purifikasi pada Stage 2 (SFA dengan kontrol kepadatan penduduk dan PDRB Non-Wisata) menghasilkan pergeseran peringkat yang signifikan. Sebanyak 2 daerah mengalami penurunan FEO₃ (efisiensi meningkat) setelah purifikasi — daerah-daerah ini sebelumnya dinilai kurang efisien karena terpengaruh kondisi lingkungan yang tidak menguntungkan, bukan karena kegagalan manajemen sesungguhnya. Sebaliknya, 14 daerah mengalami kenaikan FEO₃ (efisiensi dikoreksi turun) setelah bias urban seperti kepadatan dan aktivitas ekonomi non-wisata disaring.

Daerah dengan efisiensi murni tertinggi (FEO₃ terkecil) adalah Kab. Cilacap (FEO₃ = 1), sedangkan yang memerlukan ekspansi output terbesar adalah Kab. Wonosobo (FEO₃ = 2.8913 → output perlu ×2.8913 atau naik 189.1%).

Klasifikasi matriks Stay-Ready menghasilkan: Efficient-Elite (10 daerah) — FEO rendah dan NTL tinggi, Efficient-Transit (8 daerah) — FEO rendah namun NTL rendah (efisien tapi belum menarik lama tinggal), Overwhelmed (8 daerah) — FEO tinggi namun NTL tinggi (kunjungan besar tapi tidak dikelola efisien), dan Critical (9 daerah) — FEO tinggi dan NTL rendah, memerlukan intervensi kebijakan paling komprehensif.


T2: Analisis Slack

Matriks Diagnostik & Rekomendasi

normalize <- function(x) {
  r <- range(x, na.rm = TRUE)
  if (diff(r) == 0) return(rep(0, length(x)))
  (x - r[1]) / diff(r)
}

df_slack <- data.frame(
  kodekab   = data_pusaka$kodekab,
  label_peta = data_pusaka$label_peta,
  nmkab     = data_pusaka$nmkab,
  s_TFI = as.numeric(slack_input_s3[, "slack_TFI"]),
  s_TII = as.numeric(slack_input_s3[, "slack_TII"]),
  s_TRI = as.numeric(slack_input_s3[, "slack_TRI"]),
  s_NTL = as.numeric(slack_output_s3[, "slack_NTL"]),
  s_TSI = as.numeric(slack_output_s3[, "slack_TSI"])
) |>
  mutate(
    Fenomena = case_when(
      s_TII > quantile(s_TII, 0.75, na.rm = TRUE) &
        s_NTL > quantile(s_NTL, 0.5,  na.rm = TRUE) ~ "Ghost Hotel Syndrome",
      s_TFI > quantile(s_TFI, 0.75, na.rm = TRUE) &
        s_TSI > quantile(s_TSI, 0.5,  na.rm = TRUE) ~ "Mass Tourism Tanpa Kualitas",
      s_TRI > quantile(s_TRI, 0.75, na.rm = TRUE) &
        s_NTL > quantile(s_NTL, 0.5,  na.rm = TRUE) ~ "Connectivity Gap",
      s_TSI > quantile(s_TSI, 0.75, na.rm = TRUE)   ~ "Kualitas Layanan Rendah",
      s_NTL > quantile(s_NTL, 0.75, na.rm = TRUE)   ~ "Aktivitas Malam Rendah",
      TRUE                                            ~ "Relatif Efisien"
    ),
    Prioritas = case_when(
      Fenomena == "Ghost Hotel Syndrome"        ~ "Aktivasi night economy & festival malam",
      Fenomena == "Mass Tourism Tanpa Kualitas" ~ "Pengendalian kuantitas, paket multi-hari",
      Fenomena == "Connectivity Gap"            ~ "Evaluasi konektivitas antar POI",
      Fenomena == "Kualitas Layanan Rendah"     ~ "Peningkatan kualitas hospitality",
      Fenomena == "Aktivitas Malam Rendah"      ~ "Evaluasi produk malam & keamanan",
      TRUE                                      ~ "Pertahankan & kembangkan sirkuit wisata"
    )
  )

df_slack |>
  arrange(desc(s_NTL + s_TSI)) |>
  select(label_peta, s_TFI, s_TII, s_TRI, s_NTL, s_TSI, Fenomena) |>
  mutate(across(s_TFI:s_TSI, ~round(., 3))) |>
  kable(
    caption   = "Tabel 4. Matriks Diagnostik Slack per Kab/Kota (Stage 3)",
    col.names = c("Kab/Kota", "s_TFI", "s_TII", "s_TRI", "s_NTL", "s_TSI", "Diagnosis")
  ) |>
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE) |>
  add_header_above(c(" " = 1,
                     "Input Excess" = 3, "Output Shortfall" = 2, " " = 1))
Tabel 4. Matriks Diagnostik Slack per Kab/Kota (Stage 3)
Input Excess
Output Shortfall
Kab/Kota s_TFI s_TII s_TRI s_NTL s_TSI Diagnosis
Kab. Wonosobo 1020.999 0.000 541.819 17488.994 0.068 Ghost Hotel Syndrome
Kab. Magelang 944.748 0.000 501.824 14826.448 0.056 Ghost Hotel Syndrome
Kab. Semarang 898.719 0.000 523.159 12509.022 0.116 Ghost Hotel Syndrome
Kab. Karanganyar 811.381 0.000 439.983 11661.038 0.014 Connectivity Gap
Kab. Temanggung 409.495 0.000 197.800 9219.767 0.000 Connectivity Gap
Kab. Banyumas 0.000 0.000 0.000 9114.295 0.000 Aktivitas Malam Rendah
Kab. Sukoharjo 0.000 0.026 0.000 8359.609 0.047 Ghost Hotel Syndrome
Kab. Batang 0.000 0.000 0.000 7773.074 0.062 Kualitas Layanan Rendah
Kab. Klaten 676.985 0.000 305.644 7510.330 0.014 Connectivity Gap
Kab. Banjarnegara 0.000 0.000 0.000 7132.386 0.032 Relatif Efisien
Kab. Boyolali 515.420 0.000 279.201 6118.155 0.054 Mass Tourism Tanpa Kualitas
Kab. Wonogiri 0.000 0.000 87.083 4661.397 0.022 Connectivity Gap
Kab. Jepara 0.000 0.000 0.000 4608.817 0.010 Relatif Efisien
Kab. Pemalang 0.000 0.000 0.000 4235.946 0.056 Relatif Efisien
Kab. Kebumen 0.000 0.000 0.000 3929.424 0.000 Relatif Efisien
Kab. Blora 0.000 0.000 0.000 3152.428 0.078 Kualitas Layanan Rendah
Kab. Pekalongan 0.000 0.000 0.000 2386.495 0.072 Kualitas Layanan Rendah
Kota Surakarta 221.907 0.000 0.000 2180.243 0.021 Mass Tourism Tanpa Kualitas
Kab. Tegal 0.000 0.003 0.000 2119.355 0.023 Relatif Efisien
Kab. Kudus 0.000 0.000 0.000 1748.304 0.000 Relatif Efisien
Kab. Purbalingga 0.000 0.000 0.000 1585.267 0.000 Relatif Efisien
Kota Salatiga 132.272 0.000 0.000 1548.503 0.025 Mass Tourism Tanpa Kualitas
Kab. Brebes 0.000 0.000 0.000 632.523 0.061 Kualitas Layanan Rendah
Kab. Sragen 0.000 0.000 0.000 598.316 0.073 Kualitas Layanan Rendah
Kota Pekalongan 0.000 0.039 205.047 98.173 0.102 Kualitas Layanan Rendah
Kab. Grobogan 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kab. Rembang 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kab. Cilacap 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kab. Purworejo 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kab. Pati 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kab. Demak 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kab. Kendal 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kota Magelang 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kota Semarang 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
Kota Tegal 0.000 0.000 0.000 0.000 0.000 Relatif Efisien
df_slack |>
  select(label_peta, Fenomena, Prioritas) |>
  arrange(Fenomena, label_peta) |>
  kable(
    caption   = "Tabel 5. Rekomendasi Kebijakan Berdasarkan Pola Slack",
    col.names = c("Kab/Kota", "Fenomena Terdeteksi", "Prioritas Kebijakan")
  ) |>
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE)
Tabel 5. Rekomendasi Kebijakan Berdasarkan Pola Slack
Kab/Kota Fenomena Terdeteksi Prioritas Kebijakan
Kab. Banyumas Aktivitas Malam Rendah Evaluasi produk malam & keamanan
Kab. Karanganyar Connectivity Gap Evaluasi konektivitas antar POI
Kab. Klaten Connectivity Gap Evaluasi konektivitas antar POI
Kab. Temanggung Connectivity Gap Evaluasi konektivitas antar POI
Kab. Wonogiri Connectivity Gap Evaluasi konektivitas antar POI
Kab. Magelang Ghost Hotel Syndrome Aktivasi night economy & festival malam
Kab. Semarang Ghost Hotel Syndrome Aktivasi night economy & festival malam
Kab. Sukoharjo Ghost Hotel Syndrome Aktivasi night economy & festival malam
Kab. Wonosobo Ghost Hotel Syndrome Aktivasi night economy & festival malam
Kab. Batang Kualitas Layanan Rendah Peningkatan kualitas hospitality
Kab. Blora Kualitas Layanan Rendah Peningkatan kualitas hospitality
Kab. Brebes Kualitas Layanan Rendah Peningkatan kualitas hospitality
Kab. Pekalongan Kualitas Layanan Rendah Peningkatan kualitas hospitality
Kab. Sragen Kualitas Layanan Rendah Peningkatan kualitas hospitality
Kota Pekalongan Kualitas Layanan Rendah Peningkatan kualitas hospitality
Kab. Boyolali Mass Tourism Tanpa Kualitas Pengendalian kuantitas, paket multi-hari
Kota Salatiga Mass Tourism Tanpa Kualitas Pengendalian kuantitas, paket multi-hari
Kota Surakarta Mass Tourism Tanpa Kualitas Pengendalian kuantitas, paket multi-hari
Kab. Banjarnegara Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Cilacap Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Demak Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Grobogan Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Jepara Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Kebumen Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Kendal Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Kudus Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Pati Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Pemalang Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Purbalingga Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Purworejo Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Rembang Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kab. Tegal Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kota Magelang Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kota Semarang Relatif Efisien Pertahankan & kembangkan sirkuit wisata
Kota Tegal Relatif Efisien Pertahankan & kembangkan sirkuit wisata

Visualisasi Slack

Heatmap

df_slack_long <- df_slack |>
  select(label_peta, s_TFI, s_TII, s_TRI, s_NTL, s_TSI) |>
  pivot_longer(-label_peta, names_to = "Variabel", values_to = "Slack") |>
  group_by(Variabel) |>
  mutate(
    Slack_norm = normalize(Slack),
    label_peta = factor(label_peta,
      levels = df_slack$label_peta[order(df_slack$s_NTL + df_slack$s_TSI,
                                          decreasing = TRUE)])
  )

ggplot(df_slack_long, aes(x = Variabel, y = label_peta, fill = Slack_norm)) +
  geom_tile(color = "white", linewidth = 0.4) +
  scale_fill_viridis_c(name = "Intensitas\nSlack (0–1)",
                       option = "magma", direction = -1) +
  scale_x_discrete(labels = c("s_TFI" = "TFI", "s_TII" = "TII",
                               "s_TRI" = "TRI", "s_NTL" = "NTL",
                               "s_TSI" = "TSI")) +
  labs(
    title    = "Gambar 4. Heatmap Intensitas Slack per Kab/Kota",
    subtitle = "Gelap = slack besar = inefisiensi tinggi",
    x = NULL, y = NULL
  ) +
  theme_minimal(base_size = 10) +
  theme(axis.text.y = element_text(size = 8.5), legend.position = "right")

Distribusi Fenomena

df_slack |>
  count(Fenomena) |>
  mutate(Fenomena = reorder(Fenomena, n)) |>
  ggplot(aes(x = Fenomena, y = n, fill = Fenomena)) +
  geom_col(show.legend = FALSE, alpha = 0.85) +
  geom_text(aes(label = n), hjust = -0.3, size = 4) +
  scale_fill_brewer(palette = "Set2") +
  coord_flip() + ylim(0, NA) +
  labs(title = "Gambar 5. Distribusi Fenomena Inefisiensi (n = 35)",
       x = NULL, y = "Jumlah Kab/Kota") +
  theme_minimal(base_size = 12)

Peta Fenomena

shp_slack <- shp_merged |>
  left_join(df_slack |> select(kodekab, Fenomena, Prioritas), by = "kodekab")

centroid_t2 <- get_centroid_labels(shp_slack)

pal_fenomena <- c(
  "Ghost Hotel Syndrome"       = "#e41a1c",
  "Mass Tourism Tanpa Kualitas"= "#ff7f00",
  "Connectivity Gap"           = "#984ea3",
  "Kualitas Layanan Rendah"    = "#a65628",
  "Aktivitas Malam Rendah"     = "#f781bf",
  "Relatif Efisien"            = "#4daf4a"
)

ggplot(shp_slack) +
  geom_sf(aes(fill = Fenomena), color = "white", linewidth = 0.3) +
  scale_fill_manual(values = pal_fenomena, name = "Fenomena Inefisiensi") +
  geom_text_repel(
    data = centroid_t2,
    aes(x = lon, y = lat, label = label_peta),
    size = 2.2, max.overlaps = 30, color = "grey15",
    bg.color = "white", bg.r = 0.1
  ) +
  labs(
    title    = "Gambar 5b. Peta Sebaran Fenomena Inefisiensi Slack",
    subtitle = "Berdasarkan kombinasi slack input dan output tertinggi",
    caption  = "Sumber: Hasil analisis Slack DEA Stage 3"
  ) +
  theme_void(base_size = 11) +
  theme(legend.position = "bottom", plot.title = element_text(face = "bold"),
        legend.text = element_text(size = 9))

Hasil Pembahasan T2

Analisis slack Stage 3 mengidentifikasi enam pola inefisiensi yang tersebar di 35 kabupaten/kota Jawa Tengah. Fenomena dominan yang terdeteksi adalah ‘Relatif Efisien’ yang dialami oleh 17 daerah.

Temuan penting dari analisis ini adalah ditemukannya Ghost Hotel Syndrome pada 4 daerah, yakni kondisi di mana terdapat kelebihan kapasitas infrastruktur akomodasi (excess s_TII) namun tidak diimbangi oleh aktivitas wisata malam yang memadai (shortfall s_NTL). Ini mengindikasikan bahwa masalah rendahnya LoS bukan pada kurangnya fasilitas, melainkan pada absennya produk wisata malam yang dapat menahan wisatawan untuk bermalam.

Sebanyak 17 daerah terklasifikasi ‘Relatif Efisien’ yang menjadi kandidat hub sirkuit wisata lintas kabupaten. Temuan analisis slack menegaskan bahwa intervensi kebijakan tidak boleh seragam (one-size-fits-all), melainkan harus berbasis diagnosis spesifik per wilayah.


T3: Analisis Spasial LISA

Matriks Bobot & Moran’s I Global

nb_queen <- poly2nb(shp_merged, queen = TRUE)
listw    <- nb2listw(nb_queen, style = "W", zero.policy = TRUE)
cat("Ringkasan matriks bobot:\n"); summary(nb_queen)
## Ringkasan matriks bobot:
## Neighbour list object:
## Number of regions: 35 
## Number of nonzero links: 146 
## Percentage nonzero weights: 11.91837 
## Average number of links: 4.171429 
## Link number distribution:
## 
## 1 2 3 4 5 6 7 8 
## 2 5 7 6 8 2 3 2 
## 2 least connected regions:
## 31 35 with 1 link
## 2 most connected regions:
## 11 23 with 8 links
# PENTING: Negasi FEO agar interpretasi LISA konvensional berlaku.
# FEO tinggi = inefisiensi tinggi. Dengan negasi (-FEO):
# nilai tinggi = efisiensi tinggi → High-High = Hot-Spot efisiensi (konvensional)
eff_s3_spasial <- -as.numeric(shp_merged$Theta_S3)   # negasi FEO
if (any(is.na(eff_s3_spasial))) {
  eff_s3_spasial[is.na(eff_s3_spasial)] <- median(eff_s3_spasial, na.rm = TRUE)
}

moran_global <- moran.test(eff_s3_spasial, listw, zero.policy = TRUE)
print(moran_global)
## 
##  Moran I test under randomisation
## 
## data:  eff_s3_spasial  
## weights: listw    
## 
## Moran I statistic standard deviate = 2.0018, p-value = 0.02265
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##        0.18186705       -0.02941176        0.01113961
data.frame(
  Statistik = c("Moran's I", "E(I)", "Varians", "p-value"),
  Nilai     = c(
    round(moran_global$estimate["Moran I statistic"], 4),
    round(moran_global$estimate["Expectation"],       4),
    round(moran_global$estimate["Variance"],          6),
    format.pval(moran_global$p.value, digits = 4, eps = 0.001)
  )
) |>
  kable(caption = "Tabel 6. Uji Moran's I Global — Efisiensi Murni (berbasis FEO₃, dinegasi)") |>
  kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE) |>
  footnote(general = "Variabel yang diuji = -FEO₃ sehingga nilai tinggi = daerah efisien. High-High = klaster daerah efisien (Hot-Spot efisiensi).")
Tabel 6. Uji Moran’s I Global — Efisiensi Murni (berbasis FEO₃, dinegasi)
Statistik Nilai
Moran I statistic Moran’s I 0.1819
Expectation E(I) -0.0294
Variance Varians 0.01114
p-value 0.02265
Note:
Variabel yang diuji = -FEO₃ sehingga nilai tinggi = daerah efisien. High-High = klaster daerah efisien (Hot-Spot efisiensi).

LISA Lokal

lisa_result <- localmoran(eff_s3_spasial, listw, zero.policy = TRUE)

z_score <- scale(eff_s3_spasial)[, 1]
lag_z   <- scale(lag.listw(listw, eff_s3_spasial, zero.policy = TRUE))[, 1]
p_val   <- lisa_result[, "Pr(z != E(Ii))"]

klaster_lisa <- case_when(
  p_val > 0.05                   ~ "Not Significant",
  z_score >= 0 & lag_z >= 0     ~ "High-High (Hot-Spot)",
  z_score <  0 & lag_z <  0     ~ "Low-Low (Cold-Spot)",
  z_score >= 0 & lag_z <  0     ~ "High-Low (Outlier+)",
  z_score <  0 & lag_z >= 0     ~ "Low-High (Outlier−)",
  TRUE                           ~ "Not Significant"
)

shp_merged$Ii_local <- lisa_result[, "Ii"]
shp_merged$p_LISA   <- p_val
shp_merged$klaster  <- klaster_lisa

shp_merged |>
  as.data.frame() |>
  select(label_peta, Theta_S3, Ii_local, p_LISA, klaster) |>
  arrange(klaster, Theta_S3) |>   # ascending: daerah efisien (FEO kecil) duluan
  mutate(across(c(Theta_S3, Ii_local, p_LISA), ~round(., 4))) |>
  kable(
    caption   = "Tabel 7. Hasil LISA per Kab/Kota Jawa Tengah",
    col.names = c("Kab/Kota", "FEO₃", "Ii Lokal", "p-value", "Klaster LISA")
  ) |>
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE) |>
  footnote(general = "FEO₃ = 1/θ_raw; nilai kecil = efisien. Klaster LISA dihitung dari -FEO₃ sehingga High-High = Hot-Spot daerah efisien.")
Tabel 7. Hasil LISA per Kab/Kota Jawa Tengah
Kab/Kota FEO₃ Ii Lokal p-value Klaster LISA
Kab. Purworejo 1.0000 -1.5827 0.0005 High-Low (Outlier+)
Kab. Kendal 1.0000 -1.0307 0.0026 High-Low (Outlier+)
Kab. Banjarnegara 1.7683 0.8983 0.0306 Low-Low (Cold-Spot)
Kab. Temanggung 2.0101 2.6283 0.0009 Low-Low (Cold-Spot)
Kab. Magelang 2.0210 1.5336 0.0128 Low-Low (Cold-Spot)
Kab. Wonosobo 2.8913 2.2146 0.0077 Low-Low (Cold-Spot)
Kab. Grobogan 1.0000 0.2351 0.3541 Not Significant
Kab. Rembang 1.0000 0.3699 0.4859 Not Significant
Kab. Cilacap 1.0000 0.0083 0.9519 Not Significant
Kab. Demak 1.0000 0.2068 0.5018 Not Significant
Kab. Pati 1.0000 0.3699 0.2472 Not Significant
Kota Tegal 1.0000 0.5162 0.3375 Not Significant
Kota Semarang 1.0000 0.1866 0.6470 Not Significant
Kota Magelang 1.0000 -1.4375 0.0765 Not Significant
Kab. Brebes 1.0369 0.2897 0.3674 Not Significant
Kota Pekalongan 1.0379 -0.0955 0.8688 Not Significant
Kab. Sragen 1.0413 -0.0169 0.9942 Not Significant
Kab. Tegal 1.0802 0.1530 0.5669 Not Significant
Kab. Kudus 1.0986 0.3330 0.1935 Not Significant
Kab. Purbalingga 1.1874 -0.1058 0.5053 Not Significant
Kab. Pekalongan 1.1940 -0.0391 0.7773 Not Significant
Kota Surakarta 1.2310 -0.0787 0.5129 Not Significant
Kab. Blora 1.2612 0.1048 0.1583 Not Significant
Kab. Pemalang 1.2851 0.0120 0.7232 Not Significant
Kab. Wonogiri 1.2910 -0.0313 0.4270 Not Significant
Kab. Jepara 1.2933 0.0355 0.2081 Not Significant
Kab. Klaten 1.3075 -0.0018 0.8497 Not Significant
Kab. Boyolali 1.3154 0.0017 0.4071 Not Significant
Kab. Kebumen 1.3653 0.1102 0.0513 Not Significant
Kab. Sukoharjo 1.4157 0.0310 0.7665 Not Significant
Kota Salatiga 1.4394 0.2874 0.3738 Not Significant
Kab. Banyumas 1.5240 -0.0913 0.6557 Not Significant
Kab. Batang 1.5388 0.3892 0.1015 Not Significant
Kab. Karanganyar 1.6519 -0.1187 0.7900 Not Significant
Kab. Semarang 1.6628 0.0804 0.7115 Not Significant
Note:
FEO₃ = 1/θ_raw; nilai kecil = efisien. Klaster LISA dihitung dari -FEO₃ sehingga High-High = Hot-Spot daerah efisien.

Peta & Plot LISA

pal_lisa <- c(
  "High-High (Hot-Spot)" = "#d73027",
  "Low-Low (Cold-Spot)"  = "#4575b4",
  "High-Low (Outlier+)"  = "#fc8d59",
  "Low-High (Outlier−)"  = "#91bfdb",
  "Not Significant"      = "#f7f7f7"
)

Peta LISA

centroid_t3 <- get_centroid_labels(shp_merged)

ggplot(shp_merged) +
  geom_sf(aes(fill = klaster), color = "white", linewidth = 0.3) +
  scale_fill_manual(values = pal_lisa, name = "Klaster LISA") +
  geom_text_repel(
    data = centroid_t3,
    aes(x = lon, y = lat, label = label_peta),
    size = 2.2, max.overlaps = 30, color = "grey15",
    bg.color = "white", bg.r = 0.1
  ) +
  labs(
    title    = "Gambar 6. Peta Klaster LISA — Efisiensi Pariwisata Jawa Tengah",
    subtitle = paste0(
      "Moran's I = ", round(moran_global$estimate[1], 4),
      " | p = ", format.pval(moran_global$p.value, digits = 3),
      " | α = 0.05"
    ),
    caption = "Berdasarkan FEO₃ = 1/θ_raw | High-High = Hot-Spot daerah efisien"
  ) +
  theme_void(base_size = 11) +
  theme(legend.position = "bottom", plot.title = element_text(face = "bold"))

Moran Scatterplot

data.frame(z = z_score, lag_z = lag_z,
           label_peta = shp_merged$label_peta, klaster = klaster_lisa) |>
  ggplot(aes(x = z, y = lag_z, color = klaster, label = label_peta)) +
  geom_hline(yintercept = 0, color = "grey60") +
  geom_vline(xintercept = 0, color = "grey60") +
  geom_point(size = 3, alpha = 0.85) +
  geom_smooth(method = "lm", se = FALSE, color = "black", linewidth = 0.8,
              aes(group = 1)) +
  geom_text_repel(size = 2.5, max.overlaps = 25) +
  scale_color_manual(values = pal_lisa, guide = "none") +
  labs(
    title    = "Gambar 7. Moran Scatterplot — Efisiensi (berbasis −FEO₃)",
    subtitle = paste0("Moran's I = ", round(moran_global$estimate[1], 4),
                      " | p = ", format.pval(moran_global$p.value, digits = 3)),
    x = "z-score θ₃", y = "Spatial Lag z-score"
  ) +
  theme_minimal(base_size = 12)

Distribusi Klaster

shp_merged |>
  as.data.frame() |>
  count(klaster, name = "Jumlah") |>
  mutate(Persen = paste0(round(Jumlah / sum(Jumlah) * 100, 1), "%")) |>
  kable(caption = "Tabel 8. Distribusi Klaster LISA (n = 35)",
        col.names = c("Klaster LISA", "Jumlah Kab/Kota", "Persentase")) |>
  kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
Tabel 8. Distribusi Klaster LISA (n = 35)
Klaster LISA Jumlah Kab/Kota Persentase
High-Low (Outlier+) 2 5.7%
Low-Low (Cold-Spot) 4 11.4%
Not Significant 29 82.9%

Hasil Pembahasan T3

Uji Moran’s I global menghasilkan nilai I = 0.1819 (p = 0.02265), yang mengkonfirmasi adanya autokorelasi spasial positif yang signifikan pada distribusi efisiensi pariwisata di Jawa Tengah. Artinya, efisiensi suatu daerah cenderung dikelilingi oleh daerah dengan tingkat efisiensi yang serupa.

Analisis LISA mengidentifikasi NA kabupaten/kota dalam klaster High-High (Hot-Spot), yakni daerah efisien yang dikelilingi tetangga efisien. Klaster ini menjadi basis alami pengembangan Sirkuit Wisata Terpadu multi-hari — wisatawan dapat didorong untuk bermalam di satu kabupaten kemudian melanjutkan ke kabupaten berikutnya dalam satu koridor.

Sebanyak 4 daerah teridentifikasi dalam klaster Low-Low (Cold-Spot) yang memerlukan intervensi stimulus bersama secara lintas kabupaten, sementara 29 daerah tidak menunjukkan pola spasial yang signifikan.


T4: Geographically Weighted Regression (GWR)

OLS Baseline & Uji Homoskedastisitas

df_reg <- shp_merged |>
  as.data.frame() |>
  dplyr::select(
    kodekab,
    eff_s3      = Theta_S3,
    reg_dig, reg_akses, reg_elevasi, x_hujan
  ) |>
  mutate(eff_s3 = ifelse(is.na(eff_s3), median(eff_s3, na.rm = TRUE), eff_s3))

formula_ols <- eff_s3 ~ reg_dig + reg_akses + reg_elevasi + x_hujan
ols_model   <- lm(formula_ols, data = df_reg)

summary(ols_model)
## 
## Call:
## lm(formula = formula_ols, data = df_reg)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.52738 -0.15380 -0.05406  0.17214  0.48786 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.108e+00  2.017e-01   5.490 5.84e-06 ***
## reg_dig      8.059e-02  9.928e-02   0.812   0.4233    
## reg_akses   -6.969e-02  3.309e-02  -2.106   0.0437 *  
## reg_elevasi  1.509e-03  2.882e-04   5.235 1.20e-05 ***
## x_hujan      2.947e-07  1.084e-06   0.272   0.7875    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2443 on 30 degrees of freedom
## Multiple R-squared:  0.6681, Adjusted R-squared:  0.6238 
## F-statistic: 15.09 on 4 and 30 DF,  p-value: 7.212e-07
bp_test <- bptest(ols_model)
kb_test <- bptest(ols_model, studentize = TRUE)
gq_test <- gqtest(ols_model, order.by = ~ reg_elevasi, data = df_reg)

data.frame(
  Uji       = c("Breusch-Pagan", "Koenker-Bassett", "Goldfeld-Quandt"),
  Statistik = round(c(bp_test$statistic, kb_test$statistic, gq_test$statistic), 4),
  df = c(as.character(bp_test$parameter), as.character(kb_test$parameter),
         paste(gq_test$parameter, collapse = ", ")),
  p_value = c(format.pval(bp_test$p.value, digits = 4, eps = 0.001),
              format.pval(kb_test$p.value, digits = 4, eps = 0.001),
              format.pval(gq_test$p.value, digits = 4, eps = 0.001)),
  Kesimpulan = c(
    ifelse(bp_test$p.value < 0.05, "Heteroskedastik ✓", "Homoskedastik"),
    ifelse(kb_test$p.value < 0.05, "Heteroskedastik ✓", "Homoskedastik"),
    ifelse(gq_test$p.value < 0.05, "Heteroskedastik ✓", "Homoskedastik")
  )
) |>
  kable(
    caption   = "Tabel 8a. Uji Homoskedastisitas Residual OLS",
    col.names = c("Uji", "Statistik", "df", "p-value", "Kesimpulan")
  ) |>
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE) |>
  footnote(general = "H₀: homoskedastik. Tolak H₀ (p<0.05) → heteroskedastisitas → GWR lebih tepat dari OLS.")
Tabel 8a. Uji Homoskedastisitas Residual OLS
Uji Statistik df p-value Kesimpulan
Breusch-Pagan 9.9783 4 0.0408 Heteroskedastik ✓
Koenker-Bassett 9.9783 4 0.0408 Heteroskedastik ✓
Goldfeld-Quandt 2.6053 13, 12 0.05359 Homoskedastik
Note:
H₀: homoskedastik. Tolak H₀ (p<0.05) → heteroskedastisitas → GWR lebih tepat dari OLS.

Persiapan Data GWR

# Pastikan eff_s3 tersedia di shp_merged
shp_merged <- shp_merged |> mutate(eff_s3 = Theta_S3)

gwr_sf_ll <- shp_merged |>
  filter(!is.na(eff_s3), !is.na(reg_dig), !is.na(reg_akses),
         !is.na(reg_elevasi), !is.na(x_hujan)) |>
  st_make_valid() |>
  st_transform(crs = 4326)

centroid_gwr      <- st_centroid(gwr_sf_ll)
coords_gwr        <- st_coordinates(centroid_gwr)
gwr_sf_ll$lon     <- coords_gwr[, 1]
gwr_sf_ll$lat     <- coords_gwr[, 2]

gwr_df            <- st_drop_geometry(gwr_sf_ll)
coordinates(gwr_df) <- ~ lon + lat
proj4string(gwr_df)  <- CRS("+proj=longlat +datum=WGS84")

cat("Observasi GWR:", nrow(gwr_df), "\n")
## Observasi GWR: 35

Seleksi Bandwidth & Perbandingan Kernel

bw_gauss <- gwr.sel(formula_ols, data = gwr_df, adapt = FALSE,
                    gweight = gwr.Gauss, verbose = FALSE)
bw_bisq  <- gwr.sel(formula_ols, data = gwr_df, adapt = FALSE,
                    gweight = gwr.bisquare, verbose = FALSE)
bw_adapt <- gwr.sel(formula_ols, data = gwr_df, adapt = TRUE,
                    gweight = gwr.Gauss, verbose = FALSE)

cat("Bandwidth Gaussian fixed   :", round(bw_gauss, 6), "\n")
## Bandwidth Gaussian fixed   : 52.78095
cat("Bandwidth Bisquare fixed   :", round(bw_bisq,  6), "\n")
## Bandwidth Bisquare fixed   : 158.0082
cat("Bandwidth Gaussian adaptive:", round(bw_adapt, 6), "\n")
## Bandwidth Gaussian adaptive: 0.199961
gwr_gauss <- gwr(formula_ols, data = gwr_df,
                 bandwidth = bw_gauss, gweight = gwr.Gauss,
                 hatmatrix = TRUE, se.fit = TRUE)
gwr_bisq  <- gwr(formula_ols, data = gwr_df,
                 bandwidth = bw_bisq, gweight = gwr.bisquare,
                 hatmatrix = TRUE, se.fit = TRUE)
gwr_adapt <- gwr(formula_ols, data = gwr_df,
                 adapt = bw_adapt, gweight = gwr.Gauss,
                 hatmatrix = TRUE, se.fit = TRUE)

ambil_metrik <- function(model, nama) {
  r    <- capture.output(print(model))
  get  <- function(k) {
    b <- grep(k, r, value = TRUE)
    if (!length(b)) return(NA_real_)
    as.numeric(sub(".*:\\s*", "", b[1]))
  }
  n  <- length(model$SDF$pred)
  k  <- length(all.vars(formula_ols)) - 1
  r2 <- get("Quasi-global R2")
  data.frame(
    Model  = nama,
    AIC    = round(get("^AIC"),           4),
    AICc   = round(get("^AICc"),          4),
    RSS    = round(get("Residual sum"),    4),
    R2     = round(r2,                    4),
    Adj_R2 = round(1 - (1 - r2) * (n - 1) / (n - k - 1), 4)
  )
}

tbl_bw <- rbind(
  ambil_metrik(gwr_gauss, "Gaussian Fixed"),
  ambil_metrik(gwr_bisq,  "Bisquare Fixed"),
  ambil_metrik(gwr_adapt, "Gaussian Adaptive")
)

tbl_bw |>
  kable(caption = "Tabel 9. Perbandingan Model GWR") |>
  kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE) |>
  footnote(general = "Model terbaik: AICc terkecil dan Adj. R² tertinggi.")
Tabel 9. Perbandingan Model GWR
Model AIC AICc RSS R2 Adj_R2
Gaussian Fixed 24.2052 24.2052 1.0022 0.8142 0.7894
Bisquare Fixed 18.9985 18.9985 1.2490 0.7684 0.7376
Gaussian Adaptive 15.7297 15.7297 0.9262 0.8283 0.8054
Note:
Model terbaik: AICc terkecil dan Adj. R² tertinggi.
idx_best  <- which.min(tbl_bw$AICc)
nama_best <- tbl_bw$Model[idx_best]
gwr_best  <- list(gwr_gauss, gwr_bisq, gwr_adapt)[[idx_best]]

cat("\nModel GWR terpilih:", nama_best,
    "| AICc =", tbl_bw$AICc[idx_best],
    "| R² =",   tbl_bw$R2[idx_best], "\n")
## 
## Model GWR terpilih: Gaussian Adaptive | AICc = 15.7297 | R² = 0.8283

Gabung Hasil GWR ke Shapefile

vars_gwr <- c("reg_dig", "reg_akses", "reg_elevasi", "x_hujan")
label_var <- c(
  reg_dig     = "Digitalisasi",
  reg_akses   = "Aksesibilitas",
  reg_elevasi = "Elevasi",
  x_hujan     = "Curah Hujan"
)

gwr_hasil <- as.data.frame(gwr_best$SDF)

for (v in vars_gwr) {
  se_col  <- paste0(v, "_se")
  gwr_hasil[[paste0("t_",   v)]]     <- gwr_hasil[[v]] / gwr_hasil[[se_col]]
  gwr_hasil[[paste0("sig_", v)]]     <- ifelse(abs(gwr_hasil[[paste0("t_", v)]]) > 1.96, "Ya", "Tidak")
  gwr_hasil[[paste0("b_",   v, "_sig")]] <- ifelse(
    abs(gwr_hasil[[paste0("t_", v)]]) > 1.96, gwr_hasil[[v]], NA_real_)
}

gwr_hasil$kodekab <- gwr_sf_ll$kodekab

gwr_sf_poly <- gwr_sf_ll |>
  left_join(
    gwr_hasil |> select(kodekab, starts_with("t_"), starts_with("sig_"),
                         starts_with("b_"), pred, localR2),
    by = "kodekab"
  )

gwr_sp <- as(gwr_sf_poly, "Spatial")
cat("Join selesai — kolom sig:", grep("^sig_", names(gwr_sp@data), value = TRUE), "\n")
## Join selesai — kolom sig: sig_reg_dig sig_reg_akses sig_reg_elevasi sig_x_hujan

Tabel Signifikansi Lokal

sig_counts <- sapply(vars_gwr, function(v)
  sum(gwr_sp@data[[paste0("sig_", v)]] == "Ya", na.rm = TRUE))

data.frame(
  Variabel         = label_var[vars_gwr],
  Signifikan       = sig_counts,
  Tidak_Signifikan = nrow(gwr_sp@data) - sig_counts,
  Persen           = paste0(round(sig_counts / nrow(gwr_sp@data) * 100, 1), "%")
) |>
  kable(
    row.names = FALSE,
    col.names = c("Variabel", "Kab/Kota Signifikan", "Tidak Signifikan", "% Sig."),
    caption   = "Tabel 10. Jumlah Kab/Kota Berdasarkan Signifikansi Lokal GWR"
  ) |>
  kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
Tabel 10. Jumlah Kab/Kota Berdasarkan Signifikansi Lokal GWR
Variabel Kab/Kota Signifikan Tidak Signifikan % Sig.
Digitalisasi 7 28 20%
Aksesibilitas 25 10 71.4%
Elevasi 32 3 91.4%
Curah Hujan 0 35 0%

Peta Signifikansi GWR

# Helper: peta per variabel + label kab/kota
plot_gwr_sig <- function(v, judul) {
  col_sig <- paste0("b_", v, "_sig")
  df_plot <- gwr_sf_poly |>
    mutate(
      b_val  = .data[[col_sig]],
      Status = case_when(
        !is.na(b_val) & b_val > 0 ~ "Positif Sig.",
        !is.na(b_val) & b_val < 0 ~ "Negatif Sig.",
        TRUE                       ~ "Tidak Signifikan"
      )
    )
  centroid_v <- df_plot |>
    st_centroid() |>
    mutate(lon = st_coordinates(geometry)[, 1],
           lat = st_coordinates(geometry)[, 2]) |>
    as.data.frame() |>
    select(label_peta, lon, lat, Status)

  ggplot(df_plot) +
    geom_sf(aes(fill = Status), color = "white", linewidth = 0.25) +
    scale_fill_manual(
      values = c("Positif Sig." = "#2166ac",
                 "Negatif Sig." = "#d73027",
                 "Tidak Signifikan" = "#f0f0f0"),
      name = NULL
    ) +
    geom_text_repel(
      data = centroid_v,
      aes(x = lon, y = lat, label = label_peta),
      size = 1.9, max.overlaps = 25, color = "grey15",
      bg.color = "white", bg.r = 0.08
    ) +
    labs(
      title    = judul,
      subtitle = paste0(
        sum(df_plot$Status == "Positif Sig."), " pos. | ",
        sum(df_plot$Status == "Negatif Sig."), " neg. | ",
        sum(df_plot$Status == "Tidak Signifikan"), " tidak sig."
      )
    ) +
    theme_void(base_size = 10) +
    theme(plot.title    = element_text(face = "bold", size = 10, hjust = 0.5),
          plot.subtitle = element_text(size = 8.5, hjust = 0.5, color = "grey40"),
          legend.position = "bottom", legend.text = element_text(size = 8))
}

Panel 4 Variabel

p_dig     <- plot_gwr_sig("reg_dig",     "Digitalisasi")
p_akses   <- plot_gwr_sig("reg_akses",   "Aksesibilitas Jalan")
p_elevasi <- plot_gwr_sig("reg_elevasi", "Elevasi Rata-rata")
p_hujan   <- plot_gwr_sig("x_hujan",     "Curah Hujan")

(p_dig | p_akses) / (p_elevasi | p_hujan) +
  plot_annotation(
    title    = "Gambar 8. Peta Signifikansi Lokal GWR per Variabel",
    subtitle = paste0("Model: ", nama_best,
                      " | Biru = pengaruh positif sig. | Merah = pengaruh negatif sig."),
    theme = theme(
      plot.title    = element_text(face = "bold", size = 13, hjust = 0.5),
      plot.subtitle = element_text(size = 10, hjust = 0.5, color = "grey40")
    )
  )

Peta Kombinasi

sig_df <- gwr_sp@data[, paste0("sig_", vars_gwr)]
sig_df <- as.data.frame(lapply(sig_df, function(x) x == "Ya"))

gwr_sf_poly$var_sig <- apply(sig_df, 1, function(row) {
  sv <- label_var[vars_gwr[which(row)]]   # pakai label, bukan kode
  if (!length(sv)) "Tidak ada" else paste(sort(sv), collapse = " + ")
})

lvl_urut <- c("Tidak ada",
              sort(setdiff(unique(gwr_sf_poly$var_sig), "Tidak ada")))
gwr_sf_poly$var_sig <- factor(gwr_sf_poly$var_sig, levels = lvl_urut)
n_kat  <- length(lvl_urut)

palet  <- c(
  "#f0f0f0",
  RColorBrewer::brewer.pal(min(max(n_kat - 1, 3), 9), "Set1"),
  if (n_kat > 10) RColorBrewer::brewer.pal(min(n_kat - 10, 8), "Set2"),
  if (n_kat > 18) rep("#888888", n_kat - 18)
)[seq_len(n_kat)]
names(palet) <- lvl_urut

centroid_gwr2 <- gwr_sf_poly |>
  st_centroid() |>
  mutate(lon = st_coordinates(geometry)[, 1],
         lat = st_coordinates(geometry)[, 2]) |>
  as.data.frame() |>
  select(label_peta, lon, lat)

ggplot(gwr_sf_poly) +
  geom_sf(aes(fill = var_sig), color = "white", linewidth = 0.3) +
  scale_fill_manual(values = palet, name = "Variabel Signifikan",
                    guide = guide_legend(ncol = 2)) +
  geom_text_repel(
    data = centroid_gwr2,
    aes(x = lon, y = lat, label = label_peta),
    size = 2.1, max.overlaps = 30, color = "grey15",
    bg.color = "white", bg.r = 0.1
  ) +
  labs(
    title    = "Gambar 9. Peta Kombinasi Variabel Signifikan Lokal GWR",
    subtitle = "Setiap warna = kombinasi unik variabel yang berpengaruh signifikan",
    caption  = paste0("Model GWR: ", nama_best)
  ) +
  theme_void(base_size = 11) +
  theme(legend.position = "bottom", plot.title = element_text(face = "bold"))

Peta Local R²

centroid_r2 <- gwr_sf_poly |>
  st_centroid() |>
  mutate(lon = st_coordinates(geometry)[, 1],
         lat = st_coordinates(geometry)[, 2]) |>
  as.data.frame() |>
  select(label_peta, lon, lat)

ggplot(gwr_sf_poly) +
  geom_sf(aes(fill = localR2), color = "white", linewidth = 0.3) +
  scale_fill_viridis_c(option = "plasma", name = "Local R²") +
  geom_text_repel(
    data = centroid_r2,
    aes(x = lon, y = lat, label = label_peta),
    size = 2.1, max.overlaps = 30, color = "grey15",
    bg.color = "white", bg.r = 0.1
  ) +
  labs(
    title    = "Gambar 10. Peta Local R² Model GWR",
    subtitle = "Semakin tinggi = model menjelaskan variasi efisiensi lebih baik di daerah tersebut",
    caption  = paste0("Model: ", nama_best)
  ) +
  theme_void(base_size = 11) + theme(plot.title = element_text(face = "bold"))

Tabel Rekap Kombinasi

gwr_sf_poly |>
  st_drop_geometry() |>
  group_by(`Kombinasi Variabel Signifikan` = var_sig) |>
  summarise(
    `Jumlah Kab/Kota` = n(),
    `Daftar Kabupaten` = paste(sort(label_peta), collapse = ", "),
    .groups = "drop"
  ) |>
  arrange(desc(`Jumlah Kab/Kota`)) |>
  kable(caption = "Tabel 11. Rekap Kab/Kota per Kombinasi Variabel Signifikan GWR") |>
  kable_styling(bootstrap_options = c("striped", "condensed")) |>
  column_spec(3, width = "45%")
Tabel 11. Rekap Kab/Kota per Kombinasi Variabel Signifikan GWR
Kombinasi Variabel Signifikan Jumlah Kab/Kota Daftar Kabupaten
Aksesibilitas + Elevasi 19 Kab. Banjarnegara, Kab. Batang, Kab. Boyolali, Kab. Demak, Kab. Jepara, Kab. Kebumen, Kab. Kendal, Kab. Kudus, Kab. Magelang, Kab. Pekalongan, Kab. Purbalingga, Kab. Purworejo, Kab. Rembang, Kab. Semarang, Kab. Wonosobo, Kota Magelang, Kota Pekalongan, Kota Salatiga, Kota Semarang
Elevasi 9 Kab. Banyumas, Kab. Brebes, Kab. Cilacap, Kab. Grobogan, Kab. Pati, Kab. Pemalang, Kab. Tegal, Kab. Temanggung, Kota Tegal
Aksesibilitas + Digitalisasi + Elevasi 4 Kab. Blora, Kab. Klaten, Kab. Sukoharjo, Kab. Wonogiri
Aksesibilitas + Digitalisasi 2 Kab. Karanganyar, Kab. Sragen
Digitalisasi 1 Kota Surakarta

Hasil Pembahasan T4

Hasil uji homoskedastisitas Breusch-Pagan menunjukkan adanya heteroskedastisitas pada residual OLS, yang menjadi justifikasi penggunaan Geographically Weighted Regression (GWR) sebagai pendekatan yang lebih tepat dibanding model global (OLS/SLM/SEM). GWR memungkinkan estimasi koefisien yang bervariasi secara spasial, sesuai dengan asumsi bahwa pengaruh determinan LoS berbeda antardaerah di Jawa Tengah.

Model GWR terpilih adalah Gaussian Adaptive dengan AICc = 15.7297 dan quasi-global R² = 0.8283, meningkat 16 poin persentase dibanding OLS (R² = 0.6681). Peningkatan ini mengkonfirmasi adanya non-stasionaritas spasial dalam hubungan antara determinan dan efisiensi LoS.

Secara lokal, variabel yang paling konsisten signifikan adalah Elevasi yang berpengaruh di 32 kabupaten/kota. Peta kombinasi variabel signifikan (Gambar 9) menunjukkan bahwa tidak ada pola pengaruh yang seragam — setiap kelompok wilayah merespons determinan efisiensi LoS yang berbeda, mengimplikasikan bahwa kebijakan peningkatan LoS harus berbasis kewilayahan, bukan kebijakan tunggal untuk seluruh provinsi.


Ringkasan Akhir

# Gabungkan semua hasil ke satu tabel ringkasan
df_ringkasan <- df_hasil_dea |>
  select(kodekab, label_peta, Theta_S3, Kuadran) |>
  left_join(df_slack |> select(kodekab, Fenomena), by = "kodekab") |>
  left_join(
    shp_merged |> as.data.frame() |> select(kodekab, klaster),
    by = "kodekab"
  ) |>
  left_join(
    gwr_sf_poly |> st_drop_geometry() |> select(kodekab, var_sig, localR2),
    by = "kodekab"
  ) |>
  arrange(Theta_S3)   # ascending: FEO terkecil (paling efisien) di atas

df_ringkasan |>
  select(label_peta, Theta_S3, Kuadran, Fenomena, klaster, var_sig, localR2) |>
  mutate(
    Theta_S3 = round(Theta_S3, 4),
    localR2  = round(localR2, 3)
  ) |>
  kable(
    caption   = "Tabel 12. Matriks Rekomendasi Kebijakan Terpadu per Kab/Kota Jawa Tengah",
    col.names = c("Kab/Kota", "FEO₃", "Kuadran Stay-Ready",
                  "Fenomena Slack", "Klaster LISA",
                  "Var. Signifikan GWR", "Local R²")
  ) |>
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = TRUE, font_size = 11) |>
  column_spec(6, width = "20%") |>
  footnote(general = "FEO₃ = 1/θ_raw. Diurutkan dari FEO₃ terkecil (paling efisien) ke terbesar.")
Tabel 12. Matriks Rekomendasi Kebijakan Terpadu per Kab/Kota Jawa Tengah
Kab/Kota FEO₃ Kuadran Stay-Ready Fenomena Slack Klaster LISA Var. Signifikan GWR Local R²
Kab. Cilacap 1.0000 Efficient-Elite Relatif Efisien Not Significant Elevasi 0.850
Kab. Purworejo 1.0000 Efficient-Transit Relatif Efisien High-Low (Outlier+) Aksesibilitas + Elevasi 0.851
Kab. Grobogan 1.0000 Efficient-Elite Relatif Efisien Not Significant Elevasi 0.729
Kab. Rembang 1.0000 Efficient-Transit Relatif Efisien Not Significant Aksesibilitas + Elevasi 0.781
Kab. Pati 1.0000 Efficient-Elite Relatif Efisien Not Significant Elevasi 0.767
Kab. Demak 1.0000 Efficient-Elite Relatif Efisien Not Significant Aksesibilitas + Elevasi 0.774
Kab. Kendal 1.0000 Efficient-Elite Relatif Efisien High-Low (Outlier+) Aksesibilitas + Elevasi 0.850
Kota Magelang 1.0000 Efficient-Transit Relatif Efisien Not Significant Aksesibilitas + Elevasi 0.842
Kota Semarang 1.0000 Efficient-Elite Relatif Efisien Not Significant Aksesibilitas + Elevasi 0.825
Kota Tegal 1.0000 Efficient-Transit Relatif Efisien Not Significant Elevasi 0.840
Kab. Brebes 1.0369 Efficient-Elite Kualitas Layanan Rendah Not Significant Elevasi 0.843
Kota Pekalongan 1.0379 Efficient-Transit Kualitas Layanan Rendah Not Significant Aksesibilitas + Elevasi 0.861
Kab. Sragen 1.0413 Efficient-Elite Kualitas Layanan Rendah Not Significant Aksesibilitas + Digitalisasi 0.728
Kab. Tegal 1.0802 Efficient-Elite Relatif Efisien Not Significant Elevasi 0.843
Kab. Kudus 1.0986 Efficient-Elite Relatif Efisien Not Significant Aksesibilitas + Elevasi 0.773
Kab. Purbalingga 1.1874 Efficient-Transit Relatif Efisien Not Significant Aksesibilitas + Elevasi 0.870
Kab. Pekalongan 1.1940 Efficient-Transit Kualitas Layanan Rendah Not Significant Aksesibilitas + Elevasi 0.872
Kota Surakarta 1.2310 Efficient-Transit Mass Tourism Tanpa Kualitas Not Significant Digitalisasi 0.685
Kab. Blora 1.2612 Critical Kualitas Layanan Rendah Not Significant Aksesibilitas + Digitalisasi + Elevasi 0.773
Kab. Pemalang 1.2851 Critical Relatif Efisien Not Significant Elevasi 0.860
Kab. Wonogiri 1.2910 Overwhelmed Connectivity Gap Not Significant Aksesibilitas + Digitalisasi + Elevasi 0.796
Kab. Jepara 1.2933 Overwhelmed Relatif Efisien Not Significant Aksesibilitas + Elevasi 0.805
Kab. Klaten 1.3075 Overwhelmed Connectivity Gap Not Significant Aksesibilitas + Digitalisasi + Elevasi 0.756
Kab. Boyolali 1.3154 Overwhelmed Mass Tourism Tanpa Kualitas Not Significant Aksesibilitas + Elevasi 0.749
Kab. Kebumen 1.3653 Critical Relatif Efisien Not Significant Aksesibilitas + Elevasi 0.863
Kab. Sukoharjo 1.4157 Overwhelmed Ghost Hotel Syndrome Not Significant Aksesibilitas + Digitalisasi + Elevasi 0.734
Kota Salatiga 1.4394 Critical Mass Tourism Tanpa Kualitas Not Significant Aksesibilitas + Elevasi 0.780
Kab. Banyumas 1.5240 Overwhelmed Aktivitas Malam Rendah Not Significant Elevasi 0.853
Kab. Batang 1.5388 Critical Kualitas Layanan Rendah Not Significant Aksesibilitas + Elevasi 0.867
Kab. Karanganyar 1.6519 Overwhelmed Connectivity Gap Not Significant Aksesibilitas + Digitalisasi 0.725
Kab. Semarang 1.6628 Overwhelmed Ghost Hotel Syndrome Not Significant Aksesibilitas + Elevasi 0.798
Kab. Banjarnegara 1.7683 Critical Relatif Efisien Low-Low (Cold-Spot) Aksesibilitas + Elevasi 0.880
Kab. Temanggung 2.0101 Critical Connectivity Gap Low-Low (Cold-Spot) Elevasi 0.861
Kab. Magelang 2.0210 Critical Ghost Hotel Syndrome Low-Low (Cold-Spot) Aksesibilitas + Elevasi 0.837
Kab. Wonosobo 2.8913 Critical Ghost Hotel Syndrome Low-Low (Cold-Spot) Aksesibilitas + Elevasi 0.879
Note:
FEO₃ = 1/θ_raw. Diurutkan dari FEO₃ terkecil (paling efisien) ke terbesar.
cat("╔══════════════════════════════════════════════════════════════╗\n")
## ╔══════════════════════════════════════════════════════════════╗
cat("║        RINGKASAN HASIL ANALISIS PUSAKA JATENG 2026          ║\n")
## ║        RINGKASAN HASIL ANALISIS PUSAKA JATENG 2026          ║
cat("╠══════════════════════════════════════════════════════════════╣\n")
## ╠══════════════════════════════════════════════════════════════╣
cat("\n▸ T1 — Three-Stage SBM-DEA (Output-Oriented, VRS)\n")
## 
## ▸ T1 — Three-Stage SBM-DEA (Output-Oriented, VRS)
cat("  FEO₁ rata-rata (Stage 1) :", round(mean(eff_s1_norm, na.rm = TRUE), 4), "\n")
##   FEO₁ rata-rata (Stage 1) : 1.2907
cat("  FEO₃ rata-rata (Stage 3) :", round(mean(eff_s3_norm, na.rm = TRUE), 4), "\n")
##   FEO₃ rata-rata (Stage 3) : 1.3128
cat("  DMU efisien (FEO = 1)    :", sum(eff_s3_norm <= 1.0001, na.rm = TRUE), "\n")
##   DMU efisien (FEO = 1)    : 10
feo_ineff <- eff_s3_norm[eff_s3_norm > 1.0001]
cat("  Rata-rata ekspansi output yg dibutuhkan:",
    round((mean(feo_ineff, na.rm = TRUE) - 1) * 100, 1), "% (×",
    round(mean(feo_ineff, na.rm = TRUE), 2), ")\n")
##   Rata-rata ekspansi output yg dibutuhkan: 43.8 % (× 1.44 )
cat("  Distribusi kuadran:\n")
##   Distribusi kuadran:
print(table(df_hasil_dea$Kuadran))
## 
##          Critical   Efficient-Elite Efficient-Transit       Overwhelmed 
##                 9                10                 8                 8
cat("\n▸ T2 — Analisis Slack\n")
## 
## ▸ T2 — Analisis Slack
cat("  Distribusi fenomena:\n")
##   Distribusi fenomena:
print(table(df_slack$Fenomena))
## 
##      Aktivitas Malam Rendah            Connectivity Gap 
##                           1                           4 
##        Ghost Hotel Syndrome     Kualitas Layanan Rendah 
##                           4                           6 
## Mass Tourism Tanpa Kualitas             Relatif Efisien 
##                           3                          17
cat("\n▸ T3 — LISA Spasial\n")
## 
## ▸ T3 — LISA Spasial
cat("  Moran's I global :", round(moran_global$estimate[1], 4), "\n")
##   Moran's I global : 0.1819
cat("  p-value          :", format.pval(moran_global$p.value, digits = 4), "\n")
##   p-value          : 0.02265
cat("  Distribusi klaster:\n")
##   Distribusi klaster:
print(table(shp_merged$klaster))
## 
## High-Low (Outlier+) Low-Low (Cold-Spot)     Not Significant 
##                   2                   4                  29
cat("\n▸ T4 — GWR\n")
## 
## ▸ T4 — GWR
cat("  Model terpilih   :", nama_best, "\n")
##   Model terpilih   : Gaussian Adaptive
cat("  AICc             :", tbl_bw$AICc[idx_best], "\n")
##   AICc             : 15.7297
cat("  Quasi-global R²  :", tbl_bw$R2[idx_best], "\n")
##   Quasi-global R²  : 0.8283
cat("  Sig. per variabel:\n")
##   Sig. per variabel:
for (v in vars_gwr) cat("   ", label_var[v], ":", sig_counts[v], "kab/kota\n")
##     Digitalisasi : 7 kab/kota
##     Aksesibilitas : 25 kab/kota
##     Elevasi : 32 kab/kota
##     Curah Hujan : 0 kab/kota
cat("\n╚══════════════════════════════════════════════════════════════╝\n")
## 
## ╚══════════════════════════════════════════════════════════════╝
## 
## Selesai: 20 April 2026 12:05:29
## Versi R: R version 4.5.3 (2026-03-11 ucrt)
##   deaR : 1.5.4 
##   frontier : 1.1.8 
##   sf : 1.1.0 
##   spdep : 1.4.2 
##   spatialreg : 1.4.3 
##   spgwr : 0.6.37 
##   ggplot2 : 4.0.2 
##   patchwork : 1.3.2