Spatial Lag vs Spatial Error Model: How to Choose

TL;DR — Fit spreg.OLS(y, X, w, spat_diag=True) and read the Lagrange Multiplier tests. If only LM-Lag is significant, use a spatial lag model (spreg.GM_Lag); if only LM-Error is significant, use a spatial error model (spreg.GM_Error). If both are significant, compare the robust variants and follow the more significant one. Lag = neighbours’ outcomes drive yours (spillover); error = omitted spatial factors correlate the residuals (nuisance).

Why this matters

Both models correct the same visible symptom — spatially autocorrelated OLS residuals — but they encode completely different data-generating processes, and choosing the wrong one gives you either biased coefficients or a misread of the mechanism. The decision is not a matter of taste: it follows a well-defined diagnostic rule. This page is the decision framework for the spatial regression models workflow, within the broader Python workflows for spatial modeling and regression pipeline. For the mechanics of fitting a lag model once you have chosen it, see implementing spatial lag models in Python.

Environment

bash
pip install "geopandas>=0.14" "libpysal>=4.8" "spreg>=1.5" \
            "numpy>=1.24" "scipy>=1.10"
python
import geopandas as gpd           # >= 0.14
import numpy as np                # >= 1.24
import libpysal                   # >= 4.8
import spreg                      # >= 1.5

The two models side by side

Spatial Lag Model (SAR). The outcome depends directly on neighbours’ outcomes through a feedback term:

y=ρWy+Xβ+ε\mathbf{y} = \rho \mathbf{W}\mathbf{y} + \mathbf{X}\boldsymbol{\beta} + \boldsymbol{\varepsilon}

ρ\rho is the spatial autoregressive coefficient. Because Wy\mathbf{W}\mathbf{y} is on the right-hand side, a shock anywhere propagates through the whole network, so effects spill over.

Spatial Error Model (SEM). The outcome does not depend on neighbours’ outcomes; instead the disturbance is spatially structured:

y=Xβ+u,u=λWu+ε\mathbf{y} = \mathbf{X}\boldsymbol{\beta} + \mathbf{u}, \qquad \mathbf{u} = \lambda \mathbf{W}\mathbf{u} + \boldsymbol{\varepsilon}

λ\lambda is the spatial error coefficient. Here spatial dependence is a nuisance caused by omitted, spatially patterned covariates. OLS stays unbiased but inefficient with wrong standard errors.

Spatial Lag (SAR) Spatial Error (SEM)
Spatial term ρWy\rho \mathbf{W}\mathbf{y} on the right-hand side λWu\lambda \mathbf{W}\mathbf{u} in the disturbance
Mechanism Neighbours’ outcomes drive yours Omitted spatial factors correlate the errors
Substantive meaning Real spillover / diffusion / contagion Nuisance dependence, no feedback
Coefficient reading β\beta = direct effect only; decompose for total β\beta = ordinary marginal effect (like OLS)
OLS if ignored Biased & inconsistent Unbiased but inefficient (wrong SEs)
Estimator spreg.GM_Lag / spreg.ML_Lag spreg.GM_Error / spreg.ML_Error
Diagnostic trigger Robust LM-Lag significant Robust LM-Error significant
Typical example House prices diffusing across blocks Unmeasured soil/climate correlating yields

The decision rule

The choice is made empirically with the Anselin–Florax Lagrange Multiplier sequence, then confirmed with theory. The logic:

  • Only LM-Lag significant → spatial lag model.
  • Only LM-Error significant → spatial error model.
  • Both significant → the standard tests are contaminated by each other; switch to the robust variants and follow whichever robust statistic is more significant.
  • Neither significant, but residual Moran’s I is → an omitted spatially structured covariate; investigate specification before adding a spatial parameter.
  • Neither significant → OLS is adequate.
Spatial lag vs spatial error decision path Start at OLS with spatial diagnostics. If only LM-Lag is significant choose the spatial lag model; if only LM-Error choose the spatial error model; if both are significant compare robust LM tests and follow the more significant one. OLS + spat_diag=True LM-Lag · LM-Error Both LM significant? only LM-Lag Spatial Lag spreg.GM_Lag only LM-Error Spatial Error spreg.GM_Error both Compare robust variants Robust LM-Lag · Robust LM-Error Follow the more significant robust test Confirm the empirical choice against a substantive spillover mechanism and compare fitted AIC.

Running the diagnostic that decides

The single call below produces every statistic the decision rule needs. Build a validated spatial weight matrix first, then run OLS with spat_diag=True.

python
# Weights: row-standardised, no islands
gdf = gpd.read_file("data/tracts.gpkg").to_crs("EPSG:32633").reset_index(drop=True)
w = libpysal.weights.Queen.from_dataframe(gdf)
w.transform = "r"
assert len(w.islands) == 0, f"islands: {w.islands}"

y = gdf["log_price"].values.reshape(-1, 1)
X = gdf[["sqft", "age", "dist_cbd"]].values
names = ["sqft", "age", "dist_cbd"]

ols = spreg.OLS(y, X, w, spat_diag=True, moran=True, name_y="log_price", name_x=names)

# The four decision statistics (each is a (statistic, p-value) pair)
lm_lag,  lm_err  = ols.lm_lag,        ols.lm_error
rlm_lag, rlm_err = ols.lm_lag_robust, ols.lm_error_robust
print(f"LM-Lag        stat={lm_lag[0]:.2f}  p={lm_lag[1]:.4f}")
print(f"LM-Error      stat={lm_err[0]:.2f}  p={lm_err[1]:.4f}")
print(f"Robust LM-Lag stat={rlm_lag[0]:.2f}  p={rlm_lag[1]:.4f}")
print(f"Robust LM-Err stat={rlm_err[0]:.2f}  p={rlm_err[1]:.4f}")

