Projek Spasial

Disusun Oleh

  • Nayli Kurnia Ilahi (23031030045)
  • Nisrina Aisyah (23031030041)

1. Library

packages <- c(
  "sf", "spdep", "MASS", "car", "lmtest",
  "dplyr", "ggplot2", "readxl", "knitr",
  "kableExtra", "mgwnbr"
)

for (p in packages) {
  if (!requireNamespace(p, quietly = TRUE)) {
    install.packages(p)
  }
  library(p, character.only = TRUE)
}
## Linking to GEOS 3.14.1, GDAL 3.12.1, PROJ 9.7.1; sf_use_s2() is TRUE
## Loading required package: spData
## To access larger datasets in this package, install the spDataLarge
## package with: `install.packages('spDataLarge',
## repos='https://nowosad.github.io/drat/', type='source')`
## Loading required package: carData
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:car':
## 
##     recode
## The following object is masked from 'package:MASS':
## 
##     select
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
sf_use_s2(FALSE)
## Spherical geometry (s2) switched off
library(readxl)

Input path data

data_path <- "D:/UNY/SEMESTER 6/Statistik Spasial/Projek/Projek akhir/data1.xlsx"
shp_path  <- "D:/UNY/SEMESTER 6/Statistik Spasial/Projek/Projek akhir/jateng.shp"

# Kolom nama wilayah pada data dan shapefile
kolom_wilayah_data <- "Provinsi"
kolom_wilayah_shp  <- "WADMKK"

fungsi bantu

bersih_nama <- function(x) {
  x <- as.character(x)
  x <- iconv(x, to = "ASCII//TRANSLIT")
  x <- tolower(trimws(x))
  x <- gsub("\\s+", " ", x)
  return(x)
}

ubah_numeric <- function(x) {
  x <- as.character(x)
  x <- gsub("\\.", "", x)       # menghapus titik ribuan jika ada
  x <- gsub(",", ".", x)        # mengubah koma desimal menjadi titik
  x <- trimws(x)
  as.numeric(x)
}

ambil_aic_mgwnbr <- function(x) {
  if (is.null(x)) return(NA_real_)
  
  # kalau output berupa numeric vector
  if (is.numeric(x) && !is.null(names(x))) {
    idx <- grep("aic", names(x), ignore.case = TRUE)
    if (length(idx) > 0) return(as.numeric(x[idx[1]]))
  }
  
  # kalau output berupa data frame / matrix
  x_df <- tryCatch(as.data.frame(x), error = function(e) NULL)
  if (is.null(x_df)) return(NA_real_)
  
  kolom_aic <- grep("aic", names(x_df), ignore.case = TRUE, value = TRUE)
  if (length(kolom_aic) > 0) {
    return(as.numeric(x_df[1, kolom_aic[1]]))
  }
  
  baris_aic <- grep("aic", rownames(x_df), ignore.case = TRUE)
  if (length(baris_aic) > 0) {
    return(as.numeric(x_df[baris_aic[1], 1]))
  }
  
  return(NA_real_)
}

4. Load data

shp <- st_read(shp_path, quiet = TRUE)

if (grepl("\\.csv$", data_path, ignore.case = TRUE)) {
  data_raw <- read.csv2(data_path, stringsAsFactors = FALSE, check.names = FALSE)
} else if (grepl("\\.xlsx?$", data_path, ignore.case = TRUE)) {
  data_raw <- readxl::read_excel(data_path)
} else {
  stop("Format data harus .csv, .xls, atau .xlsx")
}

names(data_raw) <- trimws(names(data_raw))
names(shp)      <- trimws(names(shp))

cat("Jumlah wilayah shapefile :", nrow(shp), "\n")
## Jumlah wilayah shapefile : 35
cat("Jumlah baris data        :", nrow(data_raw), "\n")
## Jumlah baris data        : 35
cat("Kolom data               :", paste(names(data_raw), collapse = ", "), "\n")
## Kolom data               : Provinsi, Y, X1, X2, X3, X4
if (!(kolom_wilayah_data %in% names(data_raw))) {
  stop("Kolom wilayah pada data tidak ditemukan.")
}

if (!(kolom_wilayah_shp %in% names(shp))) {
  stop("Kolom wilayah pada shapefile tidak ditemukan.")
}

5. Join data dengan shapefile

data_raw$join_key <- bersih_nama(data_raw[[kolom_wilayah_data]])
shp$join_key      <- bersih_nama(shp[[kolom_wilayah_shp]])

cek_tidak_cocok <- setdiff(data_raw$join_key, shp$join_key)

if (length(cek_tidak_cocok) > 0) {
  cat("\nNama wilayah di data yang tidak cocok dengan shapefile:\n")
  print(cek_tidak_cocok)
}

data_sf <- shp %>%
  left_join(data_raw, by = "join_key")

cat("\nHasil join:", nrow(data_sf), "wilayah\n")
## 
## Hasil join: 35 wilayah

6. Cleaning variabel

variabel_model <- c("Y", "X1", "X2", "X3", "X4")

for (v in variabel_model) {
  if (!(v %in% names(data_sf))) {
    stop(paste("Variabel", v, "tidak ditemukan pada data."))
  }
}

data_sf <- data_sf %>%
  mutate(
    Y  = ubah_numeric(Y),
    X1 = ubah_numeric(X1),
    X2 = ubah_numeric(X2),
    X3 = ubah_numeric(X3),
    X4 = ubah_numeric(X4),
  )

# Y harus berupa data cacah
data_sf$Y_count <- as.integer(round(data_sf$Y))

# Cek missing value
cat("\nMissing value sebelum dibersihkan:\n")
## 
## Missing value sebelum dibersihkan:
print(colSums(is.na(st_drop_geometry(data_sf)[, c("Y_count", "X1", "X2", "X3", "X4")])))
## Y_count      X1      X2      X3      X4 
##       0       0       0       0       0
# Pakai hanya data lengkap
cc <- complete.cases(st_drop_geometry(data_sf)[, c("Y_count", "X1", "X2", "X3", "X4")])
data_sf <- data_sf[cc, ]

if (any(data_sf$Y_count < 0, na.rm = TRUE)) {
  stop("Y_count mengandung nilai negatif. Data count tidak boleh negatif.")
}

cat("\nJumlah data setelah cleaning:", nrow(data_sf), "\n")
## 
## Jumlah data setelah cleaning: 35

7. Koordinat centroid

if (is.na(st_crs(data_sf))) {
  stop("CRS shapefile tidak terbaca. Tetapkan CRS dulu sebelum analisis spasial.")
}

data_sf <- st_make_valid(data_sf)
data_sf <- st_transform(data_sf, 4326)

centroid_sf <- suppressWarnings(st_point_on_surface(data_sf))
coords <- st_coordinates(centroid_sf)

data_sf$long <- coords[, 1]
data_sf$lat  <- coords[, 2]

# Data tanpa geometri untuk model
df_model <- data_sf %>%
  st_drop_geometry() %>%
  mutate(
    wilayah = .data[[kolom_wilayah_shp]]
  )

8. Formula model

x_vars <- c("X1", "X2", "X3", "X4")
form_model <- as.formula(
  paste("Y_count ~", paste(x_vars, collapse = " + "))
)

cat("\nFormula model yang digunakan:\n")
## 
## Formula model yang digunakan:
print(form_model)
## Y_count ~ X1 + X2 + X3 + X4

9. Statistik Deskriptif

df_desc <- df_model[, c("Y_count", x_vars)]

