scpc-python provides spatial correlation-robust inference for regression coefficients following Müller & Watson 2022 and Müller & Watson 2023, implemented in Python based on their original Stata implementation.
Citation: If you use this package, please cite Becker, Boll and Voth 2026, Müller & Watson 2022, and Müller & Watson 2023. See CITATION.bib for the BibTeX entries.
If you encounter any issues or have any questions, please open an issue on GitHub or contact the authors.
The package can be installed as standalone package:
uv pip install scpc-pythonor as scpc-python dependency with:
uv pip install spur-pythonThis example starts from the transformed branch of the Becker, Boll and Voth 2026
workflow. In the full workflow, you would first use spurtest to decide
whether to stay in levels or transform. Here we assume the transformed branch
and move directly to the regression plus scpc step.
# assumes spur-python is installed
from spur import load_chetty_data, spurtransform
from scpc import scpc
import statsmodels.formula.api as smf
data = load_chetty_data()
data = data[~data["state"].isin(["AK", "HI"])][
["am", "gini", "fracblack", "lat", "lon"]
]
data = data.dropna(subset=["am", "gini", "fracblack", "lat", "lon"]).copy()
transformed = spurtransform(
"am ~ gini + fracblack",
data,
lon="lon",
lat="lat",
transformation="lbmgls",
prefix="h_",
)
fit = smf.ols("h_am ~ h_gini + h_fracblack", data=transformed).fit()
result = scpc(
fit,
data=transformed,
lon="lon",
lat="lat",
cvs=True,
)
print(result)scpc() returns an SCPCResult object:
print(result): prints an R-like SCPC inference tableresult.scpcstats: the main inference table with coefficient estimates, standard errors, t statistics, p values, and 95% interval endpointsresult.scpccvs: optional stored critical values at 32%, 10%, 5%, and 1%result.coef(): returns named coefficient estimates inscpc-python>=0.1.2result.confint(): returns named confidence intervals inscpc-python>=0.1.2result.summary(): prints the main table plus confidence intervals inscpc-python>=0.1.2result.avc: the average pairwise correlation bound used in the analysisresult.c0: the kernel scale implied byavcresult.cv: the unconditional 5% critical valueresult.q: the number of retained non-constant spatial principal components
The most important scpc() arguments in the workflow above are:
model: the fitted regression modeldata: the data framelon,lat: the geodesic coordinate column namescoords_euclidean: use this instead oflon/latwhen coordinates are Euclidean rather than geographiccluster: optional clustering columnncoef: how many coefficients to reportavc: upper bound on the average pairwise correlationuncond: whether to skip the conditional adjustmentcvs: whether to store the extra critical values
Please refer to the package documentation for detailed information and other (R, Python, Stata) packages.
Becker, Sascha O., P. David Boll and Hans-Joachim Voth "Testing and Correcting for Spatial Unit Roots in Regression Analysis", The Stata Journal, forthcoming. https://pauldavidboll.com/SPUR_Stata_Journal_website.pdf.
Müller, Ulrich K. and Mark W. Watson "Spatial Correlation Robust Inference", Econometrica 90(6) (2022), 2901–2935. https://www.princeton.edu/~umueller/SHAR.pdf.
Müller, Ulrich K. and Mark W. Watson "Spatial Correlation Robust Inference in Linear Regression and Panel Models", Journal of Business & Economic Statistics 41(4) (2023), 1050–1064. https://www.princeton.edu/~umueller/SpatialRegression.pdf.
