Composable local projections for Python
Project description
LocalProj
LocalProj is a Python library for estimating local projections (LPs). The current 0.1.0 implementation includes regular time-series LPs, LP-IV, high-dimensional desparsified-lasso LPs, panel fixed effects and split-panel jackknife estimates, Herbst–Johannsen finite-sample bias correction, smooth LPs, wild-bootstrap and simultaneous inference, and observed-shock Bayesian SU-LPs.
Project status:
0.1.0is an alpha release. The public API may evolve as the package gains broader production use.
Installation
uv add localproj
Plotting is included in the standard installation. Bayesian diagnostics are available as an extra:
uv add "localproj[bayes]"
Quick start
import localproj as lp
fit = lp.lp(
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
data=df,
shock="shock",
horizons=range(0, 13),
outcome="level", # also: "cumulative" or "diff"
sample="horizon", # or "common"
vcov="hc1",
)
print(fit.tidy())
print(fit.coef())
print(fit.confint())
print(fit.summary())
The formula preprocessor supports l(var, periods) and f(var, periods) helpers. horizons=12 means horizons 0..12; an explicit iterable is also accepted. Regular LPs use HC1 covariance when vcov is omitted; HAC/Newey–West remains available explicitly with vcov="hac" or a fixed lag specification.
Supported estimators and inference
| Area | Current support |
|---|---|
| Regular LP | Horizon-by-horizon PyFixest OLS; multiple outcomes and shocks |
| Outcome transforms | Level, cumulative/long difference, period difference, custom callable |
| Samples | Horizon-specific or common complete-case sample |
| Covariance | Regular LPs: IID, HC1–HC3, HAC/Newey-West, and PyFixest cluster specifications; smooth LPs also support HC0 |
| LP-IV | PyFixest/feols-style IV formulas and first-stage diagnostics |
| Panel LP | Group-aware leads/lags, fixed effects, clustered covariance |
| Bias correction | Split-panel jackknife for panels; Herbst–Johannsen analytical BC/BCC for observed-shock time-series LPs |
| Smooth LP | B-spline ridge smoothing, contiguous-fold CV, HC0/NW inference, multiple responses, and single-shock smooth IV |
| High-dimensional LP | Adamek–Smeekes–Wilms desparsified lasso with unpenalized shock coefficients, plug-in tuning, reused nodewise regressions, and Newey–West inference |
| Joint inference | Gaussian sup-t and Scheffé bands, plus cross-horizon Wald tests, for common-sample regular OLS LPs |
| Bootstrap | Wild residual bootstrap with pointwise and sup-t intervals for regular OLS LPs |
| Bayesian | Observed-shock SU-LP with hierarchical GP/Normal-Gamma shrinkage |
| Plotting | Plotnine IRF plots, selection, and outcome/shock faceting |
Multiple outcomes and shocks
fit = lp.lp(
"gdp + cpi ~ mp_shock + fp_shock + l(gdp, 1:4) + l(cpi, 1:4)",
data=df,
shock=["mp_shock", "fp_shock"],
horizons=range(0, 13),
)
fit.tidy() # one row per (outcome, shock, horizon)
fit.coef(outcome="gdp", shock="mp_shock")
fit.plot(facet_by=["outcome", "shock"])
Simultaneous and bootstrap inference
Regular LP simultaneous bands require an aligned common sample:
joint_fit = lp.lp(
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
data=df,
shock="shock",
horizons=range(0, 13),
sample="common",
vcov="hc1",
)
joint_fit.confint(band="sup-t")
joint_fit.confint(band="scheffe")
joint_fit.wald(horizons=range(0, 13), null=0)
Joint inference supports IID and HC1–HC3 covariance directly. HAC joint inference requires an explicit common lag count, such as vcov={"type": "hac", "lags": 4}; an omitted HAC lag remains valid for pointwise fits but does not produce joint covariance because its defaults vary by horizon.
Wild-bootstrap inference is available for regular PyFixest OLS LPs:
boot = joint_fit.bootstrap(reps=999, weights="rademacher", seed=123)
boot.confint(interval="percentile")
boot.confint(band="sup-t")
Bootstrap support currently excludes IV, fixed effects, panels, clustered/HAC covariance, SPJ, and smooth LPs. See docs/bootstrap_inference.md.
LP-IV and panel LPs
PyFixest/feols-style formula parts are passed through horizon by horizon:
iv_fit = lp.lp(
"y ~ controls | 0 | shock ~ instrument",
data=df,
shock="shock",
horizons=range(0, 13),
)
iv_fit.iv_diagnostics()
Panel leads and lags are built within groups. Fixed effects and clustered covariance are delegated to PyFixest:
panel_fit = lp.lp(
"y ~ shock + l(y, 1:2) | unit + time",
data=panel,
groupby="unit",
time="time",
shock="shock",
horizons=range(0, 9),
vcov={"CRV1": "unit"},
)
spj_fit = lp.lp(
"y ~ shock | unit",
data=panel,
groupby="unit",
time="time",
shock="shock",
horizons=range(0, 9),
estimator="spj",
vcov={"CRV1": "unit"},
)
SPJ currently supports one-way fixed effects matching groupby; two-way FE and multiway-cluster SPJ are not yet implemented. See docs/iv_lp.md and docs/panel_lp.md for assumptions, diagnostics, sampling, and inference details.
Herbst–Johannsen finite-sample correction
For a time-series LP with one directly observed iid shock and predetermined controls, use the recursive Herbst–Johannsen BCC estimator:
hj_fit = lp.lp(
"y ~ shock + l(y, 1:4)",
data=df,
shock="shock",
horizons=range(0, 13),
time="date",
estimator="herbst_johannsen", # `"hj"` also works
vcov="hc1",
)
The default recursively plugs corrected lower-horizon estimates into the analytical bias expression (equation 11 in Herbst & Johannsen). Use hj={"plugin": "ols"} for their one-step BC estimator (equation 10). Horizons must be ordered, contiguous, and start at zero. The current implementation supports one observed shock, no IV/fixed effects/panels, and a contiguous complete time-series sample. It retains the original OLS covariance for first-order asymptotic inference; the correction can still increase finite-sample variance. Diagnostics and horizon-specific correction terms are in hj_fit.metadata["bias_correction"]. See docs/herbst_johannsen.md.
Smooth LPs
smooth_fit = lp.lp(
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
data=df,
shock="shock",
horizons=range(0, 21),
estimator="smooth",
smooth={"lambda": "cv", "r": 2, "vcov": "nw"},
)
Smooth LPs support multiple outcomes and observed shocks. Smooth IV supports one outcome and one endogenous shock:
smooth_iv = lp.lp(
"y ~ l(y, 1:4) + l(shock, 1:4) | 0 | shock ~ instrument",
data=df,
shock="shock",
horizons=range(0, 21),
estimator="smooth",
smooth={"lambda": "cv", "vcov": "nw"},
)
Fixed effects and grouped/panel smooth LPs remain unsupported. See docs/smooth_lp.md for the estimand, tuning options, uncertainty, and smooth-IV scope.
High-dimensional desparsified-lasso LPs
Use estimator="debiased_lasso" (aliases: "desparsified_lasso" and "hdlp") when the formula contains many controls, potentially more than observations:
hdlp_fit = lp.lp(
"y ~ shock + l(y, 1:12) + l(shock, 1:12) + "
+ " + ".join(macro_controls),
data=df,
shock="shock",
horizons=range(0, 25),
time="date",
estimator="debiased_lasso",
vcov="hac",
hdlp={"plugin_constant": 0.8, "random_state": 123},
)
The named shock coefficients are left unpenalized. Other formula columns are selected with the iterative plug-in lasso. Nodewise regressions use the longest outcome-independent source sample with a valid RHS design; its scaling and desparsifying state are reused only when exact Origins, columns, and evaluated design values align. Inference uses Origin Order to construct the authors' Newey–West covariance with an Andrews data-driven bandwidth by default. Explicit non-HAC requests are rejected. A fixed maximum lag can be requested with vcov={"type": "hac", "lags": 4}; excessive lags are rejected rather than truncated, and automatic Andrews selection requires at least four observations and defined score innovations. Exact own-response impact is returned as the normalized coefficient 1 with zero covariance; normalized-only requests skip nuisance fitting entirely.
The current scope is observed-shock time-series LPs without IV, fixed effects, or panel grouping. Data are demeaned and standardized internally. hdlp diagnostics record selected controls, initial and nodewise penalties, nodewise reuse, and bandwidths. See docs/high_dimensional_lasso.md.
Bayesian SU-LP
bayes_fit = lp.lp(
"y ~ shock + l(y, 1:2)",
data=df,
shock="shock",
horizons=range(0, 9),
estimator="bayesian_sulp",
sample="common",
sulp={"draws": 1000, "burnin": 1000, "chains": 2, "seed": 123},
)
bayes_fit.tidy(level=0.9)
bayes_fit.plot(level=0.9, include_gp=True)
bayes_fit.posterior_draws
The Bayesian estimator currently supports one outcome, one or more observed shocks, simple additive RHS terms, and contiguous horizons starting at zero. IV/proxy measurement equations, panels, multiple outcomes, missing-lead imputation, and stochastic volatility are deferred. See docs/bayesian_sulp.md.
Documentation
docs/api_design.md— current API and proposed extensionsdocs/architecture.md— current implementation and target architectureCONTEXT.md— local-projection domain languagedocs/mvp_plan.md— implementation status and roadmapdocs/technique_inventory.md— literature-driven method inventorydocs/outcome_transform_examples.md— outcome transformationsdocs/multiple_response_examples.md— multi-response plottingdocs/bootstrap_inference.md— bootstrap workflow and scopedocs/iv_lp.md— instrumental-variable LP assumptions, diagnostics, and examplesdocs/panel_lp.md— fixed-effect panel LP and split-panel jackknife workflowsdocs/herbst_johannsen.md— Herbst–Johannsen BC/BCC workflow and scopedocs/smooth_lp.md— smooth observed-shock and smooth-IV workflowsdocs/bayesian_sulp.md— Bayesian SU-LP workflow and scopedocs/high_dimensional_lasso.md— high-dimensional desparsified-lasso workflow and scopedocs/replications/— literature replicationsliterature/— literature references and selected figuresCHANGELOG.md— notable changes by release
License
The LocalProj source code and original documentation are available under the MIT License.
Third-party datasets and published figures retain their original rights and are not covered by
this license; see literature/README.md.
Development
uv run --extra dev --extra bayes pytest -q
uv run --extra dev ruff check .
uv build
The test suite covers the core estimator, formulas, Horizon Samples, high-dimensional lasso inference, panel/SPJ and Herbst–Johannsen estimation, smooth LPs, IV diagnostics, simultaneous bands, bootstrap inference, Bayesian SU-LP, plotting, and sample handling.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file localproj-0.1.0.tar.gz.
File metadata
- Download URL: localproj-0.1.0.tar.gz
- Upload date:
- Size: 66.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4df23c0516b7d3a8d9b67cec4820c3c86e1572c91e7f2ded1c82188bf78261e6
|
|
| MD5 |
3653fe86a6c7e89358ad214f649e163f
|
|
| BLAKE2b-256 |
26c05955773c8d7800bbe4bc1b3395943306b0d3d242f08aa69fb53688f00f31
|
Provenance
The following attestation bundles were made for localproj-0.1.0.tar.gz:
Publisher:
publish.yml on whoisnnamdi/localproj
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
localproj-0.1.0.tar.gz -
Subject digest:
4df23c0516b7d3a8d9b67cec4820c3c86e1572c91e7f2ded1c82188bf78261e6 - Sigstore transparency entry: 2159611768
- Sigstore integration time:
-
Permalink:
whoisnnamdi/localproj@e2f153b6b65dce44bbdbec83b80a1ae18d888904 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/whoisnnamdi
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e2f153b6b65dce44bbdbec83b80a1ae18d888904 -
Trigger Event:
release
-
Statement type:
File details
Details for the file localproj-0.1.0-py3-none-any.whl.
File metadata
- Download URL: localproj-0.1.0-py3-none-any.whl
- Upload date:
- Size: 76.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f75357b71ca2b55904f5435b76bae93ac8a07c41fee1e46b5990861fcc1aac45
|
|
| MD5 |
742e65868e05a2ae43fdf57a93f1718e
|
|
| BLAKE2b-256 |
60e7709b475f6848f87e5fc78164169705f647521e5f7fe0a2851df466894000
|
Provenance
The following attestation bundles were made for localproj-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on whoisnnamdi/localproj
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
localproj-0.1.0-py3-none-any.whl -
Subject digest:
f75357b71ca2b55904f5435b76bae93ac8a07c41fee1e46b5990861fcc1aac45 - Sigstore transparency entry: 2159611817
- Sigstore integration time:
-
Permalink:
whoisnnamdi/localproj@e2f153b6b65dce44bbdbec83b80a1ae18d888904 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/whoisnnamdi
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e2f153b6b65dce44bbdbec83b80a1ae18d888904 -
Trigger Event:
release
-
Statement type: