Pendidikan merupakan salah satu dimensi utama pembangunan manusia. Dua indikator yang lazim dipakai untuk menggambarkannya adalah Harapan Lama Sekolah (HLS), yaitu lamanya sekolah (tahun) yang diharapkan akan dirasakan anak usia 7 tahun pada masa mendatang, dan Rata-rata Lama Sekolah (RLS), yaitu rata-rata lama sekolah penduduk usia 25 tahun ke atas. HLS mencerminkan harapan untuk generasi muda, sedangkan RLS mencerminkan capaian generasi dewasa saat ini.
Pertanyaan yang diajukan pada laporan ini: apakah kabupaten/kota yang HLS-nya tinggi cenderung bertetangga dengan kabupaten/kota yang RLS-nya tinggi? Pertanyaan semacam ini tidak dapat dijawab dengan korelasi biasa karena korelasi mengabaikan letak wilayah. Karena itu digunakan Moran’s I bivariat (ukuran global) dan Local Indicator of Spatial Association (LISA) bivariat (ukuran lokal).
Tujuan analisis:
Data berasal dari BPS Provinsi Banten tahun 2024 untuk 8
kabupaten/kota (file Harapan_Lama_Sekolah_...xlsx dan
Mean_Years_Schooling_...xlsx). Kode wilayah mengikuti kode
BPS.
data_banten <- tibble::tribble(
~kode, ~wilayah, ~hls, ~rls,
"3601", "Kab. Pandeglang", 13.74, 7.16,
"3602", "Kab. Lebak", 12.11, 6.61,
"3603", "Kab. Tangerang", 12.88, 9.06,
"3604", "Kab. Serang", 12.87, 7.88,
"3671", "Kota Tangerang", 13.90, 11.14,
"3672", "Kota Cilegon", 13.23, 10.39,
"3673", "Kota Serang", 12.83, 8.93,
"3674", "Kota Tangerang Selatan", 14.70, 11.86
)
n <- nrow(data_banten)
kable(data_banten, col.names = c("Kode", "Kabupaten/Kota", "HLS (tahun)", "RLS (tahun)"),
caption = "Data HLS dan RLS menurut kabupaten/kota, Provinsi Banten 2024")| Kode | Kabupaten/Kota | HLS (tahun) | RLS (tahun) |
|---|---|---|---|
| 3601 | Kab. Pandeglang | 13.74 | 7.16 |
| 3602 | Kab. Lebak | 12.11 | 6.61 |
| 3603 | Kab. Tangerang | 12.88 | 9.06 |
| 3604 | Kab. Serang | 12.87 | 7.88 |
| 3671 | Kota Tangerang | 13.90 | 11.14 |
| 3672 | Kota Cilegon | 13.23 | 10.39 |
| 3673 | Kota Serang | 12.83 | 8.93 |
| 3674 | Kota Tangerang Selatan | 14.70 | 11.86 |
Peta dibuat tanpa shapefile: bentuk wilayah digambar manual (skematik) dari koordinat simpul pada chunk berikut, dengan simpul batas yang dipakai bersama sehingga ketetanggaan Queen tetap dapat dihitung. Bentuknya mendekati batas sebenarnya, tetapi bukan batas resmi.
# Peta skematik Banten (tanpa shapefile): poligon digambar manual dari koordinat piksel
# lalu dikonversi ke lon/lat. Simpul batas dipakai bersama sehingga Queen contiguity valid.
N <- list(
a = c(322,182),
b = c(335,168),
d1 = c(350,148),
d2 = c(362,118),
d3 = c(378,108),
d4 = c(400,120),
k1 = c(410,166),
k2 = c(392,200),
c1 = c(355,176),
c2 = c(372,186),
k3 = c(392,216),
k4 = c(402,227),
k5 = c(420,236),
k6 = c(440,236),
k7 = c(458,206),
k8 = c(448,185),
k9 = c(440,160),
s1 = c(446,150),
s2 = c(480,148),
s3 = c(500,163),
t1 = c(493,190),
t2 = c(510,280),
m2 = c(522,290),
j1 = c(470,272),
j2 = c(445,264),
l1 = c(420,265),
p1 = c(300,245),
p2 = c(360,251),
p3 = c(405,245),
q1 = c(550,158),
q2 = c(600,155),
q3 = c(628,172),
q4 = c(632,190),
u3 = c(612,205),
u2 = c(590,218),
u1 = c(570,230),
v1 = c(603,240),
e1 = c(645,240),
e2 = c(650,270),
e3 = c(642,288),
w1 = c(610,288),
w2 = c(603,265),
m1 = c(560,288),
z1 = c(530,320),
z2 = c(515,345),
z3 = c(525,385),
z4 = c(555,435),
z5 = c(540,450),
z6 = c(492,455),
z7 = c(482,500),
z8 = c(470,520),
z9 = c(430,500),
z10 = c(400,485),
z11 = c(370,478),
z12 = c(335,466),
pl1 = c(322,462),
pl2 = c(330,410),
pl3 = c(342,372),
pl4 = c(376,318),
pl5 = c(405,298)
)
cincin <- list(
pandeglang = list(N$p1, N$p2, N$p3, N$l1, N$pl5, N$pl4, N$pl3, N$pl2, N$pl1, c(285,470), c(245,472), c(205,468), c(165,475), c(158,462), c(150,442), c(125,432), c(100,436), c(72,440), c(78,428), c(100,418), c(125,398), c(138,395), c(150,412), c(165,420), c(190,415), c(205,395), c(220,355), c(232,335), c(262,338), c(290,312), c(302,280)),
lebak = list(N$l1, N$j2, N$j1, N$t2, N$m2, N$z1, N$z2, N$z3, N$z4, N$z5, N$z6, N$z7, N$z8, N$z9, N$z10, N$z11, N$z12, N$pl1, N$pl2, N$pl3, N$pl4, N$pl5),
kab_serang = list(N$a, N$b, N$c1, N$c2, N$k2, N$k3, N$k4, N$k5, N$k6, N$k7, N$k8, N$k9, N$s1, N$s2, N$s3, N$t1, N$t2, N$j1, N$j2, N$l1, N$p3, N$p2, N$p1),
kab_tangerang = list(N$s3, N$q1, N$q2, N$q3, N$q4, N$u3, N$u2, N$u1, N$v1, N$w2, N$w1, N$m1, N$m2, N$t2, N$t1),
kota_tangerang = list(N$q4, N$u3, N$u2, N$u1, N$v1, N$e1),
kota_cilegon = list(N$b, N$d1, N$d2, N$d3, N$d4, N$k1, N$k2, N$c2, N$c1),
kota_serang = list(N$k1, N$k2, N$k3, N$k4, N$k5, N$k6, N$k7, N$k8, N$k9),
tangsel = list(N$v1, N$e1, N$e2, N$e3, N$w1, N$w2)
)
pulau_panaitan <- list(c(32,380), c(60,358), c(90,348), c(97,362), c(76,384), c(62,402), c(52,402), c(55,390)) # P. Panaitan (bagian Kab. Pandeglang)
px2ll <- function(p) c(105.13 + (p[1] - 30) * 0.00266, -5.92 - (p[2] - 108) * 0.00262)
buat_poly <- function(titik) {
m <- t(sapply(titik, px2ll)); m <- rbind(m, m[1, ])
st_polygon(list(m))
}
kode_peta <- c(pandeglang = "3601", lebak = "3602", kab_tangerang = "3603", kab_serang = "3604",
kota_tangerang = "3671", kota_cilegon = "3672", kota_serang = "3673", tangsel = "3674")
geom <- lapply(names(kode_peta), function(k) {
mp <- list(buat_poly(cincin[[k]]))
if (k == "pandeglang") mp <- c(mp, list(buat_poly(pulau_panaitan)))
st_multipolygon(lapply(mp, unclass))
})
peta <- st_sf(kode = unname(kode_peta), geometry = st_sfc(geom, crs = 4326))
peta <- left_join(peta, data_banten, by = "kode")
peta <- peta[match(data_banten$kode, peta$kode), ]
ada_peta <- TRUE
titik <- st_centroid(st_geometry(peta))
koord <- st_coordinates(titik)
cat("Peta skematik dimuat:", nrow(peta), "poligon (tanpa shapefile).")## Peta skematik dimuat: 8 poligon (tanpa shapefile).
## Simple feature collection with 8 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 105.1 ymin: -6.999 xmax: 106.8 ymax: -5.92
## Geodetic CRS: WGS 84
## kode wilayah hls rls geometry
## 1 3601 Kab. Pandeglang 13.74 7.16 MULTIPOLYGON (((105.8 -6.27...
## 2 3602 Kab. Lebak 12.11 6.61 MULTIPOLYGON (((106.2 -6.33...
## 3 3603 Kab. Tangerang 12.88 9.06 MULTIPOLYGON (((106.4 -6.06...
## 4 3604 Kab. Serang 12.87 7.88 MULTIPOLYGON (((105.9 -6.11...
## 5 3671 Kota Tangerang 13.90 11.14 MULTIPOLYGON (((106.7 -6.13...
## 6 3672 Kota Cilegon 13.23 10.39 MULTIPOLYGON (((105.9 -6.07...
## 7 3673 Kota Serang 12.83 8.93 MULTIPOLYGON (((106.1 -6.07...
## 8 3674 Kota Tangerang Selatan 14.70 11.86 MULTIPOLYGON (((106.7 -6.26...
skew <- function(x) { m <- length(x); mu <- mean(x); s <- sd(x)
m / ((m - 1) * (m - 2)) * sum(((x - mu) / s)^3) }
kurt <- function(x) { m <- length(x); mu <- mean(x); s <- sd(x)
m * (m + 1) / ((m - 1) * (m - 2) * (m - 3)) * sum(((x - mu) / s)^4) -
3 * (m - 1)^2 / ((m - 2) * (m - 3)) }
ringkas <- function(x) c(
"n" = length(x), "Rata-rata" = mean(x), "Median" = median(x),
"Simpangan baku" = sd(x), "Varians" = var(x),
"Minimum" = min(x), "Maksimum" = max(x), "Range" = diff(range(x)),
"Q1" = unname(quantile(x, .25)), "Q3" = unname(quantile(x, .75)),
"IQR" = IQR(x), "Koef. variasi (%)" = sd(x) / mean(x) * 100,
"Skewness" = skew(x), "Kurtosis (excess)" = kurt(x))
tab_desk <- data.frame(Statistik = names(ringkas(data_banten$hls)),
HLS = ringkas(data_banten$hls),
RLS = ringkas(data_banten$rls), row.names = NULL)
kable(tab_desk, digits = 2, caption = "Statistik deskriptif HLS dan RLS (n = 8 kabupaten/kota)")| Statistik | HLS | RLS |
|---|---|---|
| n | 8.00 | 8.00 |
| Rata-rata | 13.28 | 9.13 |
| Median | 13.05 | 9.00 |
| Simpangan baku | 0.80 | 1.89 |
| Varians | 0.64 | 3.56 |
| Minimum | 12.11 | 6.61 |
| Maksimum | 14.70 | 11.86 |
| Range | 2.59 | 5.25 |
| Q1 | 12.86 | 7.70 |
| Q3 | 13.78 | 10.58 |
| IQR | 0.92 | 2.88 |
| Koef. variasi (%) | 6.05 | 20.67 |
| Skewness | 0.49 | 0.14 |
| Kurtosis (excess) | 0.22 | -1.32 |
h <- data_banten$hls; r <- data_banten$rls
cat(sprintf(
"**Tafsiran.** Rata-rata HLS sebesar %.2f tahun, sedangkan rata-rata RLS hanya %.2f tahun; selisih sekitar %.1f tahun menunjukkan bahwa generasi muda diharapkan bersekolah lebih lama daripada capaian penduduk dewasa saat ini. Koefisien variasi RLS (%.1f%%) jauh lebih besar daripada HLS (%.1f%%), artinya **kesenjangan antarwilayah lebih terasa pada capaian aktual (RLS) daripada pada harapan bersekolah (HLS)**. %s memiliki nilai terendah pada kedua indikator (HLS %.2f; RLS %.2f), sementara %s tertinggi (HLS %.2f; RLS %.2f). Median dan rata-rata yang berdekatan serta skewness yang kecil menunjukkan sebaran yang cukup simetris. Korelasi Pearson HLS–RLS adalah %.2f, yaitu hubungan positif yang kuat namun belum memperhitungkan lokasi.",
mean(h), mean(r), mean(h) - mean(r), sd(r)/mean(r)*100, sd(h)/mean(h)*100,
data_banten$wilayah[which.min(r)], min(h), min(r),
data_banten$wilayah[which.max(r)], max(h), max(r), cor(h, r)))Tafsiran. Rata-rata HLS sebesar 13.28 tahun, sedangkan rata-rata RLS hanya 9.13 tahun; selisih sekitar 4.2 tahun menunjukkan bahwa generasi muda diharapkan bersekolah lebih lama daripada capaian penduduk dewasa saat ini. Koefisien variasi RLS (20.7%) jauh lebih besar daripada HLS (6.0%), artinya kesenjangan antarwilayah lebih terasa pada capaian aktual (RLS) daripada pada harapan bersekolah (HLS). Kab. Lebak memiliki nilai terendah pada kedua indikator (HLS 12.11; RLS 6.61), sementara Kota Tangerang Selatan tertinggi (HLS 14.70; RLS 11.86). Median dan rata-rata yang berdekatan serta skewness yang kecil menunjukkan sebaran yang cukup simetris. Korelasi Pearson HLS–RLS adalah 0.73, yaitu hubungan positif yang kuat namun belum memperhitungkan lokasi.
# Bagi nilai menjadi 4 kelas interval sama lebar, label seperti legenda peta tematik
kelas <- function(x, k = 4) {
br <- seq(floor(min(x)), ceiling(max(x)), length.out = k + 1)
lab <- sprintf("%.1f - %.1f", br[-(k + 1)], br[-1])
factor(lab[cut(x, br, labels = FALSE, include.lowest = TRUE)], levels = lab)
}
buat_peta <- function(dat, var, judul, legenda, palet) {
dat$kelas <- kelas(dat[[var]])
ggplot(dat) +
geom_sf(aes(fill = kelas), colour = "grey25", linewidth = .35) +
geom_sf_text(aes(label = sprintf("%.2f", .data[[var]])), size = 2.8,
fontface = "bold", check_overlap = TRUE) +
scale_fill_brewer(palette = palet, drop = FALSE, name = legenda) +
guides(fill = guide_legend(title.position = "top", nrow = 2)) +
labs(title = judul) +
theme_void(base_size = 11) +
theme(plot.title = element_text(size = 13, margin = margin(b = 6)),
panel.border = element_rect(colour = "black", fill = NA, linewidth = .6),
legend.position = "bottom", legend.justification = "left",
legend.box.background = element_rect(colour = "black", fill = NA),
legend.margin = margin(5, 8, 5, 8), plot.margin = margin(8, 8, 8, 8))
}
if (ada_peta) {
p_hls <- buat_peta(peta, "hls", "Peta Sebaran Harapan Lama Sekolah Provinsi Banten", "HLS 2024 (tahun)", "Blues")
p_rls <- buat_peta(peta, "rls", "Peta Sebaran Rata-rata Lama Sekolah Provinsi Banten", "RLS 2024 (tahun)", "OrRd")
p_hls + p_rls
} else {
data_banten |> mutate(X = koord[, 1], Y = koord[, 2]) |>
pivot_longer(c(hls, rls), names_to = "indikator", values_to = "nilai") |>
ggplot(aes(X, Y, size = nilai, colour = nilai)) +
geom_point() + facet_wrap(~indikator) + scale_colour_viridis_c(direction = -1) +
labs(title = "Sebaran HLS dan RLS (titik pusat perkiraan)", x = "Bujur", y = "Lintang") +
theme_minimal()
}urut_r <- data_banten$wilayah[order(-data_banten$rls)]
urut_h <- data_banten$wilayah[order(-data_banten$hls)]
rank_h <- rank(-data_banten$hls); rank_r <- rank(-data_banten$rls)
selisih <- rank_r - rank_h # positif: peringkat HLS jauh lebih baik daripada RLS
kota <- grepl("^Kota", data_banten$wilayah)
cat(sprintf(
"**Tafsiran.** Pada peta RLS, tiga wilayah tertinggi adalah %s, sedangkan tiga terendah adalah %s. Pada peta HLS, tiga tertinggi adalah %s dan tiga terendah %s. Rata-rata RLS wilayah berstatus kota (%.2f tahun) lebih tinggi %.2f tahun daripada kabupaten (%.2f tahun), sedangkan selisih serupa pada HLS hanya %.2f tahun; jadi kontras kota–kabupaten **lebih tegas pada capaian (RLS) daripada pada harapan (HLS)**. Wilayah dengan peringkat HLS jauh lebih baik daripada peringkat RLS-nya adalah **%s** (peringkat HLS %d, peringkat RLS %d), menandakan perbaikan akses pendidikan pada generasi muda yang belum tercermin pada penduduk dewasa. Secara spasial, nilai tinggi cenderung bertumpu pada wilayah yang saling berdekatan, yang diuji secara formal pada bagian berikutnya.",
paste(head(urut_r, 3), collapse = ", "), paste(tail(urut_r, 3), collapse = ", "),
paste(head(urut_h, 3), collapse = ", "), paste(tail(urut_h, 3), collapse = ", "),
mean(data_banten$rls[kota]), mean(data_banten$rls[kota]) - mean(data_banten$rls[!kota]),
mean(data_banten$rls[!kota]),
mean(data_banten$hls[kota]) - mean(data_banten$hls[!kota]),
data_banten$wilayah[which.max(selisih)], as.integer(rank_h[which.max(selisih)]),
as.integer(rank_r[which.max(selisih)])))Tafsiran. Pada peta RLS, tiga wilayah tertinggi adalah Kota Tangerang Selatan, Kota Tangerang, Kota Cilegon, sedangkan tiga terendah adalah Kab. Serang, Kab. Pandeglang, Kab. Lebak. Pada peta HLS, tiga tertinggi adalah Kota Tangerang Selatan, Kota Tangerang, Kab. Pandeglang dan tiga terendah Kab. Serang, Kota Serang, Kab. Lebak. Rata-rata RLS wilayah berstatus kota (10.58 tahun) lebih tinggi 2.90 tahun daripada kabupaten (7.68 tahun), sedangkan selisih serupa pada HLS hanya 0.76 tahun; jadi kontras kota–kabupaten lebih tegas pada capaian (RLS) daripada pada harapan (HLS). Wilayah dengan peringkat HLS jauh lebih baik daripada peringkat RLS-nya adalah Kab. Pandeglang (peringkat HLS 3, peringkat RLS 7), menandakan perbaikan akses pendidikan pada generasi muda yang belum tercermin pada penduduk dewasa. Secara spasial, nilai tinggi cenderung bertumpu pada wilayah yang saling berdekatan, yang diuji secara formal pada bagian berikutnya.
Misalkan \(w_{ij}\) adalah bobot hubungan wilayah \(i\) dan \(j\). Seluruh matriks dinormalisasi per baris sehingga \(\sum_j w_{ij}=1\).
\[w_{ij}=\frac{1/d_{ij}}{\sum_{j}1/d_{ij}},\quad i\neq j\]
dengan \(d_{ij}\) jarak antartitik pusat wilayah (km). Semua wilayah terhubung, tetapi wilayah yang lebih dekat berbobot lebih besar.
d_km <- matrix(as.numeric(st_distance(titik)), n, n) / 1000
w_id <- 1 / d_km
diag(w_id) <- 0
w_id <- w_id / rowSums(w_id)
dimnames(w_id) <- list(data_banten$kode, data_banten$kode)
lw_id <- mat2listw(w_id, style = "W")
kable(round(w_id, 3), caption = "Matriks pembobot invers jarak (row-standardized)")| 3601 | 3602 | 3603 | 3604 | 3671 | 3672 | 3673 | 3674 | |
|---|---|---|---|---|---|---|---|---|
| 3601 | 0.000 | 0.213 | 0.112 | 0.171 | 0.097 | 0.150 | 0.158 | 0.098 |
| 3602 | 0.168 | 0.000 | 0.141 | 0.169 | 0.120 | 0.120 | 0.154 | 0.129 |
| 3603 | 0.054 | 0.085 | 0.000 | 0.126 | 0.296 | 0.093 | 0.132 | 0.213 |
| 3604 | 0.050 | 0.062 | 0.077 | 0.000 | 0.054 | 0.143 | 0.564 | 0.050 |
| 3671 | 0.043 | 0.067 | 0.273 | 0.082 | 0.000 | 0.066 | 0.084 | 0.385 |
| 3672 | 0.082 | 0.083 | 0.106 | 0.268 | 0.081 | 0.000 | 0.306 | 0.074 |
| 3673 | 0.045 | 0.056 | 0.079 | 0.555 | 0.055 | 0.160 | 0.000 | 0.050 |
| 3674 | 0.048 | 0.079 | 0.216 | 0.083 | 0.424 | 0.066 | 0.085 | 0.000 |
Setiap wilayah dihubungkan dengan \(k=3\) wilayah terdekat berdasarkan jarak titik pusat. Nilai \(k=3\) dipilih karena mendekati rata-rata jumlah tetangga pada pembobot Queen. Sensitivitas terhadap \(k\) diperiksa pada bagian 6.3.
nb_knn <- knn2nb(knearneigh(koord, k = 3, longlat = TRUE))
lw_knn <- nb2listw(nb_knn, style = "W")
data.frame(Wilayah = data_banten$wilayah,
`Tetangga terdekat (k = 3)` = sapply(nb_knn, function(i) paste(data_banten$wilayah[i], collapse = "; ")),
check.names = FALSE) |>
kable(caption = "Daftar tetangga menurut k-NN (k = 3)")| Wilayah | Tetangga terdekat (k = 3) |
|---|---|
| Kab. Pandeglang | Kab. Lebak; Kab. Serang; Kota Serang |
| Kab. Lebak | Kab. Pandeglang; Kab. Serang; Kota Serang |
| Kab. Tangerang | Kota Tangerang; Kota Serang; Kota Tangerang Selatan |
| Kab. Serang | Kab. Tangerang; Kota Cilegon; Kota Serang |
| Kota Tangerang | Kab. Tangerang; Kota Serang; Kota Tangerang Selatan |
| Kota Cilegon | Kab. Tangerang; Kab. Serang; Kota Serang |
| Kota Serang | Kab. Tangerang; Kab. Serang; Kota Cilegon |
| Kota Tangerang Selatan | Kab. Tangerang; Kota Tangerang; Kota Serang |
w_knn <- nb2mat(nb_knn, style = "W")
dimnames(w_knn) <- list(data_banten$kode, data_banten$kode)
kable(round(w_knn, 3), caption = "Matriks pembobot k-NN (row-standardized)")| 3601 | 3602 | 3603 | 3604 | 3671 | 3672 | 3673 | 3674 | |
|---|---|---|---|---|---|---|---|---|
| 3601 | 0.000 | 0.333 | 0.000 | 0.333 | 0.000 | 0.000 | 0.333 | 0.000 |
| 3602 | 0.333 | 0.000 | 0.000 | 0.333 | 0.000 | 0.000 | 0.333 | 0.000 |
| 3603 | 0.000 | 0.000 | 0.000 | 0.000 | 0.333 | 0.000 | 0.333 | 0.333 |
| 3604 | 0.000 | 0.000 | 0.333 | 0.000 | 0.000 | 0.333 | 0.333 | 0.000 |
| 3671 | 0.000 | 0.000 | 0.333 | 0.000 | 0.000 | 0.000 | 0.333 | 0.333 |
| 3672 | 0.000 | 0.000 | 0.333 | 0.333 | 0.000 | 0.000 | 0.333 | 0.000 |
| 3673 | 0.000 | 0.000 | 0.333 | 0.333 | 0.000 | 0.333 | 0.000 | 0.000 |
| 3674 | 0.000 | 0.000 | 0.333 | 0.000 | 0.333 | 0.000 | 0.333 | 0.000 |
Perhatikan bahwa k-NN bersifat asimetris: wilayah A dapat menjadi tetangga B tanpa B menjadi tetangga A.
Dua wilayah bertetangga bila berbagi sisi atau titik batas.
if (ada_peta) {
nb_queen <- poly2nb(peta, queen = TRUE)
} else {
# Ketetanggaan dari pengetahuan geografis (cadangan)
pasangan <- list(c("3601","3602"), c("3601","3604"), c("3602","3604"), c("3602","3603"),
c("3604","3673"), c("3604","3672"), c("3604","3603"), c("3673","3672"),
c("3603","3671"), c("3603","3674"), c("3671","3674"))
A <- matrix(0, n, n, dimnames = list(data_banten$kode, data_banten$kode))
for (p in pasangan) A[p[1], p[2]] <- A[p[2], p[1]] <- 1
nb_queen <- mat2listw(A, style = "B")$neighbours
}
lw_q <- nb2listw(nb_queen, style = "W", zero.policy = TRUE)
data.frame(Wilayah = data_banten$wilayah,
`Jumlah tetangga` = card(nb_queen),
Tetangga = sapply(nb_queen, function(i) paste(data_banten$wilayah[i], collapse = "; ")),
check.names = FALSE) |>
kable(caption = "Daftar tetangga menurut Queen contiguity")| Wilayah | Jumlah tetangga | Tetangga |
|---|---|---|
| Kab. Pandeglang | 2 | Kab. Lebak; Kab. Serang |
| Kab. Lebak | 3 | Kab. Pandeglang; Kab. Tangerang; Kab. Serang |
| Kab. Tangerang | 4 | Kab. Lebak; Kab. Serang; Kota Tangerang; Kota Tangerang Selatan |
| Kab. Serang | 5 | Kab. Pandeglang; Kab. Lebak; Kab. Tangerang; Kota Cilegon; Kota Serang |
| Kota Tangerang | 2 | Kab. Tangerang; Kota Tangerang Selatan |
| Kota Cilegon | 2 | Kab. Serang; Kota Serang |
| Kota Serang | 2 | Kab. Serang; Kota Cilegon |
| Kota Tangerang Selatan | 2 | Kab. Tangerang; Kota Tangerang |
w_q <- nb2mat(nb_queen, style = "W", zero.policy = TRUE)
dimnames(w_q) <- list(data_banten$kode, data_banten$kode)
kable(round(w_q, 3), caption = "Matriks pembobot Queen (row-standardized)")| 3601 | 3602 | 3603 | 3604 | 3671 | 3672 | 3673 | 3674 | |
|---|---|---|---|---|---|---|---|---|
| 3601 | 0.000 | 0.50 | 0.000 | 0.500 | 0.00 | 0.0 | 0.0 | 0.00 |
| 3602 | 0.333 | 0.00 | 0.333 | 0.333 | 0.00 | 0.0 | 0.0 | 0.00 |
| 3603 | 0.000 | 0.25 | 0.000 | 0.250 | 0.25 | 0.0 | 0.0 | 0.25 |
| 3604 | 0.200 | 0.20 | 0.200 | 0.000 | 0.00 | 0.2 | 0.2 | 0.00 |
| 3671 | 0.000 | 0.00 | 0.500 | 0.000 | 0.00 | 0.0 | 0.0 | 0.50 |
| 3672 | 0.000 | 0.00 | 0.000 | 0.500 | 0.00 | 0.0 | 0.5 | 0.00 |
| 3673 | 0.000 | 0.00 | 0.000 | 0.500 | 0.00 | 0.5 | 0.0 | 0.00 |
| 3674 | 0.000 | 0.00 | 0.500 | 0.000 | 0.50 | 0.0 | 0.0 | 0.00 |
Misalkan \(z^{X}\) dan \(z^{Y}\) adalah nilai terstandarisasi HLS dan RLS (simpangan baku populasi). Dengan \(W\) ter-normalisasi baris:
\[I_{XY}=\frac{\sum_{i}z^{X}_{i}\left(\sum_{j}w_{ij}z^{Y}_{j}\right)}{n}\]
\(I_{XY}>0\) berarti nilai HLS yang tinggi cenderung berdampingan dengan nilai RLS yang tinggi (dan sebaliknya). Nilai harapan di bawah \(H_0\) (acak spasial) mendekati \(-1/(n-1)\). Signifikansi diuji dengan permutasi nilai \(Y\) sebanyak 9.999 kali, karena asumsi normalitas sulit dipenuhi untuk \(n=8\).
zs <- function(v) (v - mean(v)) / sqrt(mean((v - mean(v))^2))
zx <- zs(data_banten$hls)
zy <- zs(data_banten$rls)
moran_bv <- function(zx, zy, lw) sum(zx * lag.listw(lw, zy, zero.policy = TRUE)) / length(zx)
uji_bv <- function(lw, nsim = 9999) {
I <- moran_bv(zx, zy, lw)
sim <- replicate(nsim, moran_bv(zx, sample(zy), lw))
data.frame(I = I, `E[I]` = mean(sim), SD = sd(sim), z = (I - mean(sim)) / sd(sim),
`p (1 sisi)` = (sum(sim >= I) + 1) / (nsim + 1),
`p (2 sisi)` = (sum(abs(sim - mean(sim)) >= abs(I - mean(sim))) + 1) / (nsim + 1),
`I (RLS,HLS)` = moran_bv(zy, zx, lw), check.names = FALSE)
}daftar_w <- list("Invers jarak" = lw_id, "k-NN (k=3)" = lw_knn, "Queen" = lw_q)
hasil <- bind_rows(lapply(daftar_w, uji_bv), .id = "Pembobot")
kable(hasil, digits = 4, caption = "Moran's I bivariat global (X = HLS, Y = RLS)")| Pembobot | I | E[I] | SD | z | p (1 sisi) | p (2 sisi) | I (RLS,HLS) |
|---|---|---|---|---|---|---|---|
| Invers jarak | 0.0779 | -0.0007 | 0.1110 | 0.7079 | 0.2506 | 0.4998 | 0.0860 |
| k-NN (k=3) | 0.1273 | -0.0018 | 0.1503 | 0.8591 | 0.2082 | 0.4158 | 0.1609 |
| Queen | 0.2684 | 0.0005 | 0.2053 | 1.3048 | 0.1072 | 0.2137 | 0.2897 |
Pembobot terbaik dipilih berdasarkan nilai \(p\) terkecil (bukti autokorelasi paling kuat); bila seri, dipilih \(I\) terbesar.
terbaik <- hasil |> arrange(`p (1 sisi)`, desc(I)) |> slice(1)
lw_terbaik <- daftar_w[[terbaik$Pembobot]]
terbaik |> kable(digits = 4, caption = "Pembobot terbaik")| Pembobot | I | E[I] | SD | z | p (1 sisi) | p (2 sisi) | I (RLS,HLS) |
|---|---|---|---|---|---|---|---|
| Queen | 0.2684 | 5e-04 | 0.2053 | 1.305 | 0.1072 | 0.2137 | 0.2897 |
sc <- data.frame(wilayah = data_banten$wilayah, zx = zx,
lag = lag.listw(lw_terbaik, zy, zero.policy = TRUE))
ggplot(sc, aes(zx, lag)) +
geom_hline(yintercept = 0) + geom_vline(xintercept = 0) +
geom_smooth(method = "lm", se = FALSE, colour = "firebrick", linetype = "dashed", formula = y ~ x) +
geom_point(size = 3) +
geom_text_repel(aes(label = wilayah), size = 3.2) +
labs(x = "z(HLS)", y = "Lag spasial z(RLS)",
title = sprintf("Moran scatterplot bivariat — pembobot %s (I = %.3f)",
terbaik$Pembobot, terbaik$I)) +
theme_minimal()sig <- terbaik$`p (1 sisi)` < 0.05
cat(sprintf(
"**Tafsiran.** Pembobot **%s** memberikan Moran's I bivariat sebesar **%.3f** (nilai harapan %.3f; $z$ = %.2f; $p$ satu sisi = %.3f). Tanda %s menunjukkan kecenderungan %s. Pada taraf nyata 5%% hasil ini **%s**; %s Pembobot lain menghasilkan I yang jauh lebih kecil karena mengikutkan wilayah yang tidak berbatasan langsung sehingga pola kedekatan fisik terdilusi. Perlu diingat bahwa dengan hanya 8 wilayah, daya uji sangat terbatas sehingga hasil ini bersifat indikatif.",
terbaik$Pembobot, terbaik$I, terbaik$`E[I]`, terbaik$z, terbaik$`p (1 sisi)`,
ifelse(terbaik$I > 0, "positif", "negatif"),
ifelse(terbaik$I > 0,
"wilayah ber-HLS tinggi berdekatan dengan wilayah ber-RLS tinggi (dan sebaliknya)",
"wilayah ber-HLS tinggi berdekatan dengan wilayah ber-RLS rendah"),
ifelse(sig, "signifikan", "belum signifikan"),
ifelse(sig, "terdapat bukti autokorelasi spasial bivariat.",
"belum cukup bukti untuk menyatakan adanya autokorelasi spasial bivariat, meskipun arahnya konsisten dengan dugaan.")))Tafsiran. Pembobot Queen memberikan Moran’s I bivariat sebesar 0.268 (nilai harapan 0.000; \(z\) = 1.30; \(p\) satu sisi = 0.107). Tanda positif menunjukkan kecenderungan wilayah ber-HLS tinggi berdekatan dengan wilayah ber-RLS tinggi (dan sebaliknya). Pada taraf nyata 5% hasil ini belum signifikan; belum cukup bukti untuk menyatakan adanya autokorelasi spasial bivariat, meskipun arahnya konsisten dengan dugaan. Pembobot lain menghasilkan I yang jauh lebih kecil karena mengikutkan wilayah yang tidak berbatasan langsung sehingga pola kedekatan fisik terdilusi. Perlu diingat bahwa dengan hanya 8 wilayah, daya uji sangat terbatas sehingga hasil ini bersifat indikatif.
sens <- bind_rows(lapply(2:5, function(k) {
lw_k <- nb2listw(knn2nb(knearneigh(koord, k = k, longlat = TRUE)), style = "W")
cbind(k = k, uji_bv(lw_k, nsim = 4999))
}))
kable(sens[, c("k", "I", "z", "p (1 sisi)")], digits = 4,
caption = "Sensitivitas Moran's I bivariat terhadap jumlah tetangga k-NN")| k | I | z | p (1 sisi) |
|---|---|---|---|
| 2 | 0.1966 | 0.7227 | 0.2574 |
| 3 | 0.1273 | 0.8621 | 0.2046 |
| 4 | 0.1174 | 1.4890 | 0.0754 |
| 5 | -0.1041 | -1.2035 | 0.8778 |
Perubahan nilai \(I\) dan tanda yang cukup besar antar-\(k\) menunjukkan bahwa kesimpulan sangat bergantung pada definisi ketetanggaan, sehingga pembobot berbasis batas wilayah (Queen) lebih mudah dipertanggungjawabkan secara substantif.
\[I_{i}^{XY}=z^{X}_{i}\sum_{j}w_{ij}z^{Y}_{j}\]
Kuadran ditentukan oleh tanda \(z^{X}_{i}\) dan lag spasial \(\sum_j w_{ij}z^Y_j\):
| Kategori | Arti |
|---|---|
| High-High | HLS tinggi, tetangga ber-RLS tinggi |
| Low-Low | HLS rendah, tetangga ber-RLS rendah |
| High-Low | HLS tinggi, tetangga ber-RLS rendah (pencilan spasial) |
| Low-High | HLS rendah, tetangga ber-RLS tinggi (pencilan spasial) |
Signifikansi memakai permutasi bersyarat (nilai \(Y\) tetangga diacak dari wilayah lain dengan \(z^X_i\) tetap) sebanyak 9.999 kali.
lisa_bv <- function(lw, nsim = 9999) {
lag_y <- lag.listw(lw, zy, zero.policy = TRUE)
Ii <- zx * lag_y
p <- sapply(seq_len(n), function(i) {
nb <- lw$neighbours[[i]]; w <- lw$weights[[i]]
if (length(nb) == 1 && nb[1] == 0) return(NA_real_)
lain <- setdiff(seq_len(n), i)
sim <- replicate(nsim, zx[i] * sum(w * zy[sample(lain, length(nb))]))
if (Ii[i] >= 0) (sum(sim >= Ii[i]) + 1) / (nsim + 1) else (sum(sim <= Ii[i]) + 1) / (nsim + 1)
})
kuadran <- case_when(zx > 0 & lag_y > 0 ~ "High-High",
zx < 0 & lag_y < 0 ~ "Low-Low",
zx > 0 & lag_y < 0 ~ "High-Low",
TRUE ~ "Low-High")
data.frame(kode = data_banten$kode, wilayah = data_banten$wilayah,
zHLS = zx, lagRLS = lag_y, Ii = Ii, p = p, kuadran = kuadran,
kategori = ifelse(!is.na(p) & p < 0.05, kuadran, "Tidak signifikan"),
kategori10 = ifelse(!is.na(p) & p < 0.10, kuadran, "Tidak signifikan"))
}lisa <- lisa_bv(lw_terbaik)
kable(lisa[, c("wilayah", "zHLS", "lagRLS", "Ii", "p", "kuadran", "kategori")], digits = 3,
col.names = c("Wilayah", "z(HLS)", "Lag z(RLS)", "Ii", "Pseudo p", "Kuadran", "Kategori (p < 0,05)"),
caption = sprintf("LISA bivariat dengan pembobot %s", terbaik$Pembobot))| Wilayah | z(HLS) | Lag z(RLS) | Ii | Pseudo p | Kuadran | Kategori (p < 0,05) |
|---|---|---|---|---|---|---|
| Kab. Pandeglang | 0.609 | -1.067 | -0.650 | 0.051 | High-Low | Tidak signifikan |
| Kab. Lebak | -1.561 | -0.621 | 0.969 | 0.057 | Low-Low | Tidak signifikan |
| Kab. Tangerang | -0.536 | 0.138 | -0.074 | 0.404 | Low-High | Tidak signifikan |
| Kab. Serang | -0.549 | -0.396 | 0.217 | 0.049 | Low-Low | Low-Low |
| Kota Tangerang | 0.822 | 0.754 | 0.620 | 0.102 | High-High | Tidak signifikan |
| Kota Cilegon | -0.070 | -0.410 | 0.029 | 0.373 | Low-Low | Tidak signifikan |
| Kota Serang | -0.602 | 0.004 | -0.002 | 0.567 | Low-High | Tidak signifikan |
| Kota Tangerang Selatan | 1.887 | 0.550 | 1.038 | 0.093 | High-High | Tidak signifikan |
warna <- c("High-High" = "#d7191c", "Low-Low" = "#2c7bb6", "High-Low" = "#fdae61",
"Low-High" = "#abd9e9", "Tidak signifikan" = "grey88")
if (ada_peta) {
peta_l <- left_join(peta[, "kode"], lisa, by = "kode")
ggplot(peta_l) +
geom_sf(aes(fill = kategori), colour = "grey30") +
geom_sf_text(aes(label = wilayah), size = 2.8) +
scale_fill_manual(values = warna, name = "Klaster (p < 0,05)", drop = FALSE) +
labs(title = "LISA bivariat: HLS (X) terhadap RLS tetangga (Y)") +
theme_minimal()
} else {
ggplot(lisa |> mutate(X = koord[, 1], Y = koord[, 2]), aes(X, Y, fill = kategori)) +
geom_point(shape = 21, size = 6) + geom_text_repel(aes(label = wilayah), size = 3) +
scale_fill_manual(values = warna, name = "Klaster (p < 0,05)", drop = FALSE) +
labs(title = "LISA bivariat (titik pusat perkiraan)", x = "Bujur", y = "Lintang") +
theme_minimal()
}arti <- c("High-High" = "HLS tinggi dan dikelilingi wilayah ber-RLS tinggi",
"Low-Low" = "HLS rendah dan dikelilingi wilayah ber-RLS rendah",
"High-Low" = "HLS tinggi tetapi dikelilingi wilayah ber-RLS rendah (pencilan spasial)",
"Low-High" = "HLS rendah tetapi dikelilingi wilayah ber-RLS tinggi (pencilan spasial)")
s5 <- lisa[lisa$kategori != "Tidak signifikan", ]
s10 <- lisa[lisa$kategori == "Tidak signifikan" & lisa$kategori10 != "Tidak signifikan", ]
if (nrow(s5) > 0) {
cat("**Signifikan pada taraf 5%:**\n\n")
for (i in seq_len(nrow(s5)))
cat(sprintf("- **%s** (%s, $p$ = %.3f): %s.\n", s5$wilayah[i], s5$kategori[i], s5$p[i], arti[[s5$kategori[i]]]))
} else cat("Tidak ada wilayah yang signifikan pada taraf 5%.\n")Signifikan pada taraf 5%:
if (nrow(s10) > 0) {
cat("\n**Hanya signifikan pada taraf 10% (bukti lemah):**\n\n")
for (i in seq_len(nrow(s10)))
cat(sprintf("- %s (%s, $p$ = %.3f)\n", s10$wilayah[i], s10$kategori10[i], s10$p[i]))
}Hanya signifikan pada taraf 10% (bukti lemah):
cat(sprintf("\nWilayah lainnya (%d) tidak menunjukkan klaster maupun pencilan yang signifikan.\n",
n - nrow(s5) - nrow(s10)))Wilayah lainnya (4) tidak menunjukkan klaster maupun pencilan yang signifikan.
Catatan kehati-hatian. Dengan \(n=8\) dan hanya 2–5 tetangga per wilayah, jumlah kombinasi permutasi sangat kecil sehingga pseudo p-value bersifat diskret dan kurang stabil. Selain itu, pengujian dilakukan serentak pada delapan wilayah tanpa koreksi multiple testing (mis. Bonferroni, \(\alpha = 0{,}05/8 \approx 0{,}006\)). Hasil LISA sebaiknya diperlakukan sebagai temuan eksploratif.
## R version 4.5.2 (2025-10-31)
## Platform: x86_64-apple-darwin20
## Running under: macOS Ventura 13.7.8
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## time zone: Asia/Jakarta
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] knitr_1.50 patchwork_1.3.2 ggrepel_0.9.7 spdep_1.4-2
## [5] spData_2.3.5 sf_1.1-0 ggplot2_4.0.2 tidyr_1.3.1
## [9] dplyr_1.1.4
##
## loaded via a namespace (and not attached):
## [1] s2_1.1.9 sass_0.4.10 generics_0.1.4 class_7.3-23
## [5] KernSmooth_2.23-26 lattice_0.22-7 digest_0.6.39 magrittr_2.0.4
## [9] evaluate_1.0.5 grid_4.5.2 RColorBrewer_1.1-3 fastmap_1.2.0
## [13] Matrix_1.7-4 jsonlite_2.0.0 e1071_1.7-17 DBI_1.2.3
## [17] mgcv_1.9-3 purrr_1.2.0 scales_1.4.0 jquerylib_0.1.4
## [21] cli_3.6.5 rlang_1.2.0 units_1.0-0 splines_4.5.2
## [25] withr_3.0.2 cachem_1.1.0 yaml_2.3.11 tools_4.5.2
## [29] deldir_2.0-4 boot_1.3-32 vctrs_0.6.5 R6_2.6.1
## [33] proxy_0.4-29 lifecycle_1.0.4 classInt_0.4-11 pkgconfig_2.0.3
## [37] pillar_1.11.1 bslib_0.9.0 gtable_0.3.6 glue_1.8.0
## [41] Rcpp_1.1.0 xfun_0.61 tibble_3.3.0 tidyselect_1.2.1
## [45] rstudioapi_0.17.1 farver_2.1.2 nlme_3.1-168 htmltools_0.5.9
## [49] labeling_0.4.3 rmarkdown_2.30 wk_0.9.5 compiler_4.5.2
## [53] S7_0.2.1 sp_2.2-1