Banner

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)


Table of Contents¶

  1. Overview

  2. Python Setup

  3. The Lagrange Multiplier Tests

  4. Anselin's Decision Rule

  5. Reading the Full Diagnostics Table

  6. Does the Weights Definition Change the Answer?

  7. Summary and Conclusions

  8. Resources


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¶

In [1]:
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.

In [2]:
lm = ols.lm_tests(w)
lm.table
Out[2]:
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):

  1. Neither LM-Lag nor LM-Error significant → keep OLS.
  2. Exactly one of the two non-robust tests significant → fit the corresponding model.
  3. Both significant → compare the robust versions: whichever remains significant (or, if both do, the one with the larger statistic) determines the choice.
In [3]:
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.

In [4]:
ols.diagnostics(w)
Out[4]:
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$?

In [5]:
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¶

  1. 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.
  2. 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.
  3. Assembled the full diagnostics table (OLS.diagnostics()) combining Chapter 3.2's classical checks with this chapter's LM battery.
  4. 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