desc_tbl <- data.frame(
  Variabel = names(df_desc),
  N        = sapply(df_desc, function(x) sum(!is.na(x))),
  Min      = sapply(df_desc, min, na.rm = TRUE),
  Mean     = sapply(df_desc, mean, na.rm = TRUE),
  Median   = sapply(df_desc, median, na.rm = TRUE),
  Max      = sapply(df_desc, max, na.rm = TRUE),
  SD       = sapply(df_desc, sd, na.rm = TRUE),
  row.names = NULL
)

kable(desc_tbl, caption = "Statistik Deskriptif Variabel Penelitian", digits = 4) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Statistik Deskriptif Variabel Penelitian
Variabel N Min Mean Median Max SD
Y_count 35 8 257.7143 197 1066 217.1128
X1 35 3 9.0571 8 25 4.6838
X2 35 0 24.7714 15 86 22.2249
X3 35 467 2098.2571 1196 11324 2414.0985
X4 35 11 803.7714 824 1415 421.7383

10. Multikolinearitas

model_vif <- lm(form_model, data = df_model)
vif_val <- car::vif(model_vif)

vif_tbl <- data.frame(
  Variabel   = names(vif_val),
  VIF        = round(vif_val, 4),
  Keputusan  = ifelse(vif_val > 10, "Ada multikolinearitas", "Tidak ada multikolinearitas"),
  row.names  = NULL
)

kable(vif_tbl, caption = "Nilai VIF Prediktor") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Nilai VIF Prediktor
Variabel VIF Keputusan
X1 1.2106 Tidak ada multikolinearitas
X2 1.2374 Tidak ada multikolinearitas
X3 1.2447 Tidak ada multikolinearitas
X4 1.0805 Tidak ada multikolinearitas

11. Model Regresi Poison

model_poisson <- glm(
  form_model,
  family = poisson(link = "log"),
  data = df_model
)

cat("\n==============================\n")
## 
## ==============================
cat("MODEL POISSON\n")
## MODEL POISSON
cat("==============================\n")
## ==============================
print(summary(model_poisson))
## 
## Call:
## glm(formula = form_model, family = poisson(link = "log"), data = df_model)
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  4.638e+00  3.981e-02 116.508   <2e-16 ***
## X1           8.392e-02  2.339e-03  35.876   <2e-16 ***
## X2           6.442e-03  4.798e-04  13.427   <2e-16 ***
## X3          -1.759e-04  8.280e-06 -21.246   <2e-16 ***
## X4           2.468e-04  2.752e-05   8.967   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 5558.4  on 34  degrees of freedom
## Residual deviance: 2946.5  on 30  degrees of freedom
## AIC: 3200.9
## 
## Number of Fisher Scoring iterations: 5

12. Uji overdispersi

dev_pois  <- deviance(model_poisson)
df_pois   <- df.residual(model_poisson)

rasio_dev <- dev_pois / df_pois
rasio_pearson <- sum(residuals(model_poisson, type = "pearson")^2) / df_pois

cat("\n==============================\n")
## 
## ==============================
cat("UJI OVERDISPERSI\n")
## UJI OVERDISPERSI
cat("==============================\n")
## ==============================
cat("Deviance Poisson        :", round(dev_pois, 4), "\n")
## Deviance Poisson        : 2946.503
cat("df residual             :", df_pois, "\n")
## df residual             : 30
cat("Rasio Deviance/df       :", round(rasio_dev, 4), "\n")
## Rasio Deviance/df       : 98.2168
cat("Rasio Pearson ChiSq/df  :", round(rasio_pearson, 4), "\n")
## Rasio Pearson ChiSq/df  : 99.7037
if (rasio_dev > 1 || rasio_pearson > 1) {
  cat("Keputusan: Terjadi overdispersi.\n")
  cat("Artinya model Negative Binomial lebih sesuai dibanding Poisson.\n")
} else {
  cat("Keputusan: Tidak terindikasi overdispersi.\n")
}
## Keputusan: Terjadi overdispersi.
## Artinya model Negative Binomial lebih sesuai dibanding Poisson.

13. Model Negative Binomial global

model_nb <- MASS::glm.nb(
  form_model,
  data = df_model
)

cat("\n==============================\n")
## 
## ==============================
cat("MODEL NEGATIVE BINOMIAL GLOBAL\n")
## MODEL NEGATIVE BINOMIAL GLOBAL
cat("==============================\n")
## ==============================
print(summary(model_nb))
## 
## Call:
## MASS::glm.nb(formula = form_model, data = df_model, init.theta = 2.322325954, 
##     link = log)
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  4.331e+00  3.699e-01  11.709  < 2e-16 ***
## X1           9.316e-02  2.659e-02   3.503 0.000459 ***
## X2           9.399e-03  5.656e-03   1.662 0.096553 .  
## X3          -1.364e-04  5.265e-05  -2.590 0.009587 ** 
## X4           3.259e-04  2.792e-04   1.168 0.242993    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(2.3223) family taken to be 1)
## 
##     Null deviance: 64.770  on 34  degrees of freedom
## Residual deviance: 37.712  on 30  degrees of freedom
## AIC: 448.09
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  2.322 
##           Std. Err.:  0.536 
## 
##  2 x log-likelihood:  -436.087
theta_nb <- model_nb$theta
alpha_nb <- 1 / theta_nb

cat("\nTheta NB dari glm.nb :", round(theta_nb, 6), "\n")
## 
## Theta NB dari glm.nb : 2.322326
cat("Alpha NB = 1/theta   :", round(alpha_nb, 6), "\n")
## Alpha NB = 1/theta   : 0.430603
cat("AIC Poisson          :", round(AIC(model_poisson), 4), "\n")
## AIC Poisson          : 3200.923
cat("AIC NB Global        :", round(AIC(model_nb), 4), "\n")
## AIC NB Global        : 448.0866

14. Uji simultan model NB

model_nb_null <- MASS::glm.nb(Y_count ~ 1, data = df_model)

D_nb <- as.numeric(2 * (logLik(model_nb) - logLik(model_nb_null)))
df_nb <- length(coef(model_nb)) - 1
chi_nb <- qchisq(0.95, df_nb)
p_nb_simultan <- pchisq(D_nb, df = df_nb, lower.tail = FALSE)

cat("\n==============================\n")
## 
## ==============================
cat("UJI SIMULTAN MODEL NB\n")
## UJI SIMULTAN MODEL NB
cat("==============================\n")
## ==============================
cat("D hitung        :", round(D_nb, 4), "\n")
## D hitung        : 20.6441
cat("df              :", df_nb, "\n")
## df              : 4
cat("Chi-square 0.95 :", round(chi_nb, 4), "\n")
## Chi-square 0.95 : 9.4877
cat("p-value         :", round(p_nb_simultan, 6), "\n")
## p-value         : 0.000372
if (p_nb_simultan < 0.05) {
  cat("Keputusan: Tolak H0. Minimal ada satu prediktor berpengaruh.\n")
} else {
  cat("Keputusan: Gagal tolak H0.\n")
}
## Keputusan: Tolak H0. Minimal ada satu prediktor berpengaruh.

15. Uji parsial model NB

coef_nb <- summary(model_nb)$coefficients

parsial_nb <- data.frame(
  Variabel  = rownames(coef_nb),
  Koefisien = round(coef_nb[, 1], 6),
  SE        = round(coef_nb[, 2], 6),
  Z_hitung  = round(coef_nb[, 3], 6),
  P_value   = round(coef_nb[, 4], 6),
  Keputusan = ifelse(coef_nb[, 4] < 0.05, "Signifikan", "Tidak signifikan"),
  row.names = NULL
)

