localproj 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- localproj-0.1.0/CHANGELOG.md +38 -0
- localproj-0.1.0/LICENSE +21 -0
- localproj-0.1.0/PKG-INFO +313 -0
- localproj-0.1.0/README.md +274 -0
- localproj-0.1.0/pyproject.toml +62 -0
- localproj-0.1.0/src/localproj/__init__.py +19 -0
- localproj-0.1.0/src/localproj/_fitted.py +203 -0
- localproj-0.1.0/src/localproj/bootstrap.py +458 -0
- localproj-0.1.0/src/localproj/core.py +403 -0
- localproj-0.1.0/src/localproj/estimators/__init__.py +5 -0
- localproj-0.1.0/src/localproj/estimators/base.py +33 -0
- localproj-0.1.0/src/localproj/estimators/bayesian.py +1309 -0
- localproj-0.1.0/src/localproj/estimators/bias_corrected.py +326 -0
- localproj-0.1.0/src/localproj/estimators/high_dimensional.py +1030 -0
- localproj-0.1.0/src/localproj/estimators/panel.py +233 -0
- localproj-0.1.0/src/localproj/estimators/regular.py +559 -0
- localproj-0.1.0/src/localproj/estimators/smooth.py +787 -0
- localproj-0.1.0/src/localproj/formula.py +212 -0
- localproj-0.1.0/src/localproj/horizon.py +395 -0
- localproj-0.1.0/src/localproj/plotting.py +193 -0
- localproj-0.1.0/src/localproj/result.py +506 -0
- localproj-0.1.0/src/localproj/transforms.py +213 -0
- localproj-0.1.0/src/localproj/vcov.py +97 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to LocalProj are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-07-12
|
|
11
|
+
|
|
12
|
+
Initial alpha release of LocalProj.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- A single `localproj.lp()` entry point and a common result contract for supported estimators.
|
|
17
|
+
- Horizon-specific and common samples with explicit observation-origin alignment.
|
|
18
|
+
- Formula-based regular OLS and IV local projections with fixed effects, generated leads and
|
|
19
|
+
lags, multiple outcomes, and multiple shocks.
|
|
20
|
+
- IID, heteroskedasticity-robust, HAC/Newey-West, and clustered pointwise covariance
|
|
21
|
+
estimators, with HC1 as the regular-LP default.
|
|
22
|
+
- Cross-horizon covariance, Gaussian sup-t and Scheffé bands, and path-specific Wald tests for
|
|
23
|
+
common-sample regular OLS projections.
|
|
24
|
+
- Wild residual bootstrap inference with percentile, basic, normal, and sup-t intervals.
|
|
25
|
+
- Panel fixed-effects estimation and split-panel jackknife bias correction.
|
|
26
|
+
- Herbst–Johannsen analytical finite-sample bias correction for observed-shock time-series LPs.
|
|
27
|
+
- Barnichon–Brownlees B-spline smooth local projections, including cross-validated smoothing and
|
|
28
|
+
single-endogenous-shock smooth IV.
|
|
29
|
+
- Adamek–Smeekes–Wilms high-dimensional local projections using desparsified lasso and
|
|
30
|
+
Newey–West inference.
|
|
31
|
+
- Observed-shock Bayesian SU-LPs with hierarchical GP/Normal-Gamma shrinkage and optional ArviZ
|
|
32
|
+
integration.
|
|
33
|
+
- Plotnine impulse-response plots in the standard installation.
|
|
34
|
+
- Focused literature replications covering regular, panel, bias-corrected, smooth,
|
|
35
|
+
high-dimensional, and Bayesian estimators.
|
|
36
|
+
|
|
37
|
+
[Unreleased]: https://github.com/whoisnnamdi/localproj/compare/v0.1.0...HEAD
|
|
38
|
+
[0.1.0]: https://github.com/whoisnnamdi/localproj/releases/tag/v0.1.0
|
localproj-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nnamdi Iregbulem and LocalProj contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
localproj-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: localproj
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Composable local projections for Python
|
|
5
|
+
Keywords: econometrics,local projections,impulse responses,time series,panel data
|
|
6
|
+
Author: LocalProj contributors
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
22
|
+
Requires-Dist: formulaic>=1.0
|
|
23
|
+
Requires-Dist: numpy>=1.26
|
|
24
|
+
Requires-Dist: pandas>=2.0
|
|
25
|
+
Requires-Dist: plotnine==0.15.3
|
|
26
|
+
Requires-Dist: pyfixest>=0.50.1
|
|
27
|
+
Requires-Dist: scipy>=1.11
|
|
28
|
+
Requires-Dist: arviz>=0.17 ; extra == 'bayes'
|
|
29
|
+
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.8 ; extra == 'dev'
|
|
31
|
+
Requires-Python: >=3.11
|
|
32
|
+
Project-URL: Homepage, https://github.com/whoisnnamdi/localproj
|
|
33
|
+
Project-URL: Repository, https://github.com/whoisnnamdi/localproj
|
|
34
|
+
Project-URL: Issues, https://github.com/whoisnnamdi/localproj/issues
|
|
35
|
+
Project-URL: Changelog, https://github.com/whoisnnamdi/localproj/blob/main/CHANGELOG.md
|
|
36
|
+
Provides-Extra: bayes
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# LocalProj
|
|
41
|
+
|
|
42
|
+
[](https://github.com/whoisnnamdi/localproj/actions/workflows/ci.yml)
|
|
43
|
+
[](pyproject.toml)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
|
|
46
|
+
`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.
|
|
47
|
+
|
|
48
|
+
> **Project status:** `0.1.0` is an alpha release. The public API may evolve as the package gains broader production use.
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv add localproj
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Plotting is included in the standard installation. Bayesian diagnostics are available as an extra:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv add "localproj[bayes]"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import localproj as lp
|
|
66
|
+
|
|
67
|
+
fit = lp.lp(
|
|
68
|
+
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
|
|
69
|
+
data=df,
|
|
70
|
+
shock="shock",
|
|
71
|
+
horizons=range(0, 13),
|
|
72
|
+
outcome="level", # also: "cumulative" or "diff"
|
|
73
|
+
sample="horizon", # or "common"
|
|
74
|
+
vcov="hc1",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
print(fit.tidy())
|
|
78
|
+
print(fit.coef())
|
|
79
|
+
print(fit.confint())
|
|
80
|
+
print(fit.summary())
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
## Supported estimators and inference
|
|
86
|
+
|
|
87
|
+
| Area | Current support |
|
|
88
|
+
|---|---|
|
|
89
|
+
| Regular LP | Horizon-by-horizon PyFixest OLS; multiple outcomes and shocks |
|
|
90
|
+
| Outcome transforms | Level, cumulative/long difference, period difference, custom callable |
|
|
91
|
+
| Samples | Horizon-specific or common complete-case sample |
|
|
92
|
+
| Covariance | Regular LPs: IID, HC1–HC3, HAC/Newey-West, and PyFixest cluster specifications; smooth LPs also support HC0 |
|
|
93
|
+
| LP-IV | PyFixest/`feols`-style IV formulas and first-stage diagnostics |
|
|
94
|
+
| Panel LP | Group-aware leads/lags, fixed effects, clustered covariance |
|
|
95
|
+
| Bias correction | Split-panel jackknife for panels; Herbst–Johannsen analytical BC/BCC for observed-shock time-series LPs |
|
|
96
|
+
| Smooth LP | B-spline ridge smoothing, contiguous-fold CV, HC0/NW inference, multiple responses, and single-shock smooth IV |
|
|
97
|
+
| High-dimensional LP | Adamek–Smeekes–Wilms desparsified lasso with unpenalized shock coefficients, plug-in tuning, reused nodewise regressions, and Newey–West inference |
|
|
98
|
+
| Joint inference | Gaussian sup-t and Scheffé bands, plus cross-horizon Wald tests, for common-sample regular OLS LPs |
|
|
99
|
+
| Bootstrap | Wild residual bootstrap with pointwise and sup-t intervals for regular OLS LPs |
|
|
100
|
+
| Bayesian | Observed-shock SU-LP with hierarchical GP/Normal-Gamma shrinkage |
|
|
101
|
+
| Plotting | Plotnine IRF plots, selection, and outcome/shock faceting |
|
|
102
|
+
|
|
103
|
+
## Multiple outcomes and shocks
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
fit = lp.lp(
|
|
107
|
+
"gdp + cpi ~ mp_shock + fp_shock + l(gdp, 1:4) + l(cpi, 1:4)",
|
|
108
|
+
data=df,
|
|
109
|
+
shock=["mp_shock", "fp_shock"],
|
|
110
|
+
horizons=range(0, 13),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
fit.tidy() # one row per (outcome, shock, horizon)
|
|
114
|
+
fit.coef(outcome="gdp", shock="mp_shock")
|
|
115
|
+
fit.plot(facet_by=["outcome", "shock"])
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Simultaneous and bootstrap inference
|
|
119
|
+
|
|
120
|
+
Regular LP simultaneous bands require an aligned common sample:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
joint_fit = lp.lp(
|
|
124
|
+
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
|
|
125
|
+
data=df,
|
|
126
|
+
shock="shock",
|
|
127
|
+
horizons=range(0, 13),
|
|
128
|
+
sample="common",
|
|
129
|
+
vcov="hc1",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
joint_fit.confint(band="sup-t")
|
|
133
|
+
joint_fit.confint(band="scheffe")
|
|
134
|
+
joint_fit.wald(horizons=range(0, 13), null=0)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
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.
|
|
138
|
+
|
|
139
|
+
Wild-bootstrap inference is available for regular PyFixest OLS LPs:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
boot = joint_fit.bootstrap(reps=999, weights="rademacher", seed=123)
|
|
143
|
+
boot.confint(interval="percentile")
|
|
144
|
+
boot.confint(band="sup-t")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Bootstrap support currently excludes IV, fixed effects, panels, clustered/HAC covariance, SPJ, and smooth LPs. See [`docs/bootstrap_inference.md`](docs/bootstrap_inference.md).
|
|
148
|
+
|
|
149
|
+
## LP-IV and panel LPs
|
|
150
|
+
|
|
151
|
+
PyFixest/`feols`-style formula parts are passed through horizon by horizon:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
iv_fit = lp.lp(
|
|
155
|
+
"y ~ controls | 0 | shock ~ instrument",
|
|
156
|
+
data=df,
|
|
157
|
+
shock="shock",
|
|
158
|
+
horizons=range(0, 13),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
iv_fit.iv_diagnostics()
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Panel leads and lags are built within groups. Fixed effects and clustered covariance are delegated to PyFixest:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
panel_fit = lp.lp(
|
|
168
|
+
"y ~ shock + l(y, 1:2) | unit + time",
|
|
169
|
+
data=panel,
|
|
170
|
+
groupby="unit",
|
|
171
|
+
time="time",
|
|
172
|
+
shock="shock",
|
|
173
|
+
horizons=range(0, 9),
|
|
174
|
+
vcov={"CRV1": "unit"},
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
spj_fit = lp.lp(
|
|
178
|
+
"y ~ shock | unit",
|
|
179
|
+
data=panel,
|
|
180
|
+
groupby="unit",
|
|
181
|
+
time="time",
|
|
182
|
+
shock="shock",
|
|
183
|
+
horizons=range(0, 9),
|
|
184
|
+
estimator="spj",
|
|
185
|
+
vcov={"CRV1": "unit"},
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
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`](docs/iv_lp.md) and [`docs/panel_lp.md`](docs/panel_lp.md) for assumptions, diagnostics, sampling, and inference details.
|
|
190
|
+
|
|
191
|
+
## Herbst–Johannsen finite-sample correction
|
|
192
|
+
|
|
193
|
+
For a time-series LP with one directly observed iid shock and predetermined controls, use the recursive Herbst–Johannsen BCC estimator:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
hj_fit = lp.lp(
|
|
197
|
+
"y ~ shock + l(y, 1:4)",
|
|
198
|
+
data=df,
|
|
199
|
+
shock="shock",
|
|
200
|
+
horizons=range(0, 13),
|
|
201
|
+
time="date",
|
|
202
|
+
estimator="herbst_johannsen", # `"hj"` also works
|
|
203
|
+
vcov="hc1",
|
|
204
|
+
)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
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`](docs/herbst_johannsen.md).
|
|
208
|
+
|
|
209
|
+
## Smooth LPs
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
smooth_fit = lp.lp(
|
|
213
|
+
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
|
|
214
|
+
data=df,
|
|
215
|
+
shock="shock",
|
|
216
|
+
horizons=range(0, 21),
|
|
217
|
+
estimator="smooth",
|
|
218
|
+
smooth={"lambda": "cv", "r": 2, "vcov": "nw"},
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Smooth LPs support multiple outcomes and observed shocks. Smooth IV supports one outcome and one endogenous shock:
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
smooth_iv = lp.lp(
|
|
226
|
+
"y ~ l(y, 1:4) + l(shock, 1:4) | 0 | shock ~ instrument",
|
|
227
|
+
data=df,
|
|
228
|
+
shock="shock",
|
|
229
|
+
horizons=range(0, 21),
|
|
230
|
+
estimator="smooth",
|
|
231
|
+
smooth={"lambda": "cv", "vcov": "nw"},
|
|
232
|
+
)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Fixed effects and grouped/panel smooth LPs remain unsupported. See [`docs/smooth_lp.md`](docs/smooth_lp.md) for the estimand, tuning options, uncertainty, and smooth-IV scope.
|
|
236
|
+
|
|
237
|
+
## High-dimensional desparsified-lasso LPs
|
|
238
|
+
|
|
239
|
+
Use `estimator="debiased_lasso"` (aliases: `"desparsified_lasso"` and `"hdlp"`) when the formula contains many controls, potentially more than observations:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
hdlp_fit = lp.lp(
|
|
243
|
+
"y ~ shock + l(y, 1:12) + l(shock, 1:12) + "
|
|
244
|
+
+ " + ".join(macro_controls),
|
|
245
|
+
data=df,
|
|
246
|
+
shock="shock",
|
|
247
|
+
horizons=range(0, 25),
|
|
248
|
+
time="date",
|
|
249
|
+
estimator="debiased_lasso",
|
|
250
|
+
vcov="hac",
|
|
251
|
+
hdlp={"plugin_constant": 0.8, "random_state": 123},
|
|
252
|
+
)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
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.
|
|
256
|
+
|
|
257
|
+
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`](docs/high_dimensional_lasso.md).
|
|
258
|
+
|
|
259
|
+
## Bayesian SU-LP
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
bayes_fit = lp.lp(
|
|
263
|
+
"y ~ shock + l(y, 1:2)",
|
|
264
|
+
data=df,
|
|
265
|
+
shock="shock",
|
|
266
|
+
horizons=range(0, 9),
|
|
267
|
+
estimator="bayesian_sulp",
|
|
268
|
+
sample="common",
|
|
269
|
+
sulp={"draws": 1000, "burnin": 1000, "chains": 2, "seed": 123},
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
bayes_fit.tidy(level=0.9)
|
|
273
|
+
bayes_fit.plot(level=0.9, include_gp=True)
|
|
274
|
+
bayes_fit.posterior_draws
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
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`](docs/bayesian_sulp.md).
|
|
278
|
+
|
|
279
|
+
## Documentation
|
|
280
|
+
|
|
281
|
+
- [`docs/api_design.md`](docs/api_design.md) — current API and proposed extensions
|
|
282
|
+
- [`docs/architecture.md`](docs/architecture.md) — current implementation and target architecture
|
|
283
|
+
- [`CONTEXT.md`](CONTEXT.md) — local-projection domain language
|
|
284
|
+
- [`docs/mvp_plan.md`](docs/mvp_plan.md) — implementation status and roadmap
|
|
285
|
+
- [`docs/technique_inventory.md`](docs/technique_inventory.md) — literature-driven method inventory
|
|
286
|
+
- [`docs/outcome_transform_examples.md`](docs/outcome_transform_examples.md) — outcome transformations
|
|
287
|
+
- [`docs/multiple_response_examples.md`](docs/multiple_response_examples.md) — multi-response plotting
|
|
288
|
+
- [`docs/bootstrap_inference.md`](docs/bootstrap_inference.md) — bootstrap workflow and scope
|
|
289
|
+
- [`docs/iv_lp.md`](docs/iv_lp.md) — instrumental-variable LP assumptions, diagnostics, and examples
|
|
290
|
+
- [`docs/panel_lp.md`](docs/panel_lp.md) — fixed-effect panel LP and split-panel jackknife workflows
|
|
291
|
+
- [`docs/herbst_johannsen.md`](docs/herbst_johannsen.md) — Herbst–Johannsen BC/BCC workflow and scope
|
|
292
|
+
- [`docs/smooth_lp.md`](docs/smooth_lp.md) — smooth observed-shock and smooth-IV workflows
|
|
293
|
+
- [`docs/bayesian_sulp.md`](docs/bayesian_sulp.md) — Bayesian SU-LP workflow and scope
|
|
294
|
+
- [`docs/high_dimensional_lasso.md`](docs/high_dimensional_lasso.md) — high-dimensional desparsified-lasso workflow and scope
|
|
295
|
+
- [`docs/replications/`](docs/replications/) — literature replications
|
|
296
|
+
- [`literature/`](literature/README.md) — literature references and selected figures
|
|
297
|
+
- [`CHANGELOG.md`](CHANGELOG.md) — notable changes by release
|
|
298
|
+
|
|
299
|
+
## License
|
|
300
|
+
|
|
301
|
+
The LocalProj source code and original documentation are available under the [MIT License](LICENSE).
|
|
302
|
+
Third-party datasets and published figures retain their original rights and are not covered by
|
|
303
|
+
this license; see [`literature/README.md`](literature/README.md).
|
|
304
|
+
|
|
305
|
+
## Development
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
uv run --extra dev --extra bayes pytest -q
|
|
309
|
+
uv run --extra dev ruff check .
|
|
310
|
+
uv build
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
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.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# LocalProj
|
|
2
|
+
|
|
3
|
+
[](https://github.com/whoisnnamdi/localproj/actions/workflows/ci.yml)
|
|
4
|
+
[](pyproject.toml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
`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.
|
|
8
|
+
|
|
9
|
+
> **Project status:** `0.1.0` is an alpha release. The public API may evolve as the package gains broader production use.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv add localproj
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Plotting is included in the standard installation. Bayesian diagnostics are available as an extra:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv add "localproj[bayes]"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import localproj as lp
|
|
27
|
+
|
|
28
|
+
fit = lp.lp(
|
|
29
|
+
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
|
|
30
|
+
data=df,
|
|
31
|
+
shock="shock",
|
|
32
|
+
horizons=range(0, 13),
|
|
33
|
+
outcome="level", # also: "cumulative" or "diff"
|
|
34
|
+
sample="horizon", # or "common"
|
|
35
|
+
vcov="hc1",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
print(fit.tidy())
|
|
39
|
+
print(fit.coef())
|
|
40
|
+
print(fit.confint())
|
|
41
|
+
print(fit.summary())
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
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.
|
|
45
|
+
|
|
46
|
+
## Supported estimators and inference
|
|
47
|
+
|
|
48
|
+
| Area | Current support |
|
|
49
|
+
|---|---|
|
|
50
|
+
| Regular LP | Horizon-by-horizon PyFixest OLS; multiple outcomes and shocks |
|
|
51
|
+
| Outcome transforms | Level, cumulative/long difference, period difference, custom callable |
|
|
52
|
+
| Samples | Horizon-specific or common complete-case sample |
|
|
53
|
+
| Covariance | Regular LPs: IID, HC1–HC3, HAC/Newey-West, and PyFixest cluster specifications; smooth LPs also support HC0 |
|
|
54
|
+
| LP-IV | PyFixest/`feols`-style IV formulas and first-stage diagnostics |
|
|
55
|
+
| Panel LP | Group-aware leads/lags, fixed effects, clustered covariance |
|
|
56
|
+
| Bias correction | Split-panel jackknife for panels; Herbst–Johannsen analytical BC/BCC for observed-shock time-series LPs |
|
|
57
|
+
| Smooth LP | B-spline ridge smoothing, contiguous-fold CV, HC0/NW inference, multiple responses, and single-shock smooth IV |
|
|
58
|
+
| High-dimensional LP | Adamek–Smeekes–Wilms desparsified lasso with unpenalized shock coefficients, plug-in tuning, reused nodewise regressions, and Newey–West inference |
|
|
59
|
+
| Joint inference | Gaussian sup-t and Scheffé bands, plus cross-horizon Wald tests, for common-sample regular OLS LPs |
|
|
60
|
+
| Bootstrap | Wild residual bootstrap with pointwise and sup-t intervals for regular OLS LPs |
|
|
61
|
+
| Bayesian | Observed-shock SU-LP with hierarchical GP/Normal-Gamma shrinkage |
|
|
62
|
+
| Plotting | Plotnine IRF plots, selection, and outcome/shock faceting |
|
|
63
|
+
|
|
64
|
+
## Multiple outcomes and shocks
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
fit = lp.lp(
|
|
68
|
+
"gdp + cpi ~ mp_shock + fp_shock + l(gdp, 1:4) + l(cpi, 1:4)",
|
|
69
|
+
data=df,
|
|
70
|
+
shock=["mp_shock", "fp_shock"],
|
|
71
|
+
horizons=range(0, 13),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
fit.tidy() # one row per (outcome, shock, horizon)
|
|
75
|
+
fit.coef(outcome="gdp", shock="mp_shock")
|
|
76
|
+
fit.plot(facet_by=["outcome", "shock"])
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Simultaneous and bootstrap inference
|
|
80
|
+
|
|
81
|
+
Regular LP simultaneous bands require an aligned common sample:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
joint_fit = lp.lp(
|
|
85
|
+
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
|
|
86
|
+
data=df,
|
|
87
|
+
shock="shock",
|
|
88
|
+
horizons=range(0, 13),
|
|
89
|
+
sample="common",
|
|
90
|
+
vcov="hc1",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
joint_fit.confint(band="sup-t")
|
|
94
|
+
joint_fit.confint(band="scheffe")
|
|
95
|
+
joint_fit.wald(horizons=range(0, 13), null=0)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
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.
|
|
99
|
+
|
|
100
|
+
Wild-bootstrap inference is available for regular PyFixest OLS LPs:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
boot = joint_fit.bootstrap(reps=999, weights="rademacher", seed=123)
|
|
104
|
+
boot.confint(interval="percentile")
|
|
105
|
+
boot.confint(band="sup-t")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Bootstrap support currently excludes IV, fixed effects, panels, clustered/HAC covariance, SPJ, and smooth LPs. See [`docs/bootstrap_inference.md`](docs/bootstrap_inference.md).
|
|
109
|
+
|
|
110
|
+
## LP-IV and panel LPs
|
|
111
|
+
|
|
112
|
+
PyFixest/`feols`-style formula parts are passed through horizon by horizon:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
iv_fit = lp.lp(
|
|
116
|
+
"y ~ controls | 0 | shock ~ instrument",
|
|
117
|
+
data=df,
|
|
118
|
+
shock="shock",
|
|
119
|
+
horizons=range(0, 13),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
iv_fit.iv_diagnostics()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Panel leads and lags are built within groups. Fixed effects and clustered covariance are delegated to PyFixest:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
panel_fit = lp.lp(
|
|
129
|
+
"y ~ shock + l(y, 1:2) | unit + time",
|
|
130
|
+
data=panel,
|
|
131
|
+
groupby="unit",
|
|
132
|
+
time="time",
|
|
133
|
+
shock="shock",
|
|
134
|
+
horizons=range(0, 9),
|
|
135
|
+
vcov={"CRV1": "unit"},
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
spj_fit = lp.lp(
|
|
139
|
+
"y ~ shock | unit",
|
|
140
|
+
data=panel,
|
|
141
|
+
groupby="unit",
|
|
142
|
+
time="time",
|
|
143
|
+
shock="shock",
|
|
144
|
+
horizons=range(0, 9),
|
|
145
|
+
estimator="spj",
|
|
146
|
+
vcov={"CRV1": "unit"},
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
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`](docs/iv_lp.md) and [`docs/panel_lp.md`](docs/panel_lp.md) for assumptions, diagnostics, sampling, and inference details.
|
|
151
|
+
|
|
152
|
+
## Herbst–Johannsen finite-sample correction
|
|
153
|
+
|
|
154
|
+
For a time-series LP with one directly observed iid shock and predetermined controls, use the recursive Herbst–Johannsen BCC estimator:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
hj_fit = lp.lp(
|
|
158
|
+
"y ~ shock + l(y, 1:4)",
|
|
159
|
+
data=df,
|
|
160
|
+
shock="shock",
|
|
161
|
+
horizons=range(0, 13),
|
|
162
|
+
time="date",
|
|
163
|
+
estimator="herbst_johannsen", # `"hj"` also works
|
|
164
|
+
vcov="hc1",
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
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`](docs/herbst_johannsen.md).
|
|
169
|
+
|
|
170
|
+
## Smooth LPs
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
smooth_fit = lp.lp(
|
|
174
|
+
"y ~ shock + l(y, 1:4) + l(shock, 1:4)",
|
|
175
|
+
data=df,
|
|
176
|
+
shock="shock",
|
|
177
|
+
horizons=range(0, 21),
|
|
178
|
+
estimator="smooth",
|
|
179
|
+
smooth={"lambda": "cv", "r": 2, "vcov": "nw"},
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Smooth LPs support multiple outcomes and observed shocks. Smooth IV supports one outcome and one endogenous shock:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
smooth_iv = lp.lp(
|
|
187
|
+
"y ~ l(y, 1:4) + l(shock, 1:4) | 0 | shock ~ instrument",
|
|
188
|
+
data=df,
|
|
189
|
+
shock="shock",
|
|
190
|
+
horizons=range(0, 21),
|
|
191
|
+
estimator="smooth",
|
|
192
|
+
smooth={"lambda": "cv", "vcov": "nw"},
|
|
193
|
+
)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Fixed effects and grouped/panel smooth LPs remain unsupported. See [`docs/smooth_lp.md`](docs/smooth_lp.md) for the estimand, tuning options, uncertainty, and smooth-IV scope.
|
|
197
|
+
|
|
198
|
+
## High-dimensional desparsified-lasso LPs
|
|
199
|
+
|
|
200
|
+
Use `estimator="debiased_lasso"` (aliases: `"desparsified_lasso"` and `"hdlp"`) when the formula contains many controls, potentially more than observations:
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
hdlp_fit = lp.lp(
|
|
204
|
+
"y ~ shock + l(y, 1:12) + l(shock, 1:12) + "
|
|
205
|
+
+ " + ".join(macro_controls),
|
|
206
|
+
data=df,
|
|
207
|
+
shock="shock",
|
|
208
|
+
horizons=range(0, 25),
|
|
209
|
+
time="date",
|
|
210
|
+
estimator="debiased_lasso",
|
|
211
|
+
vcov="hac",
|
|
212
|
+
hdlp={"plugin_constant": 0.8, "random_state": 123},
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
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.
|
|
217
|
+
|
|
218
|
+
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`](docs/high_dimensional_lasso.md).
|
|
219
|
+
|
|
220
|
+
## Bayesian SU-LP
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
bayes_fit = lp.lp(
|
|
224
|
+
"y ~ shock + l(y, 1:2)",
|
|
225
|
+
data=df,
|
|
226
|
+
shock="shock",
|
|
227
|
+
horizons=range(0, 9),
|
|
228
|
+
estimator="bayesian_sulp",
|
|
229
|
+
sample="common",
|
|
230
|
+
sulp={"draws": 1000, "burnin": 1000, "chains": 2, "seed": 123},
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
bayes_fit.tidy(level=0.9)
|
|
234
|
+
bayes_fit.plot(level=0.9, include_gp=True)
|
|
235
|
+
bayes_fit.posterior_draws
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
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`](docs/bayesian_sulp.md).
|
|
239
|
+
|
|
240
|
+
## Documentation
|
|
241
|
+
|
|
242
|
+
- [`docs/api_design.md`](docs/api_design.md) — current API and proposed extensions
|
|
243
|
+
- [`docs/architecture.md`](docs/architecture.md) — current implementation and target architecture
|
|
244
|
+
- [`CONTEXT.md`](CONTEXT.md) — local-projection domain language
|
|
245
|
+
- [`docs/mvp_plan.md`](docs/mvp_plan.md) — implementation status and roadmap
|
|
246
|
+
- [`docs/technique_inventory.md`](docs/technique_inventory.md) — literature-driven method inventory
|
|
247
|
+
- [`docs/outcome_transform_examples.md`](docs/outcome_transform_examples.md) — outcome transformations
|
|
248
|
+
- [`docs/multiple_response_examples.md`](docs/multiple_response_examples.md) — multi-response plotting
|
|
249
|
+
- [`docs/bootstrap_inference.md`](docs/bootstrap_inference.md) — bootstrap workflow and scope
|
|
250
|
+
- [`docs/iv_lp.md`](docs/iv_lp.md) — instrumental-variable LP assumptions, diagnostics, and examples
|
|
251
|
+
- [`docs/panel_lp.md`](docs/panel_lp.md) — fixed-effect panel LP and split-panel jackknife workflows
|
|
252
|
+
- [`docs/herbst_johannsen.md`](docs/herbst_johannsen.md) — Herbst–Johannsen BC/BCC workflow and scope
|
|
253
|
+
- [`docs/smooth_lp.md`](docs/smooth_lp.md) — smooth observed-shock and smooth-IV workflows
|
|
254
|
+
- [`docs/bayesian_sulp.md`](docs/bayesian_sulp.md) — Bayesian SU-LP workflow and scope
|
|
255
|
+
- [`docs/high_dimensional_lasso.md`](docs/high_dimensional_lasso.md) — high-dimensional desparsified-lasso workflow and scope
|
|
256
|
+
- [`docs/replications/`](docs/replications/) — literature replications
|
|
257
|
+
- [`literature/`](literature/README.md) — literature references and selected figures
|
|
258
|
+
- [`CHANGELOG.md`](CHANGELOG.md) — notable changes by release
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
The LocalProj source code and original documentation are available under the [MIT License](LICENSE).
|
|
263
|
+
Third-party datasets and published figures retain their original rights and are not covered by
|
|
264
|
+
this license; see [`literature/README.md`](literature/README.md).
|
|
265
|
+
|
|
266
|
+
## Development
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
uv run --extra dev --extra bayes pytest -q
|
|
270
|
+
uv run --extra dev ruff check .
|
|
271
|
+
uv build
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
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.
|