
3.3 Model Selection: Spatial Dependence Diagnostics¶
Part: 3 — Spatial Regression (Chapter 3.3 of 7)
Dataset: diabetes_atlantic.shp (666 counties)
Focus: Lagrange Multiplier tests, Anselin's decision rule, and how much the recommendation depends on the weights definition
Library: spatialstats.regression (PySpatialStats)
1. Overview¶
Chapter 3.2 established that OLS residuals are spatially autocorrelated ($I=0.181$, $p\approx2.5\times10^{-14}$) — but that number alone cannot say which spatial model to fit. This chapter runs the specific test battery built for exactly that decision, computed entirely from the OLS residuals without fitting any spatial model yet.
| Step | What we do |
|---|---|
| LM tests | Five Lagrange Multiplier statistics, each testing a specific form of residual dependence |
| Decision rule | Anselin's (2005) systematic procedure for turning five test outcomes into one recommendation |
| Full table | OLS.diagnostics() — everything from Chapter 3.2 plus the LM battery, in one call |
| Robustness | Repeat the whole procedure under a different weights definition (KNN(6)) and check the recommendation survives |
The recommendation reached here is what Chapter 3.4 actually fits, and Chapter 3.5 checks the recommendation against the fitted models' own residual diagnostics.
2. Python Setup¶
import sys, warnings
from pathlib import Path
import numpy as np
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", category=DeprecationWarning)
def find_repo_root(start: Path = Path.cwd()) -> Path:
for p in [start, *start.parents]:
if (p / "pyproject.toml").exists() and (p / "data").is_dir():
return p
raise FileNotFoundError("Run this notebook from inside the PySpatialStats repository.")
ROOT = find_repo_root()
DATA = ROOT / "data"
try:
import spatialstats
except ImportError:
sys.path.insert(0, str(ROOT))
import spatialstats
from spatialstats.weights import Queen, KNN
from spatialstats.regression import OLS
plt.rcParams.update({
"figure.dpi": 100, "savefig.dpi": 100, "font.size": 10, "axes.titlesize": 11,
"axes.titleweight": "bold", "axes.spines.top": False, "axes.spines.right": False,
"axes.grid": True, "grid.alpha": 0.25, "legend.frameon": False,
})
C = {"blue": "#2b6cb0", "red": "#c0392b", "green": "#2f855a", "orange": "#dd6b20",
"grey": "#718096", "purple": "#6b46c1", "teal": "#2c7a7b"}
counties = gpd.read_file(DATA / "diabetes_atlantic.shp")
covariates = ["Obesity", "PhysInact", "Acc_Exer", "PCP_rate", "FoodEnvIdx", "SVI"]
X = counties[covariates].copy()
X["Urban"] = (counties["UrbRural"] == "Urban").astype(int)
y = counties["Dia_pct"]
w = Queen(counties)
ols = OLS(y, X, w=w)
print(f"spatialstats : version {spatialstats.__version__}")
print(f"OLS residual Moran's I: {ols.residual_moran(w)['I']:.4f} (from Chapter 3.2, for reference)")
spatialstats : version 0.2.0 OLS residual Moran's I: 0.1812 (from Chapter 3.2, for reference)
3. The Lagrange Multiplier Tests¶
Five statistics, all computed from the OLS residuals $\hat e$ and fit (no spatial model estimated):
| Test | Null hypothesis | Sensitive to |
|---|---|---|
| LM-Lag | $\rho = 0$ | A missing spatial lag, assuming $\lambda=0$ |
| LM-Error | $\lambda = 0$ | Spatially correlated errors, assuming $\rho=0$ |
| Robust LM-Lag | $\rho = 0$ | A missing lag, allowing $\lambda \ne 0$ |
| Robust LM-Error | $\lambda = 0$ | Spatially correlated errors, allowing $\rho \ne 0$ |
| LM-SARMA | $\rho = 0$ and $\lambda = 0$ jointly | Either form of dependence |
The non-robust tests are simple and powerful when only one mechanism is present, but they can both appear significant when only one mechanism is truly at work (each "borrows" some of the other's signal) — which is exactly why the robust versions exist: they explicitly net out the other mechanism before testing.
lm = ols.lm_tests(w)
lm.table
| test | statistic | df | p_value | |
|---|---|---|---|---|
| 0 | LM-Lag | 36.144149 | 1 | 1.832478e-09 |
| 1 | Robust LM-Lag | 5.522096 | 1 | 1.877774e-02 |
| 2 | LM-Error | 53.896349 | 1 | 2.113501e-13 |
| 3 | Robust LM-Error | 23.274296 | 1 | 1.404647e-06 |
| 4 | LM-SARMA | 59.418445 | 2 | 1.251551e-13 |
All five are significant. On the non-robust tests alone, this data would look ambiguous — both LM-Lag and LM-Error reject their null. The robust tests separate them: LM-Error's statistic (23.3) is far larger than LM-Lag's (5.5), both after netting out the other mechanism, pointing toward the spatial error interpretation rather than a genuine lag.
4. Anselin's Decision Rule¶
LMResult.recommendation() implements the standard systematic procedure (Anselin 2005):
- Neither LM-Lag nor LM-Error significant → keep OLS.
- Exactly one of the two non-robust tests significant → fit the corresponding model.
- Both significant → compare the robust versions: whichever remains significant (or, if both do, the one with the larger statistic) determines the choice.
print(lm.recommendation())
Spatial error model (both robust tests significant; larger statistic chosen — consider SARMA/Durbin)
Spatial error model — with an explicit caveat baked into the recommendation string itself: both robust tests are significant, which the decision rule treats as ambiguous enough to also suggest checking a model that nests both mechanisms (SARMA or the spatial Durbin model). Chapter 3.4 fits SEM as the primary recommendation, and Chapter 3.5 fits SDM alongside it specifically to check whether the joint model changes the conclusion.
5. Reading the Full Diagnostics Table¶
OLS.diagnostics(w) assembles Chapter 3.2's classical checks and this chapter's LM battery into one table —
the single call to run before deciding how to proceed on any new spatial regression.
ols.diagnostics(w)
| diagnostic | value | p_value | |
|---|---|---|---|
| 0 | Multicollinearity condition number | 48.075084 | NaN |
| 1 | Jarque-Bera (normality) | 7.512306 | 2.337349e-02 |
| 2 | Breusch-Pagan (heteroskedasticity) | 56.765621 | 6.654444e-10 |
| 3 | Moran's I (residuals) | 0.181223 | 2.512732e-14 |
| 4 | LM-Lag | 36.144149 | 1.832478e-09 |
| 5 | Robust LM-Lag | 5.522096 | 1.877774e-02 |
| 6 | LM-Error | 53.896349 | 2.113501e-13 |
| 7 | Robust LM-Error | 23.274296 | 1.404647e-06 |
| 8 | LM-SARMA | 59.418445 | 1.251551e-13 |
6. Does the Weights Definition Change the Answer?¶
Part 2, Chapter 2.6 showed every autocorrelation number moving with the weights definition. Does the regression recommendation survive a different, reasonable choice of $W$?
w_knn = KNN(counties, k=6)
ols_knn = OLS(y, X, w=w_knn)
lm_knn = ols_knn.lm_tests(w_knn)
print("Queen weights:")
print(lm.table.to_string(index=False))
print(f" -> {lm.recommendation()}\n")
print("KNN(6) weights:")
print(lm_knn.table.to_string(index=False))
print(f" -> {lm_knn.recommendation()}")
Queen weights:
test statistic df p_value
LM-Lag 36.144149 1 1.832478e-09
Robust LM-Lag 5.522096 1 1.877774e-02
LM-Error 53.896349 1 2.113501e-13
Robust LM-Error 23.274296 1 1.404647e-06
LM-SARMA 59.418445 2 1.251551e-13
-> Spatial error model (both robust tests significant; larger statistic chosen — consider SARMA/Durbin)
KNN(6) weights:
test statistic df p_value
LM-Lag 43.270779 1 4.766489e-11
Robust LM-Lag 7.477730 1 6.246679e-03
LM-Error 65.952847 1 4.618409e-16
Robust LM-Error 30.159797 1 3.978722e-08
LM-SARMA 73.430576 2 1.134365e-16
-> Spatial error model (both robust tests significant; larger statistic chosen — consider SARMA/Durbin)
The magnitudes shift (KNN(6)'s statistics are all somewhat larger — a denser, more locally-concentrated neighbourhood structure), but the qualitative recommendation is identical: spatial error model, on the same robust-tests-both-significant, larger-statistic-wins basis. This is the kind of robustness check that gives a model choice real credibility — not just fitting whichever model happened to be recommended under one arbitrary weights definition, but confirming the recommendation survives a reasonable alternative.
7. Summary and Conclusions¶
This chapter turned Chapter 3.2's single residual-Moran number into a specific, defensible model recommendation.
What we did¶
- Computed all five LM diagnostics: LM-Lag (36.1), Robust LM-Lag (5.5), LM-Error (53.9), Robust LM-Error (23.3), LM-SARMA (59.4) — all significant at conventional levels.
- Applied Anselin's decision rule: both robust tests significant, error's statistic larger → spatial error model recommended, with a note to also check a joint (SARMA/Durbin) specification.
- Assembled the full diagnostics table (
OLS.diagnostics()) combining Chapter 3.2's classical checks with this chapter's LM battery. - Repeated the entire procedure under KNN(6) weights: the statistics' magnitudes shift, but the qualitative recommendation — spatial error model — is unchanged.
Practical takeaways¶
- Never read LM-Lag and LM-Error in isolation when both are significant; the robust versions exist precisely to resolve that ambiguity, and skipping them risks picking the wrong model.
- A recommendation that survives a change of weights definition is meaningfully more trustworthy than one checked under a single arbitrary choice — make the comparison a habit, not a special case.
- The LM battery is diagnostic, not confirmatory: it recommends a starting point, and Chapter 3.5's fitted-model residual diagnostics are the actual confirmation that the choice worked.
Next steps¶
Chapter 3.4 (Spatial Regression Model Families) fits the spatial lag, spatial error, and Durbin-family models recommended here, with full attention to what each model's coefficients actually mean.
8. Resources¶
Textbooks¶
| Reference | Notes |
|---|---|
| Anselin, L. (1988). Spatial Econometrics: Methods and Models. Kluwer Academic. | Origin of the LM-Lag/LM-Error test pair |
| Anselin, L. (2005). Exploring Spatial Data with GeoDaTM: A Workbook. | Source of the systematic decision rule implemented in recommendation() |
Key papers¶
| Paper | Contribution |
|---|---|
| Anselin, L., Bera, A. K., Florax, R. & Yoon, M. J. (1996). Simple diagnostic tests for spatial dependence. Regional Science and Urban Economics, 26, 77–104. | Origin of the robust LM-Lag/LM-Error tests used to resolve ambiguity |
| Florax, R. J. G. M., Folmer, H. & Rey, S. J. (2003). Specification searches in spatial econometrics: the relevance of Hendry's methodology. Regional Science and Urban Economics, 33, 557–579. | Critical discussion of specification-search strategies, including the decision rule's limitations |
In this library¶
| Object | Role |
|---|---|
spatialstats.regression.lm_tests |
The five-statistic LM battery |
LMResult.recommendation() |
Anselin's decision rule |
OLS.diagnostics() |
Combined classical + spatial diagnostics table |
| Chapter 3.4 | Fits the spatial lag / error / Durbin models this chapter recommends between |