kable(parsial_nb, caption = "Uji Parsial Model Negative Binomial Global") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Uji Parsial Model Negative Binomial Global
Variabel Koefisien SE Z_hitung P_value Keputusan
(Intercept) 4.331190 0.369898 11.709134 0.000000 Signifikan
X1 0.093158 0.026591 3.503386 0.000459 Signifikan
X2 0.009399 0.005656 1.661799 0.096553 Tidak signifikan
X3 -0.000136 0.000053 -2.590394 0.009587 Signifikan
X4 0.000326 0.000279 1.167539 0.242993 Tidak signifikan

16. Matriks ketetanggaan dan Moran’s I residual NB

nb_poly <- spdep::poly2nb(data_sf, queen = TRUE)
## although coordinates are longitude/latitude, st_intersects assumes that they
## are planar
lw_poly <- spdep::nb2listw(nb_poly, style = "W", zero.policy = TRUE)

res_nb <- residuals(model_nb, type = "pearson")

moran_nb <- spdep::moran.test(
  res_nb,
  lw_poly,
  zero.policy = TRUE
)

cat("\n==============================\n")
## 
## ==============================
cat("MORAN'S I RESIDUAL NB\n")
## MORAN'S I RESIDUAL NB
cat("==============================\n")
## ==============================
print(moran_nb)
## 
##  Moran I test under randomisation
## 
## data:  res_nb  
## weights: lw_poly    
## 
## Moran I statistic standard deviate = 0.222, p-value = 0.4122
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##      -0.003913935      -0.029411765       0.013192128
p_moran <- moran_nb$p.value
I_moran <- moran_nb$estimate[["Moran I statistic"]]

cat("\nMoran's I :", round(I_moran, 6), "\n")
## 
## Moran's I : -0.003914
cat("p-value   :", round(p_moran, 6), "\n")
## p-value   : 0.412158
if (p_moran < 0.05) {
  cat("Keputusan: Ada autokorelasi spasial pada residual.\n")
  cat("Artinya analisis GWNBR layak dilanjutkan.\n")
} else {
  cat("Keputusan: Tidak terdapat autokorelasi spasial yang signifikan.\n")
  cat("Catatan: GWNBR masih bisa dicoba, tetapi alasan spasialnya lebih lemah.\n")
}
## Keputusan: Tidak terdapat autokorelasi spasial yang signifikan.
## Catatan: GWNBR masih bisa dicoba, tetapi alasan spasialnya lebih lemah.

17. Uji heterogenitas / Breusch-Pagan

bp_test <- lmtest::bptest(model_vif)

cat("\n==============================\n")
## 
## ==============================
cat("UJI HETEROGENITAS BREUSCH-PAGAN\n")
## UJI HETEROGENITAS BREUSCH-PAGAN
cat("==============================\n")
## ==============================
print(bp_test)
## 
##  studentized Breusch-Pagan test
## 
## data:  model_vif
## BP = 11.641, df = 4, p-value = 0.02023
if (bp_test$p.value < 0.05) {
  cat("Keputusan: Tolak H0. Ada heterogenitas.\n")
  cat("Artinya parameter lokal GWNBR layak dipertimbangkan.\n")
} else {
  cat("Keputusan: Gagal tolak H0. Heterogenitas belum signifikan.\n")
}
## Keputusan: Tolak H0. Ada heterogenitas.
## Artinya parameter lokal GWNBR layak dipertimbangkan.

18. Model GWNBR menggunakan paket mgwnbr

