Skip to main content

Composable local projections for Python

Project description

LocalProj

CI Python 3.11+ License: MIT

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.0 is 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

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

localproj-0.1.0.tar.gz (66.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

localproj-0.1.0-py3-none-any.whl (76.5 kB view details)

Uploaded Python 3

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

Hashes for localproj-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4df23c0516b7d3a8d9b67cec4820c3c86e1572c91e7f2ded1c82188bf78261e6
MD5 3653fe86a6c7e89358ad214f649e163f
BLAKE2b-256 26c05955773c8d7800bbe4bc1b3395943306b0d3d242f08aa69fb53688f00f31

See more details on using hashes here.

Provenance

The following attestation bundles were made for localproj-0.1.0.tar.gz:

Publisher: publish.yml on whoisnnamdi/localproj

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for localproj-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f75357b71ca2b55904f5435b76bae93ac8a07c41fee1e46b5990861fcc1aac45
MD5 742e65868e05a2ae43fdf57a93f1718e
BLAKE2b-256 60e7709b475f6848f87e5fc78164169705f647521e5f7fe0a2851df466894000

See more details on using hashes here.

Provenance

The following attestation bundles were made for localproj-0.1.0-py3-none-any.whl:

Publisher: publish.yml on whoisnnamdi/localproj

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page