Praktikum ini kita akan mempelajari tentang menyusun matriks bobot spasial sampai menduga parameter dengan tiga cara: OLS, Instrumental Variables (IV), dan Maximum Likelihood (ML). Datanya hanya lima wilayah, jadi setiap angka bisa dicocokkan langsung dengan slide kuliah. Tujuannya memahami prosedurnya, bukan menarik kesimpulan substantif dari lima titik data.
Setelah praktikum ini, diharapkan bisa membuat matriks bobot spasial
di R, menghitung spatial lag Wy, menduga model
SAR dengan ketiga metode tersebut, dan membaca output
lagsarlm().
Paket yang dipakai:
library(ggplot2) # grafik
library(spdep) # objek ketetanggaan (nb, listw)
library(spatialreg) # lagsarlm(), stsls()
library(knitr) # tabel
biru <- "#1b6ca8"
oranye <- "#e07a1f"
theme_set(theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold"),
plot.subtitle = element_text(color = "grey35"),
panel.grid.minor = element_blank()))Jika ada paket yang belum terpasang, jalankan
install.packages(c("ggplot2", "spdep", "spatialreg", "knitr"))
terlebih dahulu.
Kasusnya begini: kita ingin menjelaskan pertumbuhan produktivitas (Y) di lima wilayah. Penjelas yang paling natural adalah pertumbuhan output wilayah itu sendiri (X1). Namun produktivitas suatu wilayah mungkin juga dipengaruhi produktivitas wilayah di sekitarnya, misalnya lewat perpindahan tenaga kerja, teknologi, atau perdagangan. Pengaruh seperti ini ditangkap model SAR dengan menambahkan spatial lag pada variabel dependen:
\[\mathbf{y} = \rho \mathbf{W}\mathbf{y} + \mathbf{X}\boldsymbol{\beta} + \boldsymbol{\varepsilon}, \qquad \boldsymbol{\varepsilon} \sim N(\mathbf{0}, \sigma^2 \mathbf{I}_n)\]
Untuk wilayah ke-\(i\), persamaannya menjadi \(y_i = \beta_1 + \beta_2 x_i + \rho \sum_{j} w_{ij} y_j + \varepsilon_i\). Matriks \(\mathbf{W}\) memberi tahu R siapa bertetangga dengan siapa, dan \(\rho\) mengukur seberapa kuat nilai \(y\) di tetangga terbawa ke wilayah \(i\). Bila \(\mathbf{W}\) distandarisasi per baris, \((\mathbf{Wy})_i\) tidak lain adalah rata-rata \(y\) di wilayah-wilayah tetangga.
Data dan koordinat kelima wilayah kita buat langsung di R.
Data <- data.frame(
Lokasi = LETTERS[1:5],
X1 = c(0.6, 1.0, 1.6, 2.6, 2.2), # pertumbuhan output
Y = c(0.4, 0.6, 0.9, 1.1, 1.2) # pertumbuhan produktivitas
)
Koordinat <- data.frame(
Lokasi = LETTERS[1:5],
Datar = c(1, 3, 3, 5, 7),
Vertikal = c(3, 1, 3, 3, 3)
)
Data## Lokasi X1 Y
## 1 A 0.6 0.4
## 2 B 1.0 0.6
## 3 C 1.6 0.9
## 4 D 2.6 1.1
## 5 E 2.2 1.2
(Di kelas data dibaca dari Excel dengan
readxl::read_excel(). Di sini data dibuat langsung supaya
file ini bisa dijalankan tanpa mengatur path folder.)
Matriks ketetanggaan kita susun dari matriks 0/1, angka 1 berarti dua wilayah bertetangga. Setelah itu tiap baris dibagi jumlah barisnya (standarisasi baris) sehingga setiap baris berjumlah 1.
A <- matrix(c(0, 1, 1, 0, 0, # A bertetangga dengan B, C
1, 0, 1, 1, 0, # B bertetangga dengan A, C, D
1, 1, 0, 1, 0, # C bertetangga dengan A, B, D
0, 1, 1, 0, 1, # D bertetangga dengan B, C, E
0, 0, 0, 1, 0), # E hanya bertetangga dengan D
nrow = 5, byrow = TRUE,
dimnames = list(Data$Lokasi, Data$Lokasi))
W <- A / rowSums(A)
round(W, 3)## A B C D E
## A 0.000 0.500 0.500 0.000 0.000
## B 0.333 0.000 0.333 0.333 0.000
## C 0.333 0.333 0.000 0.333 0.000
## D 0.000 0.333 0.333 0.000 0.333
## E 0.000 0.000 0.000 1.000 0.000
## A B C D E
## 1 1 1 1 1
Hasilnya sama dengan matriks \(\mathbf{W}\) di slide: baris A berisi 1/2 untuk B dan C, baris E berisi 1 untuk D, dan seterusnya. Pembobotan lain yang umum dipakai adalah invers jarak, yaitu \(w_{ij} = 1/d_{ij}\), sehingga wilayah yang lebih dekat mendapat bobot lebih besar.
jarak <- as.matrix(dist(Koordinat[, c("Datar", "Vertikal")]))
dimnames(jarak) <- list(Data$Lokasi, Data$Lokasi)
W_inv <- 1 / jarak
diag(W_inv) <- 0 # wilayah tidak bertetangga dengan dirinya sendiri
W_inv_std <- W_inv / rowSums(W_inv) # versi terstandarisasi baris
round(W_inv, 4)## A B C D E
## A 0.0000 0.3536 0.50 0.2500 0.1667
## B 0.3536 0.0000 0.50 0.3536 0.2236
## C 0.5000 0.5000 0.00 0.5000 0.2500
## D 0.2500 0.3536 0.50 0.0000 0.5000
## E 0.1667 0.2236 0.25 0.5000 0.0000
## A B C D E
## A 0.0000 0.2783 0.3936 0.1968 0.1312
## B 0.2471 0.0000 0.3495 0.2471 0.1563
## C 0.2857 0.2857 0.0000 0.2857 0.1429
## D 0.1559 0.2205 0.3118 0.0000 0.3118
## E 0.1462 0.1961 0.2192 0.4385 0.0000
Dengan \(\mathbf{W}\) di tangan, spatial lag diperoleh dengan mengalikannya pada vektor Y. Kita simpan juga \(\mathbf{WX}\), yang nanti dibutuhkan sebagai instrumen.
Data$Wy_tetangga <- as.numeric(W %*% Data$Y)
Data$Wy_inv <- as.numeric(W_inv_std %*% Data$Y)
Data$WX <- as.numeric(W %*% Data$X1)
Data## Lokasi X1 Y Wy_tetangga Wy_inv WX
## 1 A 0.6 0.4 0.75 0.8952244 1.3
## 2 B 1.0 0.6 0.80 0.8727521 1.6
## 3 C 1.6 0.9 0.70 0.7714286 1.4
## 4 D 2.6 1.1 0.90 0.8494460 1.6
## 5 E 2.2 1.2 1.10 0.8557866 2.6
Kolom Wy_tetangga harus berisi 0,75; 0,80; 0,70; 0,90;
dan 1,10, sama seperti di slide. Sebagai contoh, wilayah A punya dua
tetangga, B (0,6) dan C (0,9), sehingga rata-ratanya 0,75.
Supaya lebih terbayang, kita gambar dulu strukturnya. Garis menghubungkan wilayah yang bertetangga dan warna lingkaran menunjukkan nilai Y.
garis <- which(A == 1 & upper.tri(A), arr.ind = TRUE)
garis <- data.frame(x = Koordinat$Datar[garis[, 1]], y = Koordinat$Vertikal[garis[, 1]],
xend = Koordinat$Datar[garis[, 2]], yend = Koordinat$Vertikal[garis[, 2]])
peta <- merge(Koordinat, Data, by = "Lokasi")
peta$teks <- ifelse(peta$Y > 0.8, "white", "grey15")
ggplot() +
geom_segment(data = garis, aes(x = x, y = y, xend = xend, yend = yend),
color = "grey60", linewidth = 0.9) +
geom_point(data = peta, aes(Datar, Vertikal, fill = Y),
shape = 21, size = 14, color = "white", stroke = 1.5) +
geom_text(data = peta, aes(Datar, Vertikal, label = Lokasi, color = teks),
fontface = "bold", size = 5) +
scale_color_identity() +
scale_fill_gradient(low = "#c6dbef", high = "#08306b", name = "Produktivitas (Y)") +
coord_equal(xlim = c(0, 8), ylim = c(0, 4)) +
labs(title = "Peta Ketetanggaan Lima Wilayah",
subtitle = "Garis menunjukkan wilayah yang bertetangga",
x = NULL, y = NULL) +
theme(axis.text = element_blank(), panel.grid = element_blank())Wilayah D dan E, yang produktivitasnya paling tinggi, saling bertetangga, sementara A dan B yang rendah juga berdekatan. Kesan ini bisa dilihat lebih jelas dengan memplotkan Y terhadap rata-rata Y tetangganya.
ggplot(Data, aes(Y, Wy_tetangga)) +
geom_vline(xintercept = mean(Data$Y), linetype = "dashed", color = "grey60") +
geom_hline(yintercept = mean(Data$Wy_tetangga), linetype = "dashed", color = "grey60") +
geom_smooth(method = "lm", se = FALSE, color = oranye, linewidth = 1) +
geom_point(size = 4.5, color = biru) +
geom_text(aes(label = Lokasi), nudge_y = 0.035, fontface = "bold") +
labs(title = "Y terhadap Spatial Lag Wy (W ketetanggaan)",
subtitle = "Garis putus-putus menunjukkan rata-rata masing-masing sumbu",
x = "Produktivitas wilayah (Y)", y = "Rata-rata produktivitas tetangga (Wy)")Garisnya naik: wilayah dengan Y tinggi cenderung dikelilingi tetangga dengan Y tinggi, dan sebaliknya. Pola mengelompok seperti ini disebut autokorelasi spasial positif, dan inilah alasan kita memasukkan \(\rho\mathbf{Wy}\) ke dalam model. Perlu diingat bahwa dengan \(n = 5\) plot ini hanya ilustrasi. Pada data sungguhan, keberadaan autokorelasi spasial diuji dulu dengan Moran’s I dan uji Lagrange Multiplier sebelum model spasial dipilih.
Cara yang paling mudah untuk menduga model SAR adalah memperlakukan
Wy sebagai variabel penjelas biasa lalu menjalankan
lm(). Cara ini keliru, dan kekeliruannya bisa ditunjukkan
secara aljabar. Dugaan OLS untuk \(\rho\) adalah \(\hat{\rho} =
[(\mathbf{Wy})'\mathbf{Wy}]^{-1}(\mathbf{Wy})'\mathbf{y}\).
Dengan mensubstitusi \(\mathbf{y} = \rho
\mathbf{Wy} + \boldsymbol{\varepsilon}\) diperoleh
\[\hat{\rho} = \rho + [(\mathbf{Wy})'\mathbf{Wy}]^{-1}(\mathbf{Wy})'\boldsymbol{\varepsilon}.\]
Suku kedua tidak akan menghilang. Karena \(\mathbf{y} = (\mathbf{I} - \rho\mathbf{W})^{-1}(\mathbf{X}\boldsymbol{\beta} + \boldsymbol{\varepsilon})\), kita punya \((\mathbf{Wy})'\boldsymbol{\varepsilon} = \boldsymbol{\varepsilon}'\mathbf{W}(\mathbf{I}-\rho\mathbf{W})^{-1}\boldsymbol{\varepsilon}\), yaitu bentuk kuadratik dalam \(\boldsymbol{\varepsilon}\) yang tidak bernilai nol selama \(\rho \neq 0\). Maka \(\operatorname{plim}\hat{\rho} \neq \rho\). Dengan kata lain \(\mathbf{Wy}\) berkorelasi dengan galat (endogen), sehingga penduga OLS bias dan tidak konsisten (Anselin, 1988). Bias ini tidak hilang walaupun datanya diperbanyak.
Meski begitu, kita tetap menjalankannya untuk melihat seperti apa hasilnya. Sebagai pembanding disertakan juga regresi biasa tanpa unsur spasial.
ols_biasa <- lm(Y ~ X1, data = Data)
ols_tetangga <- lm(Y ~ X1 + Wy_tetangga, data = Data)
ols_inv <- lm(Y ~ X1 + Wy_inv, data = Data)
kable(round(summary(ols_biasa)$coefficients, 4),
caption = "Regresi biasa: Y ~ X1")| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 0.2165 | 0.1217 | 1.7793 | 0.1732 |
| X1 | 0.3897 | 0.0691 | 5.6434 | 0.0110 |
kable(round(summary(ols_tetangga)$coefficients, 4),
caption = "OLS dengan Wy, W ketetanggaan terstandar")| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | -0.0176 | 0.3606 | -0.0487 | 0.9656 |
| X1 | 0.3464 | 0.0979 | 3.5378 | 0.0714 |
| Wy_tetangga | 0.3568 | 0.5107 | 0.6987 | 0.5570 |
kable(round(summary(ols_inv)$coefficients, 4),
caption = "OLS dengan Wy, W invers jarak terstandar")| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 0.9494 | 1.3183 | 0.7201 | 0.5462 |
| X1 | 0.3726 | 0.0844 | 4.4153 | 0.0477 |
| Wy_inv | -0.8311 | 1.4867 | -0.5590 | 0.6324 |
Angka-angka ini sama dengan slide: untuk W ketetanggaan \(\hat\rho = 0{,}3568\), \(\hat\beta_0 = -0{,}0176\), dan \(\hat\beta_1 = 0{,}3464\); untuk W invers jarak \(\hat\rho = -0{,}8311\), \(\hat\beta_0 = 0{,}9494\), dan \(\hat\beta_1 = 0{,}3726\). Perhatikan bahwa tanda \(\hat\rho\) berbalik hanya karena \(\mathbf{W}\) diganti, dan galat bakunya besar (nilai-\(p\) untuk \(\hat\rho\) masing-masing 0,557 dan 0,632). Dugaan seperti ini tidak stabil.
Bias OLS tidak mudah terlihat dari lima data, jadi kita buat dunia buatan yang nilai \(\rho\) sebenarnya kita ketahui, yaitu \(\rho = 0{,}6\), pada 100 wilayah berbentuk kisi 10×10. Data dibangkitkan 300 kali dari model SAR, dan setiap kali \(\rho\) diduga dengan OLS.
set.seed(2026)
W_sim <- nb2mat(cell2nb(10, 10), style = "W")
n_sim <- nrow(W_sim)
rho_benar <- 0.6
invers_IrW <- solve(diag(n_sim) - rho_benar * W_sim)
rho_ols <- replicate(300, {
x <- rnorm(n_sim)
eps <- rnorm(n_sim)
y <- as.numeric(invers_IrW %*% (1 + 2 * x + eps))
Wy <- as.numeric(W_sim %*% y)
coef(lm(y ~ x + Wy))["Wy"]
})
ggplot(data.frame(rho = rho_ols), aes(rho)) +
geom_histogram(bins = 25, fill = biru, color = "white", alpha = 0.9) +
geom_vline(xintercept = rho_benar, color = "red3", linewidth = 1.1) +
geom_vline(xintercept = mean(rho_ols), color = "grey15", linewidth = 1.1, linetype = "dashed") +
annotate("text", x = rho_benar, y = Inf, label = "ρ sebenarnya = 0,6",
hjust = 1.1, vjust = 2, color = "red3", fontface = "bold") +
annotate("text", x = mean(rho_ols), y = Inf, label = "rata-rata dugaan OLS",
hjust = -0.1, vjust = 4, color = "grey15", fontface = "bold") +
labs(title = "Sebaran Dugaan ρ dengan OLS (300 simulasi)",
subtitle = "Penduga yang tidak bias akan berpusat tepat di garis merah",
x = expression(hat(rho)[OLS]), y = "Frekuensi")## rho_sebenarnya rata_rata_dugaan_OLS
## 0.600000 0.673505
Jika OLS tidak bias, histogram akan berpusat di garis merah. Jarak
antara garis putus-putus (rata-rata dugaan) dan garis merah itulah bias
yang dibicarakan di atas. Karena itu uji-\(t\) dan uji-\(F\) dari lm() tidak valid
untuk model SAR, dan kita membutuhkan penduga yang konsisten. Dua
pilihannya adalah IV dan ML.
Sumber masalah OLS adalah Wy yang berkorelasi dengan galat. Gagasan IV adalah menggantinya dengan versi yang bersih dari galat, yaitu hasil prediksi Wy dari variabel yang eksogen: \(\mathbf{X}\) dan \(\mathbf{WX}\). Prosesnya dua tahap (sehingga sering disebut Two-Stage Least Squares): pertama memprediksi Wy, kedua meregresikan \(\mathbf{y}\) pada hasil prediksi tersebut.
Secara matriks, definisikan \(\mathbf{Z} = [\mathbf{X}\;\; \mathbf{Wy}]\) dan \(\boldsymbol{\theta} = [\boldsymbol{\beta}'\;\; \rho]'\) sehingga model menjadi \(\mathbf{y} = \mathbf{Z}\boldsymbol{\theta} + \boldsymbol{\varepsilon}\). Dengan matriks instrumen \(\mathbf{Q} = [\mathbf{X}\;\; \mathbf{WX}]\),
\[\hat{\mathbf{Z}} = \mathbf{Q}(\mathbf{Q}'\mathbf{Q})^{-1}\mathbf{Q}'\mathbf{Z}, \qquad \hat{\boldsymbol{\theta}}_{IV} = (\hat{\mathbf{Z}}'\mathbf{Z})^{-1}\hat{\mathbf{Z}}'\mathbf{y}.\]
Matriks kovariansnya adalah \(\widehat{\operatorname{Cov}}(\hat{\boldsymbol\theta}) = \hat\sigma^2(\hat{\mathbf{Z}}'\hat{\mathbf{Z}})^{-1}\) dengan \(\hat\sigma^2 = \mathbf{e}'\mathbf{e}/n\) dan \(\mathbf{e} = \mathbf{y} - \mathbf{Z}\hat{\boldsymbol\theta}\). Rumus-rumus ini kita terjemahkan langsung ke R.
y <- Data$Y
n <- length(y)
Z <- cbind("(Intercept)" = 1, X1 = Data$X1, Wy = Data$Wy_tetangga)
Q <- cbind(1, Data$X1, Data$WX)
# tahap 1: Wy diganti hasil prediksinya
Zhat <- Q %*% solve(t(Q) %*% Q) %*% t(Q) %*% Z
round(Zhat, 4)## (Intercept) X1 Wy
## [1,] 1 0.6 0.7109
## [2,] 1 1.0 0.8025
## [3,] 1 1.6 0.7730
## [4,] 1 2.6 0.8607
## [5,] 1 2.2 1.1029
## [,1]
## (Intercept) -0.2057
## X1 0.3116
## Wy 0.6437
Hasilnya \(\hat\beta_0 =
-0{,}2057\), \(\hat\beta_1 =
0{,}3116\), dan \(\hat\rho =
0{,}6437\), persis seperti di slide. Kolom ketiga
Zhat (0,7109; 0,8025; 0,7730; 0,8607; 1,1029) adalah
Wy yang sudah “dibersihkan”. Perubahannya dari nilai
aslinya bisa digambar sebagai berikut.
df_iv <- data.frame(Lokasi = Data$Lokasi,
asli = Data$Wy_tetangga,
murni = Zhat[, 3])
ggplot(df_iv, aes(asli, murni)) +
geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "grey50") +
geom_segment(aes(xend = asli, yend = asli), color = oranye, linewidth = 1) +
geom_point(size = 4.5, color = biru) +
geom_text(aes(label = Lokasi), nudge_y = 0.03, fontface = "bold") +
labs(title = "Wy sebelum dan sesudah Diprediksi dari X dan WX",
subtitle = "Garis oranye menunjukkan pergeseran dari Wy asli ke hasil prediksinya",
x = "Wy asli", y = "Wy hasil prediksi")Titik yang tepat di garis diagonal berarti nilainya tidak berubah; makin panjang garis oranye, makin jauh Wy bergeser. Selanjutnya kita hitung ragam galat, galat baku, dan uji-\(z\) untuk setiap koefisien.
e <- y - Z %*% theta
sigma2 <- sum(e^2) / n
cov_theta <- sigma2 * solve(t(Zhat) %*% Zhat)
se <- sqrt(diag(cov_theta))
z <- as.numeric(theta) / se
p <- 2 * (1 - pnorm(abs(z)))
hasil_iv <- data.frame(
Koefisien = rownames(theta), Estimasi = as.numeric(theta),
SE = se, z = z, p_value = p,
Keputusan = ifelse(abs(z) > qnorm(0.975), "Tolak H0", "Gagal tolak H0")
)
kable(hasil_iv, digits = 4, row.names = FALSE,
caption = paste0("Hasil IV (sigma^2 = ", round(sigma2, 4),
", sigma = ", round(sqrt(sigma2), 4), ")"))| Koefisien | Estimasi | SE | z | p_value | Keputusan |
|---|---|---|---|---|---|
| (Intercept) | -0.2057 | 0.2621 | -0.7849 | 0.4325 | Gagal tolak H0 |
| X1 | 0.3116 | 0.0688 | 4.5306 | 0.0000 | Tolak H0 |
| Wy | 0.6437 | 0.3748 | 1.7174 | 0.0859 | Gagal tolak H0 |
Sebagai pemeriksaan, hasil manual ini bisa dibandingkan dengan fungsi
bawaan stsls(). Argumen W2X = FALSE dipakai
supaya instrumennya sama persis dengan yang kita susun, yaitu \([\mathbf{X}\;\mathbf{WX}]\). Koefisiennya
harus sama, sedangkan galat bakunya boleh sedikit berbeda karena
penyebut ragam galat yang dipakai bisa berbeda.
iv_paket <- stsls(Y ~ X1, data = Data, listw = mat2listw(W, style = "W"), W2X = FALSE)
round(coef(iv_paket), 4) # urutan: Rho, (Intercept), X1## Rho (Intercept) X1
## 0.6437 -0.2057 0.3116
Dengan nilai kritis \(z_{0,975} = 1{,}96\), intersep tidak signifikan (\(|z| = 0{,}785\)), koefisien X1 signifikan (\(|z| = 4{,}530\)), dan \(\rho\) belum signifikan (\(|z| = 1{,}717\)). Persamaan dugaannya adalah \(\hat y_i = -0{,}206 + 0{,}312\,x_i + 0{,}644\sum_j w_{ij}y_j\) dengan \(\hat\sigma^2 = 0{,}0072\). Kenaikan output sebesar satu satuan berasosiasi dengan kenaikan produktivitas sekitar 0,31 satuan, dengan efek tetangga bertanda positif tetapi belum bisa dibuktikan secara statistik. Nilai \(\hat\rho_{IV} = 0{,}644\) juga jauh berbeda dari \(\hat\rho_{OLS} = 0{,}357\), dan selisih itulah koreksi terhadap bias OLS.
Metode ML bertanya nilai \(\rho\), \(\boldsymbol\beta\), dan \(\sigma^2\) mana yang membuat data yang teramati paling mungkin muncul. Dengan \(\boldsymbol\varepsilon = (\mathbf{I} - \rho\mathbf{W})\mathbf{y} - \mathbf{X}\boldsymbol\beta\) dan Jacobian \(J = |\mathbf{I} - \rho\mathbf{W}|\), fungsi log-likelihood-nya adalah
\[\ln L(\boldsymbol\beta, \rho, \sigma^2 \mid \mathbf{y}, \mathbf{X}) = -\frac{n}{2}\ln(2\pi) - \frac{n}{2}\ln\sigma^2 + \ln|\mathbf{I} - \rho\mathbf{W}| - \frac{1}{2\sigma^2}\boldsymbol\varepsilon'\boldsymbol\varepsilon.\]
Dibandingkan dengan regresi biasa, ada tambahan suku \(\ln|\mathbf{I} - \rho\mathbf{W}|\). Suku inilah yang memperhitungkan bahwa semua wilayah saling memengaruhi secara simultan, dan karenanya ML tidak jatuh ke masalah yang dialami OLS. Memaksimumkan \(\ln L\) sama dengan meminimumkan \(\mathbf{e}'\mathbf{e}\) yang dikoreksi log-Jacobian; hasilnya sama dengan OLS hanya jika \(\rho \to 0\). Sifat konsistensi dan efisiensi asimtotik berlaku bila kondisi keteraturan terpenuhi, khususnya \(J > 0\).
Karena \(\boldsymbol\beta\) dan \(\sigma^2\) dapat dinyatakan sebagai fungsi dari \(\rho\), pemaksimuman dilakukan dalam empat langkah pada concentrated likelihood. Mari kita kerjakan satu per satu. Langkah pertama dan kedua adalah meregresikan \(\mathbf{y}\) dan \(\mathbf{Wy}\) masing-masing pada \(\mathbf{X}\) dan menyimpan residunya.
X <- cbind(1, Data$X1)
XtXi <- solve(t(X) %*% X)
# langkah 1: regresi y pada X
b_O <- XtXi %*% t(X) %*% y
e_O <- y - X %*% b_O
# langkah 2: regresi Wy pada X
Wy <- W %*% y
b_L <- XtXi %*% t(X) %*% Wy
e_L <- Wy - X %*% b_L
list(b_O = round(as.numeric(b_O), 4), b_L = round(as.numeric(b_L), 4))## $b_O
## [1] 0.2165 0.3897
##
## $b_L
## [1] 0.6559 0.1213
Nilainya harus sama dengan slide: \(\hat\beta_O = (0{,}2165;\ 0{,}3897)\) dan \(\hat\beta_L = (0{,}6562;\ 0{,}1211)\). Langkah ketiga mencari \(\rho\) yang memaksimumkan \(\ln L_c(\rho) = C - \frac{n}{2}\ln\!\big[\tfrac{1}{n}(\mathbf{e}_O - \rho\mathbf{e}_L)'(\mathbf{e}_O - \rho\mathbf{e}_L)\big] + \ln|\mathbf{I} - \rho\mathbf{W}|\), dan langkah keempat menghitung \(\hat{\boldsymbol\beta}_{ML} = \hat{\boldsymbol\beta}_O - \hat\rho_{ML}\hat{\boldsymbol\beta}_L\) serta \(\hat\sigma^2_{ML}\).
# langkah 3: maksimumkan concentrated log-likelihood terhadap rho
lnLc <- function(rho) {
e <- e_O - rho * e_L
-n / 2 * log(sum(e^2) / n) +
as.numeric(determinant(diag(n) - rho * W, logarithm = TRUE)$modulus)
}
opt <- optimize(lnLc, interval = c(-0.99, 0.99), maximum = TRUE)
rho_ml <- opt$maximum
# langkah 4: dugaan beta dan sigma^2
b_ML <- b_O - rho_ml * b_L
e_ML <- e_O - rho_ml * e_L
sigma2_ml <- sum(e_ML^2) / n
data.frame(rho = rho_ml, beta0 = b_ML[1], beta1 = b_ML[2],
sigma2 = sigma2_ml, lnLc = opt$objective) |>
kable(digits = 4, caption = "Hasil ML manual, W ketetanggaan")| rho | beta0 | beta1 | sigma2 | lnLc |
|---|---|---|---|---|
| 0.2872 | 0.0281 | 0.3549 | 0.0063 | 12.573 |
Hasilnya \(\hat\rho_{ML} = 0{,}2872\), \(\hat{\boldsymbol\beta}_{ML} = (0{,}0280;\ 0{,}3549)\), \(\hat\sigma^2_{ML} = 0{,}0063\), dan \(\ln L_c^* = 12{,}5729\) (tanpa konstanta \(C\)), sama dengan slide. Proses pencarian \(\hat\rho\) tadi bisa dilihat langsung dengan menghitung \(\ln L_c(\rho)\) pada banyak nilai \(\rho\). Kita lakukan untuk kedua jenis \(\mathbf{W}\).
profil_lnLc <- function(Wm, label) {
XtXi <- solve(t(X) %*% X)
e_O <- y - X %*% (XtXi %*% t(X) %*% y)
Wy <- Wm %*% y
e_L <- Wy - X %*% (XtXi %*% t(X) %*% Wy)
f <- function(rho) {
e <- e_O - rho * e_L
-n / 2 * log(sum(e^2) / n) +
as.numeric(determinant(diag(n) - rho * Wm, logarithm = TRUE)$modulus)
}
ev <- Re(eigen(Wm)$values)
batas <- c(1 / min(ev), 1 / max(ev)) # rentang rho dengan J > 0
grid <- seq(batas[1] + 0.01, batas[2] - 0.01, length.out = 400)
opt <- optimize(f, interval = range(grid), maximum = TRUE)
list(kurva = data.frame(rho = grid, lnLc = sapply(grid, f), W = label),
puncak = data.frame(rho = opt$maximum, lnLc = opt$objective, W = label))
}
p1 <- profil_lnLc(W, "W ketetanggaan (terstandar)")
p2 <- profil_lnLc(W_inv, "W invers jarak")
kurva <- rbind(p1$kurva, p2$kurva)
puncak <- rbind(p1$puncak, p2$puncak)
ggplot(kurva, aes(rho, lnLc)) +
geom_line(color = biru, linewidth = 1.1) +
geom_vline(data = puncak, aes(xintercept = rho), color = oranye, linetype = "dashed") +
geom_point(data = puncak, color = oranye, size = 4) +
geom_text(data = puncak, aes(label = paste0("ρ-topi = ", round(rho, 3))),
color = oranye, fontface = "bold", vjust = -1.1, hjust = 0.5) +
geom_vline(xintercept = 0, color = "grey60") +
facet_wrap(~ W, scales = "free") +
labs(title = "Concentrated Log-Likelihood terhadap ρ",
subtitle = "Titik oranye adalah nilai ρ yang memaksimumkan likelihood",
x = expression(rho), y = expression(ln~L[c](rho))) +
theme(strip.text = element_text(face = "bold"))Puncak kurva adalah dugaan ML untuk \(\rho\). Garis abu-abu di \(\rho = 0\) menandai kondisi tanpa efek
spasial: makin jauh dan makin tajam puncaknya dari garis itu, makin kuat
indikasi bahwa \(\rho \neq 0\). Dalam
praktik, pencarian ini tidak perlu dikerjakan manual. Fungsi
lagsarlm() dari paket spatialreg melakukan
semuanya sekaligus.
lw_tetangga <- mat2listw(unname(W), style = "W")
Hasil <- lagsarlm(Y ~ X1, data = Data, listw = lw_tetangga)
summary(Hasil)##
## Call:lagsarlm(formula = Y ~ X1, data = Data, listw = lw_tetangga)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.109224 -0.056418 -0.012722 0.075287 0.103076
##
## Type: lag
## Coefficients: (asymptotic standard errors)
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.028118 0.164543 0.1709 0.8643
## X1 0.354865 0.056629 6.2664 3.694e-10
##
## Rho: 0.28717, LR test value: 0.86648, p-value: 0.35193
## Asymptotic standard error: 0.21686
## z-value: 1.3242, p-value: 0.18543
## Wald statistic: 1.7536, p-value: 0.18543
##
## Log likelihood: 5.478291 for lag model
## ML residual variance (sigma squared): 0.0063135, (sigma: 0.079458)
## Number of observations: 5
## Number of parameters estimated: 4
## AIC: -2.9566, (AIC for lm: -4.0901)
## LM test for residual autocorrelation
## test value: 3.9562, p-value: 0.046698
Cara membaca outputnya: blok Coefficients berisi intersep dan koefisien X1 beserta uji-\(z\); baris Rho berisi \(\hat\rho\) yang diuji dengan uji LR dan uji Wald; sedangkan AIC dibandingkan dengan AIC for lm, yaitu AIC model tanpa efek spasial (makin kecil makin baik). Angkanya sama dengan hitungan manual: \(\hat\rho = 0{,}2872\), intersep \(0{,}0281\), dan \(\hat\beta_1 = 0{,}3549\).
Pada W ketetanggaan ini, koefisien X1 sangat signifikan (\(z = 6{,}27\); \(p < 0{,}001\)), tetapi \(\rho\) tidak: uji LR memberi \(p = 0{,}352\) dan uji Wald \(p = 0{,}185\), sehingga \(H_0: \rho = 0\) tidak ditolak. AIC model lag (\(-2{,}957\)) bahkan lebih besar daripada AIC model biasa (\(-4{,}090\)), artinya untuk data ini model SAR tidak lebih baik daripada regresi klasik.
Sekarang kita ulangi dengan W invers jarak. Mengikuti slide, matriks ini tidak distandarisasi per baris.
##
## Call:lagsarlm(formula = Y ~ X1, data = Data, listw = mat2listw(unname(W_inv)))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.0890398 -0.0682359 0.0092734 0.0439466 0.1040557
##
## Type: lag
## Coefficients: (asymptotic standard errors)
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.600413 0.245876 2.4419 0.01461
## X1 0.397047 0.043479 9.1320 < 2e-16
##
## Rho: -0.32574, LR test value: 1.8613, p-value: 0.17248
## Asymptotic standard error: 0.19886
## z-value: -1.638, p-value: 0.10142
## Wald statistic: 2.6831, p-value: 0.10142
##
## Log likelihood: 5.975688 for lag model
## ML residual variance (sigma squared): 0.0050858, (sigma: 0.071315)
## Number of observations: 5
## Number of parameters estimated: 4
## AIC: -3.9514, (AIC for lm: -4.0901)
## LM test for residual autocorrelation
## test value: 1.9722, p-value: 0.16021
Kali ini \(\hat\rho = -0{,}3257\), bertanda negatif, dengan \(\hat\beta_0 = 0{,}6004\), \(\hat\beta_1 = 0{,}3970\), dan \(\hat\sigma^2 = 0{,}0051\). Tanda negatif akan berarti produktivitas tinggi di tetangga justru menekan wilayah ini, tetapi \(\rho\) tidak signifikan (LR \(p = 0{,}172\); Wald \(p = 0{,}101\)) sehingga tidak perlu ditafsirkan lebih jauh. AIC-nya (\(-3{,}951\)) juga masih sedikit di atas AIC model biasa.
Satu catatan interpretasi: pada model SAR, \(\beta_k\) bukan efek marjinal total, karena perubahan \(x_i\) merambat ke wilayah lain lewat pengganda spasial \((\mathbf{I} - \rho\mathbf{W})^{-1}\). Penguraian menjadi efek langsung, tidak langsung, dan total adalah topik lanjutan. Perlu diingat pula bahwa semua uji di sini bersifat asimtotik, jadi dengan \(n = 5\) nilai-\(p\) hanya berfungsi sebagai ilustrasi prosedur.
Semua hasil tadi kita kumpulkan dalam satu tabel.
perbandingan <- data.frame(
Metode = c("OLS", "OLS", "OLS terkoreksi (IV)", "ML manual", "ML lagsarlm", "ML lagsarlm"),
W = c("Ketetanggaan", "Invers jarak std", "Ketetanggaan",
"Ketetanggaan", "Ketetanggaan", "Invers jarak"),
rho = unname(c(coef(ols_tetangga)["Wy_tetangga"], coef(ols_inv)["Wy_inv"],
theta[3], rho_ml, Hasil$rho, Hasil2$rho)),
b0 = unname(c(coef(ols_tetangga)["(Intercept)"], coef(ols_inv)["(Intercept)"],
theta[1], b_ML[1], coef(Hasil)["(Intercept)"], coef(Hasil2)["(Intercept)"])),
b1 = unname(c(coef(ols_tetangga)["X1"], coef(ols_inv)["X1"],
theta[2], b_ML[2], coef(Hasil)["X1"], coef(Hasil2)["X1"]))
)
perbandingan$Label <- paste0(perbandingan$Metode, "\n(W ", tolower(perbandingan$W), ")")
perbandingan$Kelompok <- ifelse(perbandingan$W == "Ketetanggaan", "W ketetanggaan", "W invers jarak")
kable(perbandingan[, c("Metode", "W", "rho", "b0", "b1")], digits = 4,
col.names = c("Metode", "Matriks W", "ρ", "β0 (intersep)", "β1 (X1)"))| Metode | Matriks W | ρ | β0 (intersep) | β1 (X1) |
|---|---|---|---|---|
| OLS | Ketetanggaan | 0.3568 | -0.0176 | 0.3464 |
| OLS | Invers jarak std | -0.8311 | 0.9494 | 0.3726 |
| OLS terkoreksi (IV) | Ketetanggaan | 0.6437 | -0.2057 | 0.3116 |
| ML manual | Ketetanggaan | 0.2872 | 0.0281 | 0.3549 |
| ML lagsarlm | Ketetanggaan | 0.2872 | 0.0281 | 0.3549 |
| ML lagsarlm | Invers jarak | -0.3257 | 0.6004 | 0.3970 |
Dugaan \(\rho\) dari tiap metode digambarkan berikut.
perbandingan$Label <- factor(perbandingan$Label, levels = rev(perbandingan$Label))
ggplot(perbandingan, aes(rho, Label, color = Kelompok)) +
geom_vline(xintercept = 0, color = "grey50") +
geom_segment(aes(x = 0, xend = rho, yend = Label), linewidth = 1) +
geom_point(size = 5) +
geom_text(aes(label = round(rho, 3)), nudge_x = 0.07, hjust = 0, size = 3.8,
show.legend = FALSE) +
scale_color_manual(values = c("W ketetanggaan" = biru, "W invers jarak" = oranye), name = NULL) +
scale_x_continuous(expand = expansion(mult = c(0.1, 0.2))) +
labs(title = "Dugaan ρ menurut Metode Estimasi",
subtitle = "Garis abu-abu menandai ρ = 0 (tanpa efek spasial)",
x = expression(hat(rho)), y = NULL) +
theme(legend.position = "bottom")Sementara dugaan \(\beta_1\) sebagai berikut.
ggplot(perbandingan, aes(b1, Label, color = Kelompok)) +
geom_point(size = 5) +
geom_text(aes(label = round(b1, 3)), nudge_x = 0.004, hjust = 0, size = 3.8,
show.legend = FALSE) +
scale_color_manual(values = c("W ketetanggaan" = biru, "W invers jarak" = oranye), name = NULL) +
scale_x_continuous(expand = expansion(mult = c(0.1, 0.2))) +
labs(title = "Dugaan β1 menurut Metode Estimasi",
x = expression(hat(beta)[1]), y = NULL) +
theme(legend.position = "bottom")Dua hal langsung terlihat. Pertama, \(\hat\beta_1\) relatif stabil di kisaran 0,31 sampai 0,40 apa pun metode dan \(\mathbf{W}\)-nya, sedangkan \(\hat\rho\) bergerak dari \(-0{,}83\) (OLS, W invers jarak) hingga \(0{,}64\) (IV). Pengaruh tetangga jauh lebih sulit diduga secara presisi daripada pengaruh variabel penjelas, dan ini sejalan dengan galat baku \(\hat\rho\) yang besar di semua metode.
Kedua, metode yang berbeda memberi \(\hat\rho\) yang berbeda walaupun \(\mathbf{W}\)-nya sama: OLS 0,357, IV 0,644,
dan ML 0,287. Karena OLS tidak konsisten, hasilnya tidak dijadikan
acuan. ML manual dan lagsarlm() memberi hasil yang identik,
yang menegaskan bahwa fungsi tersebut memang mengerjakan prosedur empat
langkah tadi. Pergantian \(\mathbf{W}\)
dari ketetanggaan ke invers jarak juga mengubah tanda \(\hat\rho\), jadi dalam penelitian nyata
pilihan \(\mathbf{W}\) harus didasarkan
pada teori dan hasilnya sebaiknya diperiksa dengan beberapa spesifikasi.
Untuk model SAR, ML melalui lagsarlm() adalah pilihan
standar; IV berguna terutama bila asumsi kenormalan galat diragukan atau
ada variabel endogen lain di dalam model.
Pada kedua \(\mathbf{W}\), \(H_0: \rho = 0\) tidak ditolak dan AIC model lag tidak lebih baik daripada model klasik. Jadi pada lima wilayah ini belum ada bukti kuat bahwa efek spasial diperlukan, dan memang bukan itu tujuan latihan ini.
Wy_tetangga pada baris C.lagsarlm() dengan W invers jarak yang sudah
distandarisasi baris (W_inv_std). Apakah \(\hat\rho\) berubah, dan apakah kesimpulan
tentang signifikansinya ikut berubah?A,
lalu ulangi analisis ML. Bagaimana pengaruhnya terhadap \(\hat\rho\)?rho_benar pada simulasi menjadi 0,2 dan 0,8. Pada
nilai \(\rho\) mana bias OLS terlihat
paling besar?summary(Hasil) dalam satu
paragraf, seolah-olah untuk laporan.Referensi. Anselin, L. (1988). Spatial Econometrics: Methods and Models. Kluwer Academic. Slide kuliah Regresi Spasial, Pertemuan 2: Model SAR.