options(warn = -1)
model_gwnbr <- mgwnbr::mgwnbr(
  data           = df_model,
  formula        = form_model,
  long           = "long",
  lat            = "lat",
  band_method    = "adaptive_bsq",
  band_criterion = "aic",
  distribution   = "negbin",
  globalmin      = TRUE,
  multiscale     = FALSE,
  distancekm     = TRUE,
  id             = "wilayah",
  max_int        = 50
)
## NOTE: The denominator degrees of freedom for the t tests is 29.
cat("\n==============================\n")
## 
## ==============================
cat("MODEL GWNBR\n")
## MODEL GWNBR
cat("==============================\n")
## ==============================
print(model_gwnbr)
## General Bandwidth:
## [1] 8
## 
## Fitted Values:
##                 wt    Y        yhat           res
## Cilacap          1  401  548.770403 -147.77040302
## Banyumas         1 1066 1065.715011    0.28498931
## Purbalingga      1  178  190.482695  -12.48269526
## Banjarnegara     1  308  309.569967   -1.56996707
## Kebumen          1  130  130.525113   -0.52511262
## Purworejo        1  336  268.152394   67.84760646
## Wonosobo         1  268  295.509858  -27.50985766
## Magelang         1  157  160.197348   -3.19734811
## Boyolali         1  342  342.164923   -0.16492274
## Klaten           1  505  504.958073    0.04192670
## Sukoharjo        1  251  293.554213  -42.55421262
## Wonogiri         1  197  219.560456  -22.56045566
## Karanganyar      1  362  318.228266   43.77173352
## Sragen           1  249  272.869543  -23.86954325
## Grobogan         1  485  484.279439    0.72056084
## Blora            1  133  143.117441  -10.11744097
## Rembang          1   81   80.297453    0.70254750
## Pati             1  330  356.129687  -26.12968667
## Kudus            1  206  191.199192   14.80080802
## Jepara           1  117  121.677627   -4.67762744
## Demak            1  420  422.662932   -2.66293194
## Semarang         1   56   53.595205    2.40479472
## Temanggung       1   27   29.343752   -2.34375191
## Kendal           1  372  368.314895    3.68510477
## Batang           1   83   86.125262   -3.12526157
## Pekalongan       1  112  111.480197    0.51980275
## Pemalang         1  117  132.522547  -15.52254668
## Tegal            1  629  628.989018    0.01098207
## Brebes           1  615  559.251827   55.74817342
## Kota Magelang    1    9    8.763287    0.23671263
## Kota Surakarta   1  114  112.697072    1.30292777
## Kota Salatiga    1    8    9.526749   -1.52674921
## Kota Semarang    1  131  131.015838   -0.01583776
## Kota Pekalongan  1  161  160.561861    0.43813927
## Kota Tegal       1   64   62.989553    1.01044727
## 
## Goodness of Fit Measures:
##            deviance full_Log_likelihood              pctdev           adjpctdev 
##           7.8384030        -146.7663260           0.9977697           1.0459347 
##                 AIC                AICc 
##         366.8343756        -674.2885691 
## 
## Effective Number of Parameters:
## Intercept        X1        X2        X3        X4      MGWR       GWR     alpha 
## 30.542385 30.542385 30.542385 30.542385 30.542385 30.542385 30.542385  6.108477 
## 
## Parameter Estimates:
##                  Intercept           X1           X2            X3
## Cilacap          5.0392371  0.159633223 -0.001123033 -7.282204e-05
## Banyumas         3.5423022  0.150309181  0.004900755  9.575498e-04
## Purbalingga     11.7868536  0.180409054 -0.078084262 -2.235446e-03
## Banjarnegara    -0.9803372 -0.455808116  0.077442670  2.410718e-03
## Kebumen          8.3759588  0.202960678 -0.076067795 -2.223626e-03
## Purworejo        4.1840615  0.266732242 -0.061641123 -6.796266e-04
## Wonosobo         1.9367883  0.587791140 -0.129507849 -7.742867e-04
## Magelang         2.4591043  0.525616370 -0.112876238 -7.582885e-04
## Boyolali         1.1425618  0.398952753  0.021883742 -2.618487e-04
## Klaten           2.4619184  0.007827100  0.104158106 -6.068827e-05
## Sukoharjo        6.6321243  0.056939255 -0.023391650 -1.566799e-04
## Wonogiri         6.9519833  0.093036391 -0.042148485 -1.956519e-04
## Karanganyar      6.6490205  0.064042264 -0.030704010 -1.711696e-04
## Sragen           6.5022300 -0.004410891  0.008634883 -1.016985e-04
## Grobogan         9.2326632 -0.081946766  0.004066531 -1.276418e-03
## Blora            4.1028186  0.123586949  0.007185064  5.016516e-04
## Rembang          2.8255731  0.152676223  0.011596029  2.408043e-04
## Pati             3.3579048  0.095301268  0.015528369  1.846186e-04
## Kudus            3.7049352  0.045676028  0.008036636 -2.786412e-06
## Jepara           4.1376423  0.026561934  0.012210441 -7.694410e-05
## Demak            1.6737248  0.048327830 -0.012087075  5.123135e-04
## Semarang         3.6905182  0.280755564 -0.061387648 -6.720440e-04
## Temanggung      -4.3691169  1.462402654 -0.012515665 -8.769185e-04
## Kendal           3.2664084  0.005822745  0.042239443 -8.812103e-05
## Batang          -2.9125057 -1.204160417  0.263549370  1.090645e-03
## Pekalongan       1.0079684 -0.291716562  0.070011774  3.896996e-04
## Pemalang         8.9760570  0.185886409 -0.084799927 -2.837082e-04
## Tegal            5.8028712  0.166671661  0.011915663 -1.316734e-04
## Brebes           5.4708947  0.170305764  0.007269430 -1.091629e-04
## Kota Magelang    2.4426562  0.530650009 -0.113988042 -7.626556e-04
## Kota Surakarta   6.1875406  0.036750034 -0.005701447 -1.312591e-04
## Kota Salatiga    3.6915465  0.097263162  0.034334610 -5.447539e-04
## Kota Semarang    3.5811901  0.080211122  0.035993397 -4.892770e-04
## Kota Pekalongan  1.2728294 -0.214251629  0.079220583  3.084919e-04
## Kota Tegal       4.9379924  0.262944080  0.004084498 -3.270503e-05
##                            X4        alpha
## Cilacap         -7.390704e-04 0.0636708743
## Banyumas        -5.862376e-04 0.0000010000
## Purbalingga     -3.153040e-03 0.0782278434
## Banjarnegara     4.213088e-03 0.0000010000
## Kebumen         -6.191258e-04 0.0057897825
## Purworejo        1.005169e-03 0.1312668996
## Wonosobo         2.263604e-03 0.0293529739
## Magelang         1.853832e-03 0.0000010000
## Boyolali        -4.635290e-04 0.0000010000
## Klaten           2.865157e-03 0.0030533428
## Sukoharjo       -1.239388e-03 0.0193597718
## Wonogiri        -1.727705e-03 0.0198953604
## Karanganyar     -1.149289e-03 0.0203977181
## Sragen          -7.875857e-04 0.0152457891
## Grobogan        -1.302515e-03 0.0003237762
## Blora           -4.713007e-04 0.0471078196
## Rembang          5.032652e-04 0.0000010000
## Pati             3.635722e-04 0.0049462703
## Kudus            1.291331e-03 0.0000010000
## Jepara           7.764538e-04 0.0060046036
## Demak            4.102519e-03 0.0000010000
## Semarang         9.739057e-05 0.0318618066
## Temanggung       3.735220e-03 0.0000010000
## Kendal           3.449548e-04 0.1352114491
## Batang           7.637987e-03 0.0082736969
## Pekalongan       3.697047e-03 0.0000010000
## Pemalang        -3.142732e-03 0.0387082044
## Tegal           -1.741547e-03 0.0178578012
## Brebes          -1.421583e-03 0.0535844776
## Kota Magelang    1.856547e-03 0.0000010000
## Kota Surakarta  -7.373223e-04 0.0260810633
## Kota Salatiga    1.394895e-03 0.0915100219
## Kota Semarang   -4.545999e-04 0.0000010000
## Kota Pekalongan  2.879960e-03 0.0330226218
## Kota Tegal      -1.888519e-03 0.0000010000
## 
## Alpha Levels for Parameter Significance Tests:
##   Intercept          X1          X2          X3          X4        MGWR 
## 0.008185346 0.008185346 0.008185346 0.008185346 0.008185346 0.008185346 
##         GWR       alpha 
## 0.008185346 0.008185346 
## 
## Critical Values for Parameter Significance Tests:
## Intercept        X1        X2        X3        X4      MGWR       GWR     alpha 
##      4.53      4.53      4.53      4.53      4.53      4.53      4.53      4.53 
## 
## Standard Errors for Parameter Estimates:
##                 Intercept          X1          X2           X3           X4
## Cilacap         0.5312326 0.032290354 0.007157249 7.929872e-05 4.235313e-04
## Banyumas        0.2329360 0.006209268 0.001376785 9.454190e-05 9.747809e-05
## Purbalingga     2.0882921 0.033872887 0.015426323 8.805393e-04 7.558922e-04
## Banjarnegara    2.1056137 0.106089659 0.023748084 7.678188e-04 8.030052e-04
## Kebumen         1.0042269 0.018416335 0.006292032 3.432885e-04 4.921227e-04
## Purworejo       2.5766511 0.218530767 0.058013572 1.499437e-04 7.886755e-04
## Wonosobo        0.9396100 0.077963644 0.020658689 8.409440e-05 3.081474e-04
## Magelang        0.4575989 0.038179217 0.009459021 6.226593e-05 1.224427e-04
## Boyolali        0.3770084 0.034399131 0.004715689 1.868503e-05 1.286888e-04
## Klaten          0.5039468 0.023236682 0.010932055 2.074555e-05 3.644154e-04
## Sukoharjo       0.5219908 0.033269584 0.011596547 3.444235e-05 3.074185e-04
## Wonogiri        0.6689032 0.039778107 0.015488458 4.192212e-05 4.039296e-04
## Karanganyar     0.5713612 0.035716760 0.013317448 3.782250e-05 3.195217e-04
## Sragen          0.4770897 0.028150551 0.002919103 2.759336e-05 4.770471e-04
## Grobogan        0.6703231 0.033913968 0.001881376 2.417717e-04 3.531902e-04
## Blora           1.2809294 0.043623905 0.005046148 4.467619e-04 8.218481e-04
## Rembang         0.4035461 0.026889028 0.002741458 1.016062e-04 2.513021e-04
## Pati            0.4620105 0.021693124 0.001896264 1.414881e-04 2.993008e-04
## Kudus           0.2857620 0.007671253 0.002107133 6.171139e-05 2.703069e-04
## Jepara          0.4099190 0.012713067 0.002872528 9.245179e-05 3.832488e-04
## Demak           1.8213676 0.009461351 0.012898480 3.849635e-04 1.853896e-03
## Semarang        0.6173247 0.030706760 0.014439102 8.543316e-05 3.247741e-04
## Temanggung      1.2245382 0.123586062 0.002926113 6.967521e-05 2.928618e-04
## Kendal          1.0076502 0.064882707 0.010617254 3.130590e-04 3.999601e-04
## Batang          1.9477817 0.178425893 0.026806064 1.355009e-04 6.700751e-04
## Pekalongan      0.5454190 0.030679577 0.013264913 3.891421e-05 3.834078e-04
## Pemalang        0.8811081 0.044709293 0.022981098 6.024742e-05 6.281075e-04
## Tegal           0.3016100 0.016457263 0.004177296 4.359550e-05 2.483138e-04
## Brebes          0.4715367 0.029219050 0.006445536 6.599982e-05 3.669214e-04
## Kota Magelang   0.4576011 0.038238159 0.009461626 6.156373e-05 1.228595e-04
## Kota Surakarta  0.6394957 0.038660053 0.014163431 3.858359e-05 3.938149e-04
## Kota Salatiga   1.1188463 0.057524277 0.024031871 9.725101e-05 5.626867e-04
## Kota Semarang   0.3773033 0.024829451 0.002372228 1.214037e-04 1.357323e-04
## Kota Pekalongan 1.3449211 0.087961965 0.027434232 9.842689e-05 9.681721e-04
## Kota Tegal      0.1978617 0.016170638 0.003386150 1.958337e-05 1.113724e-04
##                        alpha
## Cilacap         5.264082e-02
## Banyumas        2.830441e-05
## Purbalingga     6.694274e-02
## Banjarnegara    4.133834e-05
## Kebumen         7.791436e-03
## Purworejo       1.083395e-01
## Wonosobo        3.913585e-02
## Magelang        8.345049e-05
## Boyolali        4.727340e-05
## Klaten          4.335494e-03
## Sukoharjo       1.564508e-02
## Wonogiri        1.752868e-02
## Karanganyar     1.806303e-02
## Sragen          1.444847e-02
## Grobogan        2.282221e-03
## Blora           4.514423e-02
## Rembang         4.749461e-05
## Pati            6.606592e-03
## Kudus           4.683118e-05
## Jepara          7.980224e-03
## Demak           4.023561e-05
## Semarang        4.999973e-02
## Temanggung      9.569966e-05
## Kendal          1.276061e-01
## Batang          1.229117e-02
## Pekalongan      6.995792e-05
## Pemalang        3.714384e-02
## Tegal           1.955867e-02
## Brebes          4.092498e-02
## Kota Magelang   1.322525e-04
## Kota Surakarta  2.335085e-02
## Kota Salatiga   9.729921e-02
## Kota Semarang   4.519957e-05
## Kota Pekalongan 3.103441e-02
## Kota Tegal      4.356338e-05
## 
## Global Parameter Estimates:
##               Par. Est.    Std Error   t Value     Pr > |t|
## Intercept  4.3311902301 3.992715e-01 10.847731 1.009548e-11
## X1         0.0931576112 2.817338e-02  3.306583 2.523655e-03
## X2         0.0093994014 5.452890e-03  1.723747 9.540075e-02
## X3        -0.0001363947 4.983132e-05 -2.737128 1.047474e-02
## X4         0.0003259440 2.855230e-04  1.141568 2.629725e-01
## alpha      0.4306027746 9.937915e-02  4.332929 1.609572e-04
## 
## Denominator Degrees of Freedom for the T Tests:
## [1] 29
## 
## Goodness of Fit Statistics for the Global Model:
##            deviance full_Log_likelihood             pctdevg          adjpctdevg 
##          37.7122798        -315.7223367           0.4177487           0.3401152 
##                 AIC                AICc 
##         643.4446734         646.4446734
cat("\nRingkasan model GWNBR:\n")
## 
## Ringkasan model GWNBR:
print(summary(model_gwnbr))
## Model Summary:
## 
## General Bandwidth:
## [1] 8
## 
## Effective Number of Parameters and T Test Information:
##                      Intercept           X1           X2           X3
## ENP               30.542384854 30.542384854 30.542384854 30.542384854
## alpha_level_5_pct  0.008185346  0.008185346  0.008185346  0.008185346
## t_critical         4.530000000  4.530000000  4.530000000  4.530000000
##                             X4         MGWR          GWR       alpha
## ENP               30.542384854 30.542384854 30.542384854 6.108476971
## alpha_level_5_pct  0.008185346  0.008185346  0.008185346 0.008185346
## t_critical         4.530000000  4.530000000  4.530000000 4.530000000
## 
## Five Number Summary for Parameter Estimates:
##        Intercept          X1           X2            X3            X4
## Min    -4.369117 -1.20416042 -0.129507849 -2.235446e-03 -3.153040e-03
## 1Q      2.450880  0.03165598 -0.036426248 -6.083990e-04 -9.684372e-04
## Median  3.691547  0.09726316  0.004900755 -1.312591e-04  9.739057e-05
## 3Q      5.995206  0.19442354  0.018706056  9.091611e-05  1.855189e-03
## Max    11.786854  1.46240265  0.263549370  2.410718e-03  7.637987e-03
##              alpha
## Min    0.000001000
## 1Q     0.000001000
## Median 0.008273697
## 3Q     0.032442214
## Max    0.135211449
## 
## Five Number Summary for Standard Errors:
##        Intercept          X1          X2           X3           X4        alpha
## Min    0.1978617 0.006209268 0.001376785 1.868503e-05 9.747809e-05 2.830441e-05
## 1Q     0.4576000 0.024033067 0.003156131 4.275881e-05 2.815843e-04 7.670421e-05
## Median 0.5713612 0.033872887 0.009461626 8.409440e-05 3.669214e-04 1.229117e-02
## 3Q     1.0632483 0.044166599 0.014932712 1.384945e-04 5.274047e-04 3.813984e-02
## Max    2.5766511 0.218530767 0.058013572 8.805393e-04 1.853896e-03 1.276061e-01
## 
## Deviance: 7.8384, Full Log-Likelihood: -146.7663
## AIC: 366.8344, AICc: -674.2886
cat("\nNama komponen output mgwnbr:\n")
## 
## Nama komponen output mgwnbr:
print(names(model_gwnbr))
##  [1] "general_bandwidth"      "fitted_values"          "measures"              
##  [4] "ENP"                    "mgwr_param_estimates"   "alpha_level_5_pct"     
##  [7] "t_critical"             "mgwr_se"                "global_param_estimates"
## [10] "t_test_dfs"             "global_measures"

