analyze_spatial_model

analyze_spatial_model(
    df,
    outcome=None,
    covariates=None,
    *,
    gdf,
    w=None,
    model='durbin',
    method='ml',
    period=None,
    entity=None,
    time=None,
    fixed_effects=None,
    impacts=True,
    n_draws=10000,
    seed=20250620,
    spat_diag=False,
    title=None,
)

Estimate a cross-sectional spatial econometric model with impact decomposition.

One period of the panel (the latest by default) is aligned to the geometry and weights, the requested :mod:spreg model is estimated, and — for models with a spatially lagged outcome or regressors — the per-covariate LeSage-Pace direct/indirect/total impacts are computed from betas + vm (Monte-Carlo standard errors for lag/Durbin models, analytic for SLX/Durbin-error).

Parameters

Name Type Description Default
df pd.DataFrame Long panel (or cross-section) holding the outcome and covariates per entity. required
outcome str | None Dependent variable. Defaults to the outcome declared via :func:geometrics.set_roles. None
covariates str | Sequence[str] | None Regressor column(s). Default to the covariates declared via :func:geometrics.set_roles. None
gdf gpd.GeoDataFrame Entity geometry carrying the same entity ids as df. required
w W | None libpysal weights aligned to the gdf ids. None builds the default weights (queen contiguity for polygons) with a :class:~geometrics.GeometricsWarning. None
model str "ols", "lag" (SAR), "error" (SEM), "slx", "durbin" (SDM) or "durbin_error" (SDEM). 'durbin'
method str "ml" (maximum likelihood) or "gm" (method of moments / GMM; not available for durbin_error). OLS-based models (ols / slx) ignore it. 'ml'
period Any Period to model when df has a time dimension; None uses the latest period and records a note. None
entity str | None Panel identifiers; default to the ids declared via :func:geometrics.set_panel. None
time str | None Panel identifiers; default to the ids declared via :func:geometrics.set_panel. None
fixed_effects str | None Categorical column expanded to drop_first dummy regressors (e.g. state fixed effects). Dummies are never spatially lagged when their lag is collinear (full-rank check). None
impacts bool Compute the impact table where defined (lag/durbin: Monte-Carlo; slx/ durbin_error: analytic). OLS and pure error models have no impact decomposition (impacts is None). True
n_draws int Monte-Carlo draws for the impact standard errors. 10000
seed int | None Seed for the Monte-Carlo draws (reproducible by default). 20250620
spat_diag bool For model="ols" only: attach spreg’s spatial diagnostics to the fitted object (see :func:analyze_spatial_diagnostics for the full workflow). False
title str | None Header for the coefficient table. Defaults to the model and outcome labels. None

Returns

Name Type Description
SpatialModelResult Frozen result with the tidy coefficient frame (df), the Great Tables coefficient table (gt), the fitted spreg object (model_obj), the spatial parameters (rho / lam), fit scalars, the impact table (impacts) and w_spec.

Raises

Name Type Description
KeyError If a requested column is not in df.
TypeError If the outcome or a covariate is not numeric.
ValueError For an unknown model / method, the unsupported durbin_error + gm combination, an unknown period, or too few / degenerate observations.

Examples

A spatial lag model on a small constructed lattice:

import geopandas as gpd
import numpy as np
import pandas as pd
from shapely.geometry import box

from geometrics.spatial_models import analyze_spatial_model
from geometrics.weights import make_weights

cells = [box(i % 4, i // 4, i % 4 + 1, i // 4 + 1) for i in range(16)]
gdf = gpd.GeoDataFrame(
    {"id": [f"r{i}" for i in range(16)]}, geometry=cells, crs="EPSG:4326"
)
rng = np.random.default_rng(0)
df = pd.DataFrame({"id": gdf["id"], "x": rng.normal(size=16)})
df["y"] = 2.0 * df["x"] + rng.normal(scale=0.1, size=16)
res = analyze_spatial_model(
    df, "y", ["x"], gdf=gdf, w=make_weights(gdf), model="lag",
    entity="id", n_draws=200,
)
print(res.model, res.n_obs, res.impacts.shape)