analyze_spatial_model_by_weights

analyze_spatial_model_by_weights(
    df,
    outcome=None,
    covariates=None,
    *,
    gdf,
    weights=None,
    baseline=None,
    focal=None,
    model='durbin',
    period=None,
    entity=None,
    time=None,
    fixed_effects=None,
    n_draws=10000,
    seed=20250620,
    title=None,
)

Re-estimate a spatial model under alternative weights and compare the impacts.

The weights-choice robustness check of the source paper (notebook c07): the same model is re-estimated under each spatial weights specification, and the focal regressor’s direct/indirect/total impacts are compared across specifications in a table and a three-facet dot-whisker figure (95% Monte-Carlo confidence intervals, baseline highlighted with a dashed reference line).

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 and regressors; default to the roles declared via :func:geometrics.set_roles. None
covariates str | None Dependent variable and regressors; default to the roles declared via :func:geometrics.set_roles. None
gdf gpd.GeoDataFrame Entity geometry carrying the same entity ids as df. required
weights Mapping[str, W] | None Mapping of specification name to libpysal weights. None builds the paper’s suite from the geometry: 4/6/8-nearest-neighbor, queen and rook contiguity, and inverse distance with powers 1 and 2 (all row-standardized). None
baseline str | None Name of the reference specification (highlighted in the figure). Defaults to the first key of weights. None
focal str | None Covariate whose impacts are compared. Defaults to the first covariate. None
model str "lag", "slx", "durbin" (default) or "durbin_error" — a model with a defined impact decomposition. Estimated by ML. 'durbin'
period Any Period to model; 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 dummies in each design. None
n_draws int Monte-Carlo draws for the impact standard errors (the RNG is re-seeded per weights specification, so rows are reproducible individually). 10000
seed int | None Seed for the Monte-Carlo draws. 20250620
title str | None Figure title. Defaults to a title naming the focal regressor. None

Returns

Name Type Description
WeightsRobustnessResult Frozen result with one row per specification (weights, rho, direct / indirect / total and their standard errors, aic, n_obs, w_spec), the dot-whisker figure and the comparison table.

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 a model without impacts (ols / error), an empty weights mapping, an unknown baseline or focal, or degenerate data.

Examples

Compare queen contiguity against 4-nearest-neighbor weights:

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_by_weights
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_by_weights(
    df, "y", ["x"], gdf=gdf, model="lag", entity="id", n_draws=200,
    weights={
        "queen": make_weights(gdf, method="queen"),
        "knn4": make_weights(gdf, method="knn", k=4),
    },
)
print(res.baseline, list(res.df["weights"]))