19. Goodness of fit GWNBR

cat("\n==============================\n")
## 
## ==============================
cat("GOODNESS OF FIT GWNBR\n")
## GOODNESS OF FIT GWNBR
cat("==============================\n")
## ==============================
if (!is.null(model_gwnbr$general_bandwidth)) {
  cat("\nBandwidth umum:\n")
  print(model_gwnbr$general_bandwidth)
}
## 
## Bandwidth umum:
## [1] 8
if (!is.null(model_gwnbr$band)) {
  cat("\nBandwidth:\n")
  print(model_gwnbr$band)
}

if (!is.null(model_gwnbr$measures)) {
  cat("\nMeasures GWNBR:\n")
  print(model_gwnbr$measures)
}
## 
## Measures GWNBR:
##            deviance full_Log_likelihood              pctdev           adjpctdev 
##           7.8384030        -146.7663260           0.9977697           1.0459347 
##                 AIC                AICc 
##         366.8343756        -674.2885691
if (!is.null(model_gwnbr$global_measures)) {
  cat("\nGlobal measures dari mgwnbr:\n")
  print(model_gwnbr$global_measures)
}
## 
## Global measures dari mgwnbr:
##            deviance full_Log_likelihood             pctdevg          adjpctdevg 
##          37.7122798        -315.7223367           0.4177487           0.3401152 
##                 AIC                AICc 
##         643.4446734         646.4446734

20. Koefisien lokal GWNBR