Encode the decision rule directly so the choice is reproducible:

python
def choose_spatial_model(lm_lag, lm_err, rlm_lag, rlm_err, alpha=0.05):
    lag_sig,  err_sig  = lm_lag[1]  < alpha, lm_err[1]  < alpha
    if lag_sig and not err_sig:
        return "lag"
    if err_sig and not lag_sig:
        return "error"
    if lag_sig and err_sig:
        # both significant -> robust tests break the tie
        return "lag" if rlm_lag[1] < rlm_err[1] else "error"
    return "ols"   # neither significant

choice = choose_spatial_model(lm_lag, lm_err, rlm_lag, rlm_err)
print("Selected specification:", choice)

Then fit the chosen specification and compare fit:

python
if choice == "lag":
    model = spreg.GM_Lag(y, X, w, name_y="log_price", name_x=names)
elif choice == "error":
    model = spreg.GM_Error(y, X, w, name_y="log_price", name_x=names)
else:
    model = ols
print(model.summary)

Interpreting the output

Read the four LM statistics as pairs of (statistic, p-value). The standard LM tests answer “is there any lag/error dependence?”, but each is inflated when the other process is present. The robust variants partial out that contamination, which is exactly why they are the tie-breaker when both standard tests fire. Follow the robust test with the smaller p-value.

After fitting, confirm the choice two ways. First, theory: a lag model is only defensible when a genuine outcome-to-outcome spillover mechanism exists (prices, contagion, diffusion). If no such feedback is plausible, prefer the error model even when Robust LM-Lag edges it. Second, fit: compare model.aic between a fitted lag and error model; a large AIC gap corroborates the diagnostic. Finally, refit Moran’s I on the model residuals — if autocorrelation persists, neither simple specification is enough and you should consider a Spatial Durbin Model or geographically varying coefficients.

Best practices

Never skip the robust tests when both fire

Standard LM-Lag and LM-Error are each biased upward in the presence of the other process. When both are significant, the non-robust statistics cannot discriminate; only the robust variants can. Building the decision on the standard tests alone is the classic mistake.

Let theory veto the statistics

The LM sequence is empirical, not causal. If your domain has no mechanism by which one location’s outcome changes another’s, a significant Robust LM-Lag more likely reflects an omitted spatial covariate — favour the error model. Statistics narrow the choice; substance confirms it.

Fix the weights matrix before trusting any test

Islands and disconnected components corrupt the LM statistics and can make ρ\rho or λ\lambda escape (1,1)(-1, 1). Validate w.islands == [] and w.n_components == 1 before reading diagnostics, as detailed in spatial weight matrices.

Remember the coefficients mean different things

Error-model β\beta are ordinary marginal effects; lag-model β\beta are direct effects only and need a (IρW)1(I - \rho W)^{-1} decomposition for the total impact. Do not compare the raw coefficient tables across the two model types as if they were on the same footing.

Troubleshooting

Symptom Likely cause Fix
Both LM tests significant, unclear choice Both processes present; standard tests contaminated Compare Robust LM-Lag vs Robust LM-Error; follow the smaller p-value
AttributeError: no attribute 'lm_lag' spat_diag=True omitted from spreg.OLS Add spat_diag=True to the OLS call
Neither LM significant but residuals look clustered Weak signal or omitted covariate Check residual Moran’s I; revisit covariate selection before adding a spatial term
Chosen model still has autocorrelated residuals Single spatial term insufficient Consider Spatial Durbin Model or GWR for local variation
ρ\rho or λ\lambda outside (1,1)(-1, 1) Islands / disconnected w Fix islands; re-standardise weights before refitting

Next steps

Once you have selected the lag specification, work through the full fit, rho interpretation, and effect decomposition in implementing spatial lag models in Python. If diagnostics point to relationships that vary across space rather than a single global spillover, move to geographically weighted regression.

Frequently Asked Questions

What is the core difference between a spatial lag and a spatial error model?

A spatial lag model (SAR) adds a spatially lagged dependent variable, ρWy\rho \mathbf{W}\mathbf{y}, to the equation: an outcome at one location is directly influenced by neighbours’ outcomes, so effects spill over across the map. A spatial error model (SEM) instead places spatial structure in the disturbance, u=λWu+ε\mathbf{u} = \lambda \mathbf{W}\mathbf{u} + \boldsymbol{\varepsilon}: the outcome is not affected by neighbours’ outcomes, but omitted spatially patterned factors correlate the errors. Lag models a substantive feedback; error models a nuisance.

Which Lagrange Multiplier test decides between lag and error?

Run OLS with spat_diag=True and read LM-Lag and LM-Error. If only one is significant, pick the matching model. If both are significant, the standard tests are contaminated by the other process, so compare the robust variants Robust LM-Lag and Robust LM-Error and follow whichever is more significant. This is the Anselin–Florax decision sequence.

Can I interpret coefficients the same way in both models?

No. In a spatial error model the β\beta coefficients are ordinary marginal effects, read exactly like OLS but with corrected standard errors. In a spatial lag model the β\beta are only direct effects; because a change propagates through the network via (IρW)1(I - \rho W)^{-1}, the total effect includes an indirect spillover and must be decomposed. Reading lag betas as if they were OLS coefficients understates the true impact.


Related

← Back to Spatial Regression Models