Autokorelasi spasial positif : pada lokasi yang berdekatan cenderung memiliki nilai variabel yang mirip.
Autokorelasi spasial negatif : lokasi-lokasi yang berdekatan cenderung memiliki nilai variabel yang jauh berbeda.
Autokorelasi spasial positif : Tingkat polusi di wilayah industri pada beberapa tempat yang berdekatan menunjukkan angka yang tinggi.
Autokorelasi spasial negatif : Kawasan elite dengan pendapatan tinggi yang berbatasan dengan kawasan kumuh dengan pendapatan rendah,
Rumus Moran’s I secara global dituliskan sebagai berikut:
\[ I = \frac{N}{S_0} \cdot \frac{\sum_{i=1}^{N} \sum_{j=1}^{N} w_{ij}(x_i - \bar{x})(x_j - \bar{x})} {\sum_{i=1}^{N}(x_i - \bar{x})^2} \]
Penjelasan Komponen :
Interpretasi Nilai
\(I > 0\) → Autokorelasi
spasial positif
Wilayah yang berdekatan cenderung memiliki nilai mirip
(clustering).
\(I < 0\) → Autokorelasi
spasial negatif
Wilayah yang berdekatan cenderung memiliki nilai berbeda
(dispersion).
\(I \approx 0\) → Tidak ada
autokorelasi spasial
Pola distribusi nilai acak dan tidak dipengaruhi oleh kedekatan
wilayah.
Rumus Geary’s C secara global dituliskan sebagai berikut:
\[ C = \frac{(N-1) \sum_{i=1}^{N} \sum_{j=1}^{N} w_{ij}(x_i - x_j)^2} {2 S_0 \sum_{i=1}^{N}(x_i - \bar{x})^2} \]
Penjelasan Komponen :
Interpretasi Nilai \(C\)
Rumus Local Moran’s I dituliskan sebagai berikut:
\[ I_i = \frac{x_i - \bar{x}}{m_2} \sum_{j=1}^{N} w_{ij}(x_j - \bar{x}), \]
dengan
\[ m_2 = \frac{1}{N} \sum_{k=1}^{N}(x_k - \bar{x})^2 \]
Penjelasan Komponen :
Interpretasi Nilai \(I_i\)
\(I_i > 0\) → Lokasi \(i\) cenderung dikelilingi oleh lokasi
dengan nilai yang serupa
(indikasi High-High atau Low-Low cluster).
\(I_i < 0\) → Lokasi \(i\) cenderung dikelilingi oleh lokasi
dengan nilai yang berbeda
(indikasi High-Low atau Low-High outlier).
\(I_i \approx 0\) → Tidak ada pola spasial yang signifikan pada lokasi tersebut.
Statistik lokal Getis–Ord digunakan untuk mengidentifikasi hot spot (nilai tinggi berkelompok) dan cold spot (nilai rendah berkelompok).
Definisi awal statistik ini merupakan rasio massa
tetangga terhadap massa total (Ord &
Getis, 1995).
Untuk bobot ketetanggaan biner, \(w_{ij}(d)
\in \{0,1\}\).
\[ G_i(d) = \frac{\sum_{j \neq i} w_{ij}(d) \, x_j} {\sum_{j \neq i} x_j} \]
\[ G_i^*(d) = \frac{\sum_{j=1}^{N} w_{ij}(d) \, x_j} {\sum_{j=1}^{N} x_j}, \quad \text{dengan } w_{ii}(d) = 1 \]
Penjelasan Komponen :
Interpretasi Nilai \(G_i^*\)
Nilai \(G_i^*\)
besar → menunjukkan hot spot
(lokasi \(i\) dikelilingi nilai
tinggi).
Nilai \(G_i^*\)
kecil → menunjukkan cold spot
(lokasi \(i\) dikelilingi nilai
rendah).
library(sf)
library(sp)
library(spdep)
library(dplyr)
library(ggplot2)
library(mapview)
library(spData)
library(spdep)
library(viridis)
library(tmap)
#Tetapkan Set.seed
set.seed(53)
#buat grid 6 x 5 = 30 polygon (proxy 30 kecamatan)
nx <- 6; ny <- 5; cell_size <- 1
polys <- list(); ids <- character()
idx <- 1
for(i in 0:(nx-1)){
for(j in 0:(ny-1)){
x0 <- i * cell_size
y0 <- j * cell_size
coords <- matrix(c(
x0, y0,
x0 + cell_size, y0,
x0 + cell_size, y0 + cell_size,
x0, y0 + cell_size,
x0, y0
), ncol=2, byrow=TRUE)
polys[[idx]] <- st_polygon(list(coords))
ids[idx] <- sprintf("Kec_%02d", idx)
idx <- idx + 1
}
}
sf_grid <- st_sf(
kecamatan = ids,
geometry = st_sfc(polys)
)
#hitung centroid untuk setiap polygon (digunakan utk menghitung jarak ke hotspot)
centroids_mat <- st_coordinates(st_centroid(sf_grid))
#definisikan parameter Poisson (base rate + 2 hotspot Gaussian)
base_rate <- 2 # kasus per 10.000 (base)
hotspots <- data.frame(
cx = c(2.5, 4.0),
cy = c(3.5, 1.0),
amp = c(6.0, 3.5),
sigma = c(1.0, 0.8)
)
#hitung mean Poisson untuk tiap kecamatan (menggabungkan kedua hotspot + noise kecil)
means <- numeric(nrow(sf_grid))
for(k in seq_len(nrow(sf_grid))){
m <- base_rate
for(h in seq_len(nrow(hotspots))){
dx <- centroids_mat[k,1] - hotspots$cx[h]
dy <- centroids_mat[k,2] - hotspots$cy[h]
dist2 <- dx^2 + dy^2
m <- m + hotspots$amp[h] * exp(- dist2 / (2 * (hotspots$sigma[h]^2)))
}
# sedikit variasi acak antar kecamatan
m <- m * runif(1, 0.85, 1.15)
means[k] <- m
}
#simulasi kasus per 10.000 penduduk (Poisson)
cases_per_10k <- rpois(length(means), lambda = means)
#gabungkan ke sf object
sf_grid$mean_per_10k <- round(means, 3)
sf_grid$cases_per_10k <- cases_per_10k
sf_grid$centroid_x <- centroids_mat[,1]
sf_grid$centroid_y <- centroids_mat[,2]
#tampilkan tabel ringkas
print(sf_grid %>% st_drop_geometry() %>% arrange(kecamatan))
## kecamatan mean_per_10k cases_per_10k centroid_x centroid_y
## 1 Kec_01 2.057 1 0.5 0.5
## 2 Kec_02 1.868 3 0.5 1.5
## 3 Kec_03 2.187 0 0.5 2.5
## 4 Kec_04 2.906 4 0.5 3.5
## 5 Kec_05 2.672 3 0.5 4.5
## 6 Kec_06 2.160 1 1.5 0.5
## 7 Kec_07 2.187 0 1.5 1.5
## 8 Kec_08 4.003 5 1.5 2.5
## 9 Kec_09 5.153 5 1.5 3.5
## 10 Kec_10 4.405 6 1.5 4.5
## 11 Kec_11 2.854 2 2.5 0.5
## 12 Kec_12 2.854 2 2.5 1.5
## 13 Kec_13 5.946 5 2.5 2.5
## 14 Kec_14 8.037 7 2.5 3.5
## 15 Kec_15 5.065 6 2.5 4.5
## 16 Kec_16 4.780 5 3.5 0.5
## 17 Kec_17 5.488 7 3.5 1.5
## 18 Kec_18 4.605 6 3.5 2.5
## 19 Kec_19 6.145 8 3.5 3.5
## 20 Kec_20 4.236 2 3.5 4.5
## 21 Kec_21 4.541 5 4.5 0.5
## 22 Kec_22 4.129 4 4.5 1.5
## 23 Kec_23 2.953 3 4.5 2.5
## 24 Kec_24 2.590 3 4.5 3.5
## 25 Kec_25 2.746 0 4.5 4.5
## 26 Kec_26 2.731 3 5.5 0.5
## 27 Kec_27 2.163 2 5.5 1.5
## 28 Kec_28 2.297 3 5.5 2.5
## 29 Kec_29 2.350 3 5.5 3.5
## 30 Kec_30 1.798 2 5.5 4.5
#Membuat choropleth dengan ggplot2
p <- ggplot(sf_grid) +
geom_sf(aes(fill = cases_per_10k), color = "black", size = 0.25) +
geom_sf_text(aes(label = as.numeric(sub("Kec_","",kecamatan))), size = 3) +
scale_fill_viridis_c(option = "D", name = "Kasus per 10.000") +
theme_minimal() +
labs(title = "Choropleth Simulasi: Kasus Diare per 10.000 penduduk (30 kecamatan - grid)",
subtitle = "Simulasi Poisson dengan dua hotspot (Gaussian)")
print(p)
Berdasarkan gambar tersebut terlihat adanya pola spasial yang cukup jelas, ditunjukkan oleh konsentrasi nilai kasus yang lebih tinggi pada area tertentu dibandingkan dengan sekitarnya. Hal ini terlihat dari dua hotspot dengan intensitas warna kuning-hijau yang menandakan jumlah kasus relatif lebih tinggi, sedangkan area lainnya cenderung berwarna biru-ungu yang menunjukkan jumlah kasus lebih rendah. Pola ini menggambarkan distribusi yang tidak merata sehingga secara visual dapat disimpulkan bahwa terdapat kecenderungan spasial dalam penyebaran kasus diare pada wilayah simulasi.
# queen contiguity (Bobot Spasial)
nb_queen <- poly2nb(sf_grid, queen = TRUE)
# binary listw (B) and row-standardized (W)
lw_B <- nb2listw(nb_queen, style = "B", zero.policy = TRUE)
lw_W <- nb2listw(nb_queen, style = "W", zero.policy = TRUE)
# tampilkan ringkasan neighbor count
summary(nb_queen)
## Neighbour list object:
## Number of regions: 30
## Number of nonzero links: 178
## Percentage nonzero weights: 19.77778
## Average number of links: 5.933333
## Link number distribution:
##
## 3 5 8
## 4 14 12
## 4 least connected regions:
## 1 5 26 30 with 3 links
## 12 most connected regions:
## 7 8 9 12 13 14 17 18 19 22 23 24 with 8 links
# Global Moran's I dengan permutasi (moran.mc)
nperm <- 9999
moran_mc_res <- moran.mc(sf_grid$cases_per_10k, listw = lw_B, nsim = nperm, alternative = "two.sided")
# hasil
moran_mc_res
##
## Monte-Carlo simulation of Moran I
##
## data: sf_grid$cases_per_10k
## weights: lw_B
## number of simulations + 1: 10000
##
## statistic = 0.32379, observed rank = 9990, p-value = 0.002
## alternative hypothesis: two.sided
# simpan nilai observed & p-value ke variabel untuk ringkasan akhir
moran_I_obs <- moran_mc_res$statistic
moran_I_pvalue <- moran_mc_res$p.value
cat("Nilai Moran's I adalah", round(moran_I_obs, 6), "\n")
## Nilai Moran's I adalah 0.323794
cat("Didapatkan P-Value Moran's I (Uji permutasi dua sisi) sebesar", moran_I_pvalue, "\n")
## Didapatkan P-Value Moran's I (Uji permutasi dua sisi) sebesar 0.002
alpha <- 0.05
is_signif <- moran_I_pvalue < alpha
cat("**Jawaban:** p-value (perm) = ", moran_I_pvalue, "\n", sep = "")
## **Jawaban:** p-value (perm) = 0.002
if(is_signif){
cat("Kesimpulan: Karena p-value < ", alpha, ", hasil signifikan secara statistik pada α = ", alpha, ".\n", sep = "")
} else {
cat("Kesimpulan: Karena p-value >= ", alpha, ", hasil tidak signifikan pada α = ", alpha, ".\n", sep = "")
}
## Kesimpulan: Karena p-value < 0.05, hasil signifikan secara statistik pada α = 0.05.
Penyakit dalam simualasi ini muncul berkelompok (clustered), bukan tersebar acak.
kemungkinan ada faktor lingkungan atau sosial yang membuat penularan/risiko lebih tinggi di area berdekatan, misalnya kualitas air, sanitasi, atau interaksi antarwilayah.
Bagi surveilans penyakit menular, ini menandakan perlunya fokus pada klaster geografis untuk intervensi.
# Geary's C dengan Monte Carlo (geary.mc)
geary_mc_res <- geary.mc(sf_grid$cases_per_10k, listw = lw_B, nsim = nperm)
geary_mc_res
##
## Monte-Carlo simulation of Geary C
##
## data: sf_grid$cases_per_10k
## weights: lw_B
## number of simulations + 1: 10000
##
## statistic = 0.73599, observed rank = 121.5, p-value = 0.01215
## alternative hypothesis: greater
# simpan nilai observed & p-value
geary_C_obs <- geary_mc_res$statistic
geary_C_pvalue <- geary_mc_res$p.value
cat("Nilai Geary's C adalah", round(geary_C_obs, 6), "\n")
## Nilai Geary's C adalah 0.73599
cat("Didapat P-Value Geary's C (Uji Permutasi Dua Sisi)", geary_C_pvalue, "\n")
## Didapat P-Value Geary's C (Uji Permutasi Dua Sisi) 0.01215
Nilai Moran’s I = 0.324 (p = 0.002) menunjukkan adanya autokorelasi spasial positif (pola klaster).
Nilai Geary’s C = 0.736 (< 1, p = 0.0129) juga menunjukkan adanya autokorelasi spasial positif.
Keduanya konsisten: pola penyakit cenderung berklaster dan signifikan secara statistik.
Moran’s I lebih menekankan pada hubungan global rata-rata di seluruh wilayah. Nilainya mencerminkan apakah wilayah dengan nilai tinggi/ rendah cenderung berdekatan secara umum.
Geary’s C lebih sensitif terhadap perbedaan lokal (dissimilarity) antar tetangga langsung. Ia menyoroti variasi kecil antar wilayah bersebelahan yang mungkin tidak ditangkap Moran’s I.
Dengan demikian, meskipun keduanya signifikan, Geary’s C bisa lebih cepat mendeteksi anomali lokal dibandingkan Moran’s I yang lebih menggambarkan pola global.
x <- sf_grid$cases_per_10k
n <- length(x)
xbar <- mean(x)
m2 <- sum((x - xbar)^2) / n
Wmat <- nb2mat(nb_queen, style = "B", zero.policy = TRUE)
local_I_obs <- numeric(n)
for(i in seq_len(n)){
local_I_obs[i] <- (x[i] - xbar) / m2 * sum(Wmat[i, ] * (x - xbar))
}
nsim <- 999
local_perm_mat <- matrix(0, nrow = n, ncol = nsim)
for(s in seq_len(nsim)){
perm_x <- sample(x, replace = FALSE)
perm_xbar <- mean(perm_x)
perm_m2 <- sum((perm_x - perm_xbar)^2) / n
for(i in seq_len(n)){
local_perm_mat[i, s] <- (perm_x[i] - perm_xbar) / perm_m2 * sum(Wmat[i, ] * (perm_x - perm_xbar))
}
}
local_pvals <- sapply(seq_len(n), function(i){
(sum(abs(local_perm_mat[i, ]) >= abs(local_I_obs[i])) + 1) / (nsim + 1)
})
sf_grid$local_I <- local_I_obs
sf_grid$local_pval <- local_pvals
alpha <- 0.05
value_sign <- ifelse(x - xbar > 0, 1, -1)
lag <- as.numeric(Wmat %*% (x - xbar))
lag_sign <- ifelse(lag > 0, 1, -1)
lisa_cat <- rep("Not-significant", n)
for(i in seq_len(n)){
if(sf_grid$local_pval[i] <= alpha){
if(value_sign[i] == 1 && lag_sign[i] == 1) lisa_cat[i] <- "High-High"
if(value_sign[i] == -1 && lag_sign[i] == -1) lisa_cat[i] <- "Low-Low"
if(value_sign[i] == 1 && lag_sign[i] == -1) lisa_cat[i] <- "High-Low"
if(value_sign[i] == -1 && lag_sign[i] == 1) lisa_cat[i] <- "Low-High"
}
}
sf_grid$LISA_cat <- factor(lisa_cat, levels = c("High-High","Low-Low","High-Low","Low-High","Not-significant"))
table(sf_grid$LISA_cat)
##
## High-High Low-Low High-Low Low-High Not-significant
## 3 3 0 0 24
sf_grid %>% st_drop_geometry() %>% select(kecamatan, cases_per_10k, local_I, local_pval, LISA_cat)
cols <- c("red", "blue", "orange", "purple", "grey90")
names(cols) <- levels(sf_grid$LISA_cat)
ggplot(sf_grid) +
geom_sf(aes(fill = LISA_cat), color = "black", size = 0.25) +
geom_sf_text(aes(label = as.numeric(sub("Kec_","",kecamatan))), size = 3) +
scale_fill_manual(values = cols, name = "LISA cluster") +
theme_minimal() +
labs(title = "LISA cluster map (High-High, Low-Low, High-Low, Low-High)")
Gstar <- localG(sf_grid$cases_per_10k, listw = lw_B, zero.policy = TRUE)
sf_grid$Gstar <- as.numeric(Gstar)
sf_grid$Gstar_pval <- 2 * (1 - pnorm(abs(sf_grid$Gstar)))
sf_grid$Gstar_sig <- "Not-significant"
sf_grid$Gstar_sig[sf_grid$Gstar > 1.96 & sf_grid$Gstar_pval <= 0.05] <- "Hotspot"
sf_grid$Gstar_sig[sf_grid$Gstar < -1.96 & sf_grid$Gstar_pval <= 0.05] <- "Coldspot"
table(sf_grid$Gstar_sig)
##
## Coldspot Hotspot Not-significant
## 3 4 23
sf_grid %>% st_drop_geometry() %>% select(kecamatan, cases_per_10k, Gstar, Gstar_pval, Gstar_sig)
ggplot(sf_grid) +
geom_sf(aes(fill = Gstar_sig), color = "black", size = 0.25) +
geom_sf_text(aes(label = as.numeric(sub("Kec_","",kecamatan))), size = 3) +
scale_fill_manual(values = c("Hotspot"="red","Coldspot"="blue","Not-significant"="grey90")) +
theme_minimal() +
labs(title = "Getis-Ord G* (hotspot/coldspot approx.)")
Pada LISA, identifikasi cluster dibedakan lebih detail:
High-High (hotspot dengan tetangga hotspot)
Low-Low (coldspot dengan tetangga coldspot)
Potensi High-Low & Low-High (outlier spasial, meskipun tidak muncul signifikan di peta ini).
Pada Getis–Ord G*, hasil lebih “global” di tingkat lokal, hanya membedakan hotspot vs coldspot, tanpa mendeteksi pola outlier (misalnya wilayah dengan nilai tinggi tapi diapit wilayah rendah, atau sebaliknya).