if (!is.null(model_gwnbr$mgwr_param_estimates)) {
  
  coef_lokal <- as.data.frame(model_gwnbr$mgwr_param_estimates)
  coef_lokal$Wilayah <- df_model$wilayah
  
  coef_lokal <- coef_lokal %>%
    dplyr::select(Wilayah, everything())
  
  kable(coef_lokal,
        caption = "Koefisien Lokal GWNBR per Kabupaten/Kota",
        digits = 6) %>%
    kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                  full_width = FALSE) %>%
    scroll_box(height = "400px")
  
} else {
  cat("Komponen mgwr_param_estimates tidak ditemukan pada output mgwnbr.\n")
}
Koefisien Lokal GWNBR per Kabupaten/Kota
Wilayah Intercept X1 X2 X3 X4 alpha
Cilacap Cilacap 5.039237 0.159633 -0.001123 -0.000073 -0.000739 0.063671
Banyumas Banyumas 3.542302 0.150309 0.004901 0.000958 -0.000586 0.000001
Purbalingga Purbalingga 11.786854 0.180409 -0.078084 -0.002235 -0.003153 0.078228
Banjarnegara Banjarnegara -0.980337 -0.455808 0.077443 0.002411 0.004213 0.000001
Kebumen Kebumen 8.375959 0.202961 -0.076068 -0.002224 -0.000619 0.005790
Purworejo Purworejo 4.184061 0.266732 -0.061641 -0.000680 0.001005 0.131267
Wonosobo Wonosobo 1.936788 0.587791 -0.129508 -0.000774 0.002264 0.029353
Magelang Magelang 2.459104 0.525616 -0.112876 -0.000758 0.001854 0.000001
Boyolali Boyolali 1.142562 0.398953 0.021884 -0.000262 -0.000464 0.000001
Klaten Klaten 2.461918 0.007827 0.104158 -0.000061 0.002865 0.003053
Sukoharjo Sukoharjo 6.632124 0.056939 -0.023392 -0.000157 -0.001239 0.019360
Wonogiri Wonogiri 6.951983 0.093036 -0.042148 -0.000196 -0.001728 0.019895
Karanganyar Karanganyar 6.649021 0.064042 -0.030704 -0.000171 -0.001149 0.020398
Sragen Sragen 6.502230 -0.004411 0.008635 -0.000102 -0.000788 0.015246
Grobogan Grobogan 9.232663 -0.081947 0.004067 -0.001276 -0.001303 0.000324
Blora Blora 4.102819 0.123587 0.007185 0.000502 -0.000471 0.047108
Rembang Rembang 2.825573 0.152676 0.011596 0.000241 0.000503 0.000001
Pati Pati 3.357905 0.095301 0.015528 0.000185 0.000364 0.004946
Kudus Kudus 3.704935 0.045676 0.008037 -0.000003 0.001291 0.000001
Jepara Jepara 4.137642 0.026562 0.012210 -0.000077 0.000776 0.006005
Demak Demak 1.673725 0.048328 -0.012087 0.000512 0.004103 0.000001
Semarang Semarang 3.690518 0.280756 -0.061388 -0.000672 0.000097 0.031862
Temanggung Temanggung -4.369117 1.462403 -0.012516 -0.000877 0.003735 0.000001
Kendal Kendal 3.266408 0.005823 0.042239 -0.000088 0.000345 0.135211
Batang Batang -2.912506 -1.204160 0.263549 0.001091 0.007638 0.008274
Pekalongan Pekalongan 1.007968 -0.291717 0.070012 0.000390 0.003697 0.000001
Pemalang Pemalang 8.976057 0.185886 -0.084800 -0.000284 -0.003143 0.038708
Tegal Tegal 5.802871 0.166672 0.011916 -0.000132 -0.001742 0.017858
Brebes Brebes 5.470895 0.170306 0.007269 -0.000109 -0.001422 0.053584
Kota Magelang Kota Magelang 2.442656 0.530650 -0.113988 -0.000763 0.001857 0.000001
Kota Surakarta Kota Surakarta 6.187541 0.036750 -0.005701 -0.000131 -0.000737 0.026081
Kota Salatiga Kota Salatiga 3.691547 0.097263 0.034335 -0.000545 0.001395 0.091510
Kota Semarang Kota Semarang 3.581190 0.080211 0.035993 -0.000489 -0.000455 0.000001
Kota Pekalongan Kota Pekalongan 1.272829 -0.214252 0.079221 0.000308 0.002880 0.033023
Kota Tegal Kota Tegal 4.937992 0.262944 0.004084 -0.000033 -0.001889 0.000001

21. Standard error dan signifikansi lokal

if (!is.null(model_gwnbr$mgwr_param_estimates) &&
    !is.null(model_gwnbr$mgwr_se)) {
  
  beta_mat <- as.matrix(model_gwnbr$mgwr_param_estimates)
  se_mat   <- as.matrix(model_gwnbr$mgwr_se)
  
  z_mat <- beta_mat / se_mat
  p_mat <- 2 * (1 - pnorm(abs(z_mat)))
  
  sig_mat <- p_mat < 0.05
  
  sig_tbl <- as.data.frame(sig_mat)
  sig_tbl$Wilayah <- df_model$wilayah
  
  sig_tbl <- sig_tbl %>%
    dplyr::select(Wilayah, everything())
  
  kable(sig_tbl,
        caption = "Signifikansi Lokal Parameter GWNBR, TRUE = Signifikan") %>%
    kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                  full_width = FALSE) %>%
    scroll_box(height = "400px")
  
  # Pengelompokan wilayah berdasarkan variabel signifikan
  kelompok_sig <- apply(sig_mat, 1, function(x) {
    vars <- colnames(sig_mat)[x]
    vars <- vars[!vars %in% c("(Intercept)", "Intercept")]
    if (length(vars) == 0) {
      return("Tidak ada variabel signifikan")
    } else {
      return(paste(vars, collapse = ", "))
    }
  })
  
  data_sf$Kelompok_Signifikan <- kelompok_sig
  
  kelompok_tbl <- data.frame(
    Wilayah = df_model$wilayah,
    Kelompok_Signifikan = kelompok_sig
  )
  
  kable(kelompok_tbl,
        caption = "Kelompok Wilayah Berdasarkan Variabel Signifikan pada GWNBR") %>%
    kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
  
} else {
  cat("Komponen mgwr_param_estimates atau mgwr_se tidak ditemukan.\n")
}
Kelompok Wilayah Berdasarkan Variabel Signifikan pada GWNBR
Wilayah Kelompok_Signifikan
Cilacap Cilacap X1
Banyumas Banyumas X1, X2, X3, X4
Purbalingga Purbalingga X1, X2, X3, X4
Banjarnegara Banjarnegara X1, X2, X3, X4
Kebumen Kebumen X1, X2, X3
Purworejo Purworejo X3
Wonosobo Wonosobo X1, X2, X3, X4
Magelang Magelang X1, X2, X3, X4
Boyolali Boyolali X1, X2, X3, X4
Klaten Klaten X2, X3, X4
Sukoharjo Sukoharjo X2, X3, X4
Wonogiri Wonogiri X1, X2, X3, X4
Karanganyar Karanganyar X2, X3, X4
Sragen Sragen X2, X3
Grobogan Grobogan X1, X2, X3, X4
Blora Blora X1
Rembang Rembang X1, X2, X3, X4
Pati Pati X1, X2
Kudus Kudus X1, X2, X4
Jepara Jepara X1, X2, X4
Demak Demak X1, X4
Semarang Semarang X1, X2, X3
Temanggung Temanggung X1, X2, X3, X4
Kendal Kendal X2
Batang Batang X1, X2, X3, X4
Pekalongan Pekalongan X1, X2, X3, X4
Pemalang Pemalang X1, X2, X3, X4
Tegal Tegal X1, X2, X3, X4
Brebes Brebes X1, X4
Kota Magelang Kota Magelang X1, X2, X3, X4
Kota Surakarta Kota Surakarta X3
Kota Salatiga Kota Salatiga X3, X4
Kota Semarang Kota Semarang X1, X2, X3, X4
Kota Pekalongan Kota Pekalongan X1, X2, X3, X4
Kota Tegal Kota Tegal X1, X4

22. Perbandingan AIC Poisson, NB, dan GWNBR

AIC_pois <- AIC(model_poisson)
AIC_nb   <- AIC(model_nb)
AIC_gwnbr <- ambil_aic_mgwnbr(model_gwnbr$measures)

perbandingan_aic <- data.frame(
  Model = c("Regresi Poisson", "Regresi Negative Binomial Global", "GWNBR"),
  AIC   = c(round(AIC_pois, 4), round(AIC_nb, 4), round(AIC_gwnbr, 4))
)

kable(perbandingan_aic,
      caption = "Perbandingan AIC Model") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Perbandingan AIC Model
Model AIC
Regresi Poisson 3200.9233
Regresi Negative Binomial Global 448.0866
GWNBR 366.8344
cat("\n==============================\n")
## 
## ==============================
cat("PERBANDINGAN AIC\n")
## PERBANDINGAN AIC
cat("==============================\n")
## ==============================
print(perbandingan_aic)
##                              Model       AIC
## 1                  Regresi Poisson 3200.9233
## 2 Regresi Negative Binomial Global  448.0866
## 3                            GWNBR  366.8344
if (!is.na(AIC_gwnbr)) {
  if (AIC_gwnbr < AIC_nb) {
    cat("\nKesimpulan AIC: AIC GWNBR lebih kecil daripada AIC NB global.\n")
    cat("Artinya GWNBR memberikan model yang lebih baik berdasarkan AIC.\n")
  } else {
    cat("\nKesimpulan AIC: AIC GWNBR belum lebih kecil daripada AIC NB global.\n")
    cat("Coba cek kembali bandwidth, variabel, atau struktur spasial.\n")
  }
} else {
  cat("\nAIC GWNBR tidak berhasil diekstrak otomatis.\n")
  cat("Lihat langsung pada output model_gwnbr$measures.\n")
}
## 
## Kesimpulan AIC: AIC GWNBR lebih kecil daripada AIC NB global.
## Artinya GWNBR memberikan model yang lebih baik berdasarkan AIC.

25. Kesimpulan

cat("1. Model yang digunakan:\n")
## 1. Model yang digunakan:
cat("   Y_count ~ X1 + X2 + X3 + X4\n\n")
##    Y_count ~ X1 + X2 + X3 + X4
cat("2. Overdispersi:\n")
## 2. Overdispersi:
cat("   Rasio Deviance/df      =", round(rasio_dev, 4), "\n")
##    Rasio Deviance/df      = 98.2168
cat("   Rasio Pearson ChiSq/df =", round(rasio_pearson, 4), "\n")
##    Rasio Pearson ChiSq/df = 99.7037
if (rasio_dev > 1 || rasio_pearson > 1) {
  cat("   Kesimpulan: terjadi overdispersi, sehingga NB lebih sesuai daripada Poisson.\n\n")
} else {
  cat("   Kesimpulan: overdispersi belum terlihat kuat.\n\n")
}
##    Kesimpulan: terjadi overdispersi, sehingga NB lebih sesuai daripada Poisson.
cat("3. Model Negative Binomial global:\n")
## 3. Model Negative Binomial global:
cat("   AIC NB =", round(AIC_nb, 4), "\n")
##    AIC NB = 448.0866
cat("   Theta =", round(theta_nb, 6), "\n")
##    Theta = 2.322326
cat("   Alpha =", round(alpha_nb, 6), "\n\n")
##    Alpha = 0.430603
cat("4. Moran's I residual NB:\n")
## 4. Moran's I residual NB:
cat("   Moran's I =", round(I_moran, 6), "\n")
##    Moran's I = -0.003914
cat("   p-value   =", round(p_moran, 6), "\n")
##    p-value   = 0.412158
if (p_moran < 0.05) {
  cat("   Kesimpulan: terdapat autokorelasi spasial.\n\n")
} else {
  cat("   Kesimpulan: autokorelasi spasial belum signifikan.\n\n")
}
##    Kesimpulan: autokorelasi spasial belum signifikan.
cat("5. Heterogenitas Breusch-Pagan:\n")
## 5. Heterogenitas Breusch-Pagan:
cat("   BP statistic =", round(bp_test$statistic, 6), "\n")
##    BP statistic = 11.64138
cat("   p-value      =", round(bp_test$p.value, 6), "\n")
##    p-value      = 0.020227
if (bp_test$p.value < 0.05) {
  cat("   Kesimpulan: terdapat heterogenitas.\n\n")
} else {
  cat("   Kesimpulan: heterogenitas belum signifikan.\n\n")
}
##    Kesimpulan: terdapat heterogenitas.
cat("6. Perbandingan AIC:\n")
## 6. Perbandingan AIC:
cat("   AIC Poisson =", round(AIC_pois, 4), "\n")
##    AIC Poisson = 3200.923
cat("   AIC NB      =", round(AIC_nb, 4), "\n")
##    AIC NB      = 448.0866
cat("   AIC GWNBR   =", round(AIC_gwnbr, 4), "\n")
##    AIC GWNBR   = 366.8344
if (!is.na(AIC_gwnbr) && AIC_gwnbr < AIC_nb) {
  cat("   Kesimpulan: GWNBR lebih baik daripada NB global berdasarkan AIC.\n")
} else if (!is.na(AIC_gwnbr) && AIC_gwnbr >= AIC_nb) {
  cat("   Kesimpulan: GWNBR belum lebih baik daripada NB global berdasarkan AIC.\n")
} else {
  cat("   Kesimpulan: AIC GWNBR perlu dilihat manual pada model_gwnbr$measures.\n")
}
##    Kesimpulan: GWNBR lebih baik daripada NB global berdasarkan AIC.

VISUALISASI PETA

# ============================================================
# VISUALISASI PETA TEMATIK
# ============================================================

library(ggplot2)
library(sf)
library(dplyr)
library(ggrepel)
library(RColorBrewer)
library(scales)

# ── Label wilayah (centroid) ─────────────────────────────────
# Gunakan st_point_on_surface agar titik selalu jatuh di dalam polygon
centroids_label <- suppressWarnings(st_point_on_surface(st_geometry(data_sf)))
coords_label    <- st_coordinates(centroids_label)

df_label <- data.frame(
  wilayah = data_sf[[kolom_wilayah_shp]],
  lx      = coords_label[, 1],
  ly      = coords_label[, 2],
  stringsAsFactors = FALSE
)

# ── Tema dasar peta ──────────────────────────────────────────
tema_peta <- theme_void(base_family = "sans") +
  theme(
    plot.title        = element_text(face = "bold", size = 14, hjust = 0.5,
                                     margin = margin(b = 8)),
    plot.subtitle     = element_text(size = 10, hjust = 0.5, color = "grey40",
                                     margin = margin(b = 6)),
    legend.position   = "right",
    legend.title      = element_text(size = 9, face = "bold"),
    legend.text       = element_text(size = 8),
    plot.margin       = margin(10, 10, 10, 10),
    panel.background  = element_rect(fill = "#f0f4f8", color = NA),
    plot.background   = element_rect(fill = "white", color = NA)
  )
# ────────────────────────────────────────────────────────────
# FUNGSI UTAMA: peta khoropleth satu variabel
# ────────────────────────────────────────────────────────────
buat_peta_khoropleth <- function(sf_data, var_col, judul, subjudul = "",
                                  label_df, palet = "YlOrRd",
                                  arah_palet = 1) {
  
  sf_data$nilai_plot <- sf_data[[var_col]]
  
  ggplot(sf_data) +
    geom_sf(aes(fill = nilai_plot), color = "white", linewidth = 0.4) +
    scale_fill_distiller(
      palette   = palet,
      direction = arah_palet,
      name      = var_col,
      labels    = label_comma(big.mark = ".", decimal.mark = ","),
      guide     = guide_colorbar(barwidth = 0.8, barheight = 8,
                                  title.position = "top")
    ) +
    geom_text_repel(
      data          = label_df,
      aes(x = lx, y = ly, label = wilayah),
      size          = 2.2,
      fontface      = "bold",
      color         = "black",
      bg.color      = "white",
      bg.r          = 0.15,
      segment.color = "grey50",
      segment.size  = 0.3,
      max.overlaps  = 50,
      min.segment.length = 0.1,
      force         = 1.5,
      seed          = 42
    ) +
    labs(title = judul, subtitle = subjudul) +
    tema_peta
}

# ============================================================
# PETA 1 – SEBARAN VARIABEL Y (Jumlah Kasus / Count)
# ============================================================
p_Y <- buat_peta_khoropleth(
  sf_data   = data_sf,
  var_col   = "Y_count",
  judul     = "Peta Sebaran Variabel Y",
  subjudul  = "Jumlah Kasus per Kabupaten/Kota di Jawa Tengah",
  label_df  = df_label,
  palet     = "YlOrRd",
  arah_palet = 1
)

print(p_Y)

ggsave("peta_Y.png", p_Y, width = 12, height = 8, dpi = 200)


# ============================================================
# PETA 2–5 – SEBARAN VARIABEL X1, X2, X3, X4
# ============================================================

info_x <- list(
  X1 = list(judul    = "Peta Sebaran Variabel X1",
             subjudul = "Kabupaten/Kota di Jawa Tengah",
             palet    = "Blues",   arah = 1),
  X2 = list(judul    = "Peta Sebaran Variabel X2",
             subjudul = "Kabupaten/Kota di Jawa Tengah",
             palet    = "Greens",  arah = 1),
  X3 = list(judul    = "Peta Sebaran Variabel X3",
             subjudul = "Kabupaten/Kota di Jawa Tengah",
             palet    = "Purples", arah = 1),
  X4 = list(judul    = "Peta Sebaran Variabel X4",
             subjudul = "Kabupaten/Kota di Jawa Tengah",
             palet    = "Oranges", arah = 1)
)

peta_list <- list()

for (xv in names(info_x)) {
  info <- info_x[[xv]]
  p <- buat_peta_khoropleth(
    sf_data    = data_sf,
    var_col    = xv,
    judul      = info$judul,
    subjudul   = info$subjudul,
    label_df   = df_label,
    palet      = info$palet,
    arah_palet = info$arah
  )
  peta_list[[xv]] <- p
  print(p)
  ggsave(paste0("peta_", xv, ".png"), p, width = 12, height = 8, dpi = 200)
}

# ============================================================
# PETA 6 – PENGELOMPOKAN WILAYAH BERDASARKAN SIGNIFIKANSI GWNBR
# ============================================================

# Pastikan kolom Kelompok_Signifikan sudah ada (dibuat di chunk 21)
# Bila belum ada, jalankan ulang chunk 21 terlebih dahulu.

if (!("Kelompok_Signifikan" %in% names(data_sf))) {
  stop("Kolom Kelompok_Signifikan belum ada. Jalankan chunk 21 terlebih dahulu.")
}

# Buat palet warna kategorikal yang kontras
kelompok_unik <- sort(unique(data_sf$Kelompok_Signifikan))
n_kelompok    <- length(kelompok_unik)

# Palet kontras: gabungkan beberapa set warna yang tajam
palet_kontras <- c(
  "#E63946",  # merah terang
  "#2A9D8F",  # tosca
  "#F4A261",  # oranye hangat
  "#457B9D",  # biru tua
  "#8338EC",  # ungu cerah
  "#06D6A0",  # hijau mint
  "#FFB703",  # kuning amber
  "#FB5607",  # oranye tua
  "#3A86FF",  # biru cerah
  "#FFBE0B",  # kuning
  "#D62246",  # merah tua
  "#4CC9F0",  # biru langit
  "#B5179E",  # magenta
  "#4D908E",  # hijau kebiruan
  "#F72585",  # pink terang
  "#90E0EF",  # biru pastel kontras
  "#43AA8B",  # hijau sage
  "#577590"   # abu kebiruan
)

# Kalau jumlah kelompok > panjang vektor, pakai rainbow
if (n_kelompok > length(palet_kontras)) {
  palet_pakai <- hcl.colors(n_kelompok, palette = "Dynamic")
} else {
  palet_pakai <- palet_kontras[seq_len(n_kelompok)]
}
names(palet_pakai) <- kelompok_unik

p_sig <- ggplot(data_sf) +
  geom_sf(
    aes(fill = Kelompok_Signifikan),
    color    = "white",
    linewidth = 0.45
  ) +
  scale_fill_manual(
    values = palet_pakai,
    name   = "Variabel Signifikan",
    guide  = guide_legend(
      ncol         = 1,
      byrow        = TRUE,
      keywidth     = unit(0.6, "cm"),
      keyheight    = unit(0.5, "cm"),
      override.aes = list(color = "white", linewidth = 0.3)
    )
  ) +
  geom_text_repel(
    data          = df_label,
    aes(x = lx, y = ly, label = wilayah),
    size          = 2.2,
    fontface      = "bold",
    color         = "black",
    bg.color      = "white",
    bg.r          = 0.15,
    segment.color = "grey50",
    segment.size  = 0.3,
    max.overlaps  = 50,
    min.segment.length = 0.1,
    force         = 1.5,
    seed          = 42
  ) +
  labs(
    title    = "Peta Pengelompokan Wilayah Berdasarkan Variabel Signifikan GWNBR",
    subtitle = "Kabupaten/Kota di Jawa Tengah"
  ) +
  tema_peta +
  theme(
    legend.text  = element_text(size = 7.5),
    legend.title = element_text(size = 9, face = "bold")
  )

print(p_sig)

ggsave("peta_signifikansi_gwnbr.png", p_sig, width = 14, height = 9, dpi = 200)


# ============================================================
# BONUS: Panel 6 peta sekaligus (opsional, butuh patchwork)
# ============================================================

if (requireNamespace("patchwork", quietly = TRUE)) {
  library(patchwork)
  
  panel_semua <- (p_Y | peta_list$X1 | peta_list$X2) /
                 (peta_list$X3 | peta_list$X4 | p_sig) +
    plot_annotation(
      title   = "Analisis Spasial GWNBR – Jawa Tengah",
      caption = "Sumber: Data Penelitian | Disusun: Nisrina Aisyah & Nayli Kurnia Ilahi",
      theme   = theme(
        plot.title   = element_text(face = "bold", size = 16, hjust = 0.5),
        plot.caption = element_text(size = 8, color = "grey50")
      )
    )
  
  ggsave("panel_semua_peta.png", panel_semua,
         width = 26, height = 16, dpi = 180)
  cat("Panel lengkap disimpan: panel_semua_peta.png\n")
  
} else {
  cat("Install patchwork untuk panel gabungan: install.packages('patchwork')\n")
}
## 
## Attaching package: 'patchwork'
## The following object is masked from 'package:MASS':
## 
##     area
## Panel lengkap disimpan: panel_semua_peta.png
cat("\nSemua peta berhasil dibuat dan disimpan.\n")
## 
## Semua peta berhasil dibuat dan disimpan.