dpd-stream 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.
- dpd_stream-0.1.0/CHANGELOG.md +53 -0
- dpd_stream-0.1.0/CITATION.cff +20 -0
- dpd_stream-0.1.0/LICENSE.md +21 -0
- dpd_stream-0.1.0/MANIFEST.in +2 -0
- dpd_stream-0.1.0/PKG-INFO +155 -0
- dpd_stream-0.1.0/README.md +108 -0
- dpd_stream-0.1.0/dpd_stream/__init__.py +56 -0
- dpd_stream-0.1.0/dpd_stream/_absorb.py +288 -0
- dpd_stream-0.1.0/dpd_stream/_blocks.py +26 -0
- dpd_stream-0.1.0/dpd_stream/_frontend.py +447 -0
- dpd_stream-0.1.0/dpd_stream/_idtests.py +181 -0
- dpd_stream-0.1.0/dpd_stream/_nl.py +123 -0
- dpd_stream-0.1.0/dpd_stream/_passes.py +229 -0
- dpd_stream-0.1.0/dpd_stream/api.py +201 -0
- dpd_stream-0.1.0/dpd_stream/estimator.py +806 -0
- dpd_stream-0.1.0/dpd_stream/inference.py +292 -0
- dpd_stream-0.1.0/dpd_stream/kernels/__init__.py +37 -0
- dpd_stream-0.1.0/dpd_stream/kernels/cells.py +204 -0
- dpd_stream-0.1.0/dpd_stream/kernels/idtests.py +165 -0
- dpd_stream-0.1.0/dpd_stream/kernels/moments.py +697 -0
- dpd_stream-0.1.0/dpd_stream/kernels/nl.py +224 -0
- dpd_stream-0.1.0/dpd_stream/kernels/panel.py +302 -0
- dpd_stream-0.1.0/dpd_stream/layout.py +360 -0
- dpd_stream-0.1.0/dpd_stream/py.typed +0 -0
- dpd_stream-0.1.0/dpd_stream/results.py +485 -0
- dpd_stream-0.1.0/dpd_stream/simulate.py +145 -0
- dpd_stream-0.1.0/dpd_stream/store.py +213 -0
- dpd_stream-0.1.0/dpd_stream.egg-info/PKG-INFO +155 -0
- dpd_stream-0.1.0/dpd_stream.egg-info/SOURCES.txt +41 -0
- dpd_stream-0.1.0/dpd_stream.egg-info/dependency_links.txt +1 -0
- dpd_stream-0.1.0/dpd_stream.egg-info/requires.txt +22 -0
- dpd_stream-0.1.0/dpd_stream.egg-info/top_level.txt +1 -0
- dpd_stream-0.1.0/pyproject.toml +91 -0
- dpd_stream-0.1.0/setup.cfg +4 -0
- dpd_stream-0.1.0/tests/test_absorb.py +189 -0
- dpd_stream-0.1.0/tests/test_features.py +201 -0
- dpd_stream-0.1.0/tests/test_idtests.py +237 -0
- dpd_stream-0.1.0/tests/test_index_dtype.py +88 -0
- dpd_stream-0.1.0/tests/test_nl.py +176 -0
- dpd_stream-0.1.0/tests/test_oracle.py +210 -0
- dpd_stream-0.1.0/tests/test_published.py +140 -0
- dpd_stream-0.1.0/tests/test_pydynpd.py +47 -0
- dpd_stream-0.1.0/tests/test_stata.py +126 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
The first version.
|
|
6
|
+
|
|
7
|
+
- **Estimators.** Arellano–Bond difference GMM and Blundell–Bond system GMM;
|
|
8
|
+
one-step, two-step or iterated; first differences or forward orthogonal
|
|
9
|
+
deviations. Where conventions differ, the design follows Stata's xtabond2
|
|
10
|
+
(3.7.2), the reference for the instrument layout, the generalized inverse
|
|
11
|
+
(including `invsym()`'s treatment of negative pivots), the weights'
|
|
12
|
+
normalizations and the tests.
|
|
13
|
+
- **Instruments.** Lag limits, `collapse`, `eq`, `split`, `passthru`, backward
|
|
14
|
+
orthogonal deviations, IV-style `mz`, `h()` and `pca`, written in the
|
|
15
|
+
formula (`gmm()`, `iv()`) or passed as `Gmm` / `Iv` objects.
|
|
16
|
+
- **Out of core.** Polars reads the data once, from a Parquet file or glob or
|
|
17
|
+
a Lazy- or DataFrame, into blocks of whole panels. Three passes over the
|
|
18
|
+
blocks, in numba, give two-step estimates with Windmeijer's correction and
|
|
19
|
+
the Arellano–Bond tests; everything else comes from the sums. No instrument
|
|
20
|
+
matrix is ever built.
|
|
21
|
+
- **Inference.** Windmeijer's (2005) correction; one- and multi-way
|
|
22
|
+
clustering; `small`; aweights, pweights and fweights; the Arellano–Bond
|
|
23
|
+
AR(m), Sargan, Hansen and difference-in-Hansen tests; Andrews and Lu's
|
|
24
|
+
MMSC; delta-method long-run effects (`long_run`).
|
|
25
|
+
- **Identification tests.** Kleibergen–Paap rk LM and Wald and
|
|
26
|
+
Sanderson–Windmeijer tests (Windmeijer 2025), robust to any clustering,
|
|
27
|
+
with weights, PCA and absorbed effects; first-stage partial and Shea
|
|
28
|
+
partial R², the Anderson–Rubin test (and, with one endogenous regressor, its
|
|
29
|
+
confidence set) and Montiel Olea and Pflueger's effective F. They are
|
|
30
|
+
ivreg2's forms, not the LIML-based ones of Stata's `underid`, so the digits
|
|
31
|
+
differ (see the identification page). The algebra is hdfe-stream's
|
|
32
|
+
`core.ivtests`.
|
|
33
|
+
- **Ahn–Schmidt nonlinear moments** (`nl="noserial"` or `"iid"`), with
|
|
34
|
+
xtdpdgmm's first step, one-step fits and Windmeijer's correction in its
|
|
35
|
+
general form. The nonlinear fits' standard errors are the exact ones for the
|
|
36
|
+
moments and differ by a few percent from xtdpdgmm's (see the nonlinear
|
|
37
|
+
page).
|
|
38
|
+
- **Absorbed effects.** Time effects (`| year`) and, in difference GMM,
|
|
39
|
+
period-by-group effects (`| region^year`), with the estimates, variances and
|
|
40
|
+
tests of their dummies, under first differences or orthogonal deviations,
|
|
41
|
+
weights, any clustering and iterated GMM.
|
|
42
|
+
- **`DPDResult`.** A hdfe-stream `RegressionResult`: `summary()`, `tidy()`,
|
|
43
|
+
`confint()`, `wald_test()`, `etable`, `summary_json()`, `with_vcov()`, the
|
|
44
|
+
one-step results of a two-step fit (`onestep`), and lazy `predict()`,
|
|
45
|
+
`resid()` and `sample()` over the source.
|
|
46
|
+
- **`dpd_stream.simulate`.** `simulate_dynamic` and `truth_dynamic`: dynamic
|
|
47
|
+
panels with predetermined, endogenous or exogenous regressors, stationary or
|
|
48
|
+
not, unbalanced, with gaps, clusters and weights.
|
|
49
|
+
- **Tested against** a dense implementation of the algebra, Roodman's (2009)
|
|
50
|
+
published output, pydynpd 0.2.2 and plm 2.6.7's `pgmm` (on a simulated panel
|
|
51
|
+
shipped with the tests; Arellano and Bond's data are not distributed), and
|
|
52
|
+
Stata's xtabond2 and xtdpdgmm on about fifty specifications, whose bundle
|
|
53
|
+
is in `benchmarks/external/stata`.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software in your research, please cite it as below."
|
|
3
|
+
type: software
|
|
4
|
+
title: "dpd-stream"
|
|
5
|
+
abstract: "Out-of-core difference and system GMM for dynamic panels (Arellano-Bond, Blundell-Bond). One-step, two-step or iterated estimation with flexible instrument sets, Windmeijer-corrected and clustered inference, specification and identification tests, Ahn-Schmidt nonlinear moments and absorbed fixed effects, computed in streamed passes so that panel data far larger than memory can be used. Conventions follow Stata's xtabond2; results are tested against it, xtdpdgmm and other implementations."
|
|
6
|
+
authors:
|
|
7
|
+
- family-names: Tucker
|
|
8
|
+
given-names: Lee
|
|
9
|
+
email: lee.c.tucker@census.gov
|
|
10
|
+
version: 0.1.0
|
|
11
|
+
license: MIT
|
|
12
|
+
repository-code: "https://github.com/leetucker/dpd-stream"
|
|
13
|
+
url: "https://github.com/leetucker/dpd-stream"
|
|
14
|
+
keywords:
|
|
15
|
+
- econometrics
|
|
16
|
+
- dynamic panel data
|
|
17
|
+
- GMM
|
|
18
|
+
- Arellano-Bond
|
|
19
|
+
- Blundell-Bond
|
|
20
|
+
- out-of-core
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lee Tucker
|
|
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.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dpd-stream
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Out-of-core dynamic panel GMM: Arellano-Bond difference GMM and Blundell-Bond system GMM, by streamed passes
|
|
5
|
+
Author: Lee Tucker
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/leetucker/dpd-stream
|
|
8
|
+
Project-URL: Source, https://github.com/leetucker/dpd-stream
|
|
9
|
+
Project-URL: Issues, https://github.com/leetucker/dpd-stream/issues
|
|
10
|
+
Project-URL: Documentation, https://leetucker.github.io/dpd-stream/
|
|
11
|
+
Project-URL: Changelog, https://github.com/leetucker/dpd-stream/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: econometrics,dynamic panel,GMM,Arellano-Bond,Blundell-Bond,system GMM,xtabond2,out-of-core,panel data
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE.md
|
|
27
|
+
Requires-Dist: hdfe-stream[formula]>=0.6.2
|
|
28
|
+
Requires-Dist: polars!=1.41.*,>=1.37
|
|
29
|
+
Requires-Dist: numpy>=1.24
|
|
30
|
+
Requires-Dist: numba>=0.59
|
|
31
|
+
Requires-Dist: pyarrow>=14.0
|
|
32
|
+
Requires-Dist: scipy>=1.11
|
|
33
|
+
Requires-Dist: threadpoolctl>=3.1
|
|
34
|
+
Provides-Extra: benchmark
|
|
35
|
+
Requires-Dist: matplotlib>=3.7; extra == "benchmark"
|
|
36
|
+
Requires-Dist: pandas>=2.0; extra == "benchmark"
|
|
37
|
+
Provides-Extra: docs
|
|
38
|
+
Requires-Dist: sphinx>=8.0; extra == "docs"
|
|
39
|
+
Requires-Dist: myst-parser>=4.0; extra == "docs"
|
|
40
|
+
Requires-Dist: furo>=2024.8.6; extra == "docs"
|
|
41
|
+
Requires-Dist: sphinx-copybutton>=0.5; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-design>=0.6; extra == "docs"
|
|
43
|
+
Provides-Extra: test
|
|
44
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
45
|
+
Requires-Dist: pandas>=2.0; extra == "test"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
# dpd-stream
|
|
49
|
+
|
|
50
|
+
Arellano–Bond (difference) and Blundell–Bond (system) GMM for dynamic panels,
|
|
51
|
+
out of core. One-step, two-step or iterated estimation with flexible
|
|
52
|
+
instrument sets, Windmeijer-corrected and clustered inference, specification
|
|
53
|
+
and identification tests, Ahn–Schmidt nonlinear moments and absorbed fixed
|
|
54
|
+
effects. It works on data far larger than memory, and is much faster than the
|
|
55
|
+
existing implementations on large panels. Where conventions differ, it follows
|
|
56
|
+
Stata's xtabond2, and its results are tested against xtabond2, xtdpdgmm and
|
|
57
|
+
other implementations.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from dpd_stream import dpd_stream
|
|
61
|
+
|
|
62
|
+
fit = dpd_stream("n ~ l(n, 1) + w + k | year | gmm(n, 2:4) + gmm(w + k, 1:3)",
|
|
63
|
+
"firms/*.parquet", panel=("firm", "year"), system=True)
|
|
64
|
+
fit.summary()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## What it does
|
|
68
|
+
|
|
69
|
+
**Estimators**
|
|
70
|
+
- Difference or system GMM: one-step, two-step (Windmeijer-corrected) or
|
|
71
|
+
iterated.
|
|
72
|
+
- First differences or forward orthogonal deviations.
|
|
73
|
+
|
|
74
|
+
**Instruments**
|
|
75
|
+
- Lag limits, `collapse`, `eq`, `split`, `passthru`, `orthogonal`, `mz`,
|
|
76
|
+
`h()`, `pca`.
|
|
77
|
+
|
|
78
|
+
**Tests and inference**
|
|
79
|
+
- Arellano–Bond AR(m), Sargan, Hansen, difference-in-Hansen.
|
|
80
|
+
- Clustering, one- and multi-way.
|
|
81
|
+
- `small`-sample statistics.
|
|
82
|
+
- Weights.
|
|
83
|
+
|
|
84
|
+
**Also**
|
|
85
|
+
- Iterated GMM.
|
|
86
|
+
- MMSC model selection.
|
|
87
|
+
- Long-run effects.
|
|
88
|
+
- Kleibergen–Paap and Sanderson–Windmeijer identification tests (ivreg2's
|
|
89
|
+
forms; they differ in the digits from Stata's `underid`).
|
|
90
|
+
- Ahn–Schmidt nonlinear moments (xtdpdgmm's `nl()`).
|
|
91
|
+
- Absorbed time effects, and absorbed period-by-group effects in difference
|
|
92
|
+
GMM.
|
|
93
|
+
|
|
94
|
+
**Interface**
|
|
95
|
+
- pyfixest-style formulas with fixest's `l()`/`f()`/`d()` lag operators.
|
|
96
|
+
- Results with hdfe-stream's reporting: `summary()`, `tidy()`, `etable`,
|
|
97
|
+
`wald_test()`, `summary_json()`.
|
|
98
|
+
|
|
99
|
+
## Why it is fast
|
|
100
|
+
|
|
101
|
+
Everything linear GMM needs is a sum over panels: Z′X, Z′y, Σ Zᵢ′HZᵢ, and
|
|
102
|
+
Σ gᵢgᵢ′ with gᵢ = Zᵢ′eᵢ.
|
|
103
|
+
|
|
104
|
+
- **One read of the source.** The data are read once into blocks of whole
|
|
105
|
+
panels, sorted by time.
|
|
106
|
+
- **No instrument matrix.** Each pass is a numba kernel that builds every
|
|
107
|
+
panel's rows and its sparse instruments and adds them into those sums. No
|
|
108
|
+
instrument matrix is ever built.
|
|
109
|
+
- **Three passes in all** give two-step estimates with Windmeijer's
|
|
110
|
+
correction and the Arellano–Bond tests.
|
|
111
|
+
|
|
112
|
+
The other implementations instead hold every panel's dense instrument
|
|
113
|
+
matrix: xtabond2, xtdpdgmm, plm, pdynmc and pydynpd.
|
|
114
|
+
|
|
115
|
+
## Performance
|
|
116
|
+
|
|
117
|
+
Two-step GMM with Windmeijer-corrected standard errors on simulated panels
|
|
118
|
+
of 8 periods, on an EC2 spot instance (16 vCPUs, 32 GiB):
|
|
119
|
+
|
|
120
|
+
| Panels (rows) | | dpd-stream | pydynpd | plm `pgmm` | pdynmc |
|
|
121
|
+
|---|---|---|---|---|---|
|
|
122
|
+
| 100,000 (800,000) | difference GMM | 0.6 s, 1.3 GB | 15 s, 1.0 GB | 72 s, 5.2 GB | > 1,800 s |
|
|
123
|
+
| 1,000,000 (8 million) | difference GMM | 5.7 s, 1.6 GB | 158 s, 7.7 GB | out of memory | — |
|
|
124
|
+
| 1,000,000 (8 million) | system GMM | 5.3 s, 1.6 GB | 159 s, 8.2 GB | 799 s, 26 GB | — |
|
|
125
|
+
| 10,000,000 (80 million) | system GMM | 80 s, 9.7 GB | out of memory | out of memory | — |
|
|
126
|
+
|
|
127
|
+
Estimates agree to about 1e-13 (system GMM: with pydynpd; plm and pdynmc enter
|
|
128
|
+
the year effects differently). With uncollapsed instruments over 20 periods
|
|
129
|
+
(5,000 panels), dpd-stream takes 0.24 s; pydynpd, plm and pdynmc take 24, 47
|
|
130
|
+
and 441 s, and at 30 periods plm and pdynmc run out of memory. systemgmmkit,
|
|
131
|
+
which builds collapsed instruments only, takes 500 s at 100,000 panels. See the
|
|
132
|
+
benchmarks in the documentation.
|
|
133
|
+
|
|
134
|
+
## Install
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
pip install dpd-stream
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Documentation
|
|
141
|
+
|
|
142
|
+
The documentation is at https://leetucker.github.io/dpd-stream/. Build it
|
|
143
|
+
locally with:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install -e ".[docs]"
|
|
147
|
+
sphinx-build -W --keep-going -b html docs docs/_build/html
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`examples/` has runnable scripts, and `benchmarks/` the performance
|
|
151
|
+
comparisons (see `benchmarks/README.md`).
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
MIT.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# dpd-stream
|
|
2
|
+
|
|
3
|
+
Arellano–Bond (difference) and Blundell–Bond (system) GMM for dynamic panels,
|
|
4
|
+
out of core. One-step, two-step or iterated estimation with flexible
|
|
5
|
+
instrument sets, Windmeijer-corrected and clustered inference, specification
|
|
6
|
+
and identification tests, Ahn–Schmidt nonlinear moments and absorbed fixed
|
|
7
|
+
effects. It works on data far larger than memory, and is much faster than the
|
|
8
|
+
existing implementations on large panels. Where conventions differ, it follows
|
|
9
|
+
Stata's xtabond2, and its results are tested against xtabond2, xtdpdgmm and
|
|
10
|
+
other implementations.
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from dpd_stream import dpd_stream
|
|
14
|
+
|
|
15
|
+
fit = dpd_stream("n ~ l(n, 1) + w + k | year | gmm(n, 2:4) + gmm(w + k, 1:3)",
|
|
16
|
+
"firms/*.parquet", panel=("firm", "year"), system=True)
|
|
17
|
+
fit.summary()
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## What it does
|
|
21
|
+
|
|
22
|
+
**Estimators**
|
|
23
|
+
- Difference or system GMM: one-step, two-step (Windmeijer-corrected) or
|
|
24
|
+
iterated.
|
|
25
|
+
- First differences or forward orthogonal deviations.
|
|
26
|
+
|
|
27
|
+
**Instruments**
|
|
28
|
+
- Lag limits, `collapse`, `eq`, `split`, `passthru`, `orthogonal`, `mz`,
|
|
29
|
+
`h()`, `pca`.
|
|
30
|
+
|
|
31
|
+
**Tests and inference**
|
|
32
|
+
- Arellano–Bond AR(m), Sargan, Hansen, difference-in-Hansen.
|
|
33
|
+
- Clustering, one- and multi-way.
|
|
34
|
+
- `small`-sample statistics.
|
|
35
|
+
- Weights.
|
|
36
|
+
|
|
37
|
+
**Also**
|
|
38
|
+
- Iterated GMM.
|
|
39
|
+
- MMSC model selection.
|
|
40
|
+
- Long-run effects.
|
|
41
|
+
- Kleibergen–Paap and Sanderson–Windmeijer identification tests (ivreg2's
|
|
42
|
+
forms; they differ in the digits from Stata's `underid`).
|
|
43
|
+
- Ahn–Schmidt nonlinear moments (xtdpdgmm's `nl()`).
|
|
44
|
+
- Absorbed time effects, and absorbed period-by-group effects in difference
|
|
45
|
+
GMM.
|
|
46
|
+
|
|
47
|
+
**Interface**
|
|
48
|
+
- pyfixest-style formulas with fixest's `l()`/`f()`/`d()` lag operators.
|
|
49
|
+
- Results with hdfe-stream's reporting: `summary()`, `tidy()`, `etable`,
|
|
50
|
+
`wald_test()`, `summary_json()`.
|
|
51
|
+
|
|
52
|
+
## Why it is fast
|
|
53
|
+
|
|
54
|
+
Everything linear GMM needs is a sum over panels: Z′X, Z′y, Σ Zᵢ′HZᵢ, and
|
|
55
|
+
Σ gᵢgᵢ′ with gᵢ = Zᵢ′eᵢ.
|
|
56
|
+
|
|
57
|
+
- **One read of the source.** The data are read once into blocks of whole
|
|
58
|
+
panels, sorted by time.
|
|
59
|
+
- **No instrument matrix.** Each pass is a numba kernel that builds every
|
|
60
|
+
panel's rows and its sparse instruments and adds them into those sums. No
|
|
61
|
+
instrument matrix is ever built.
|
|
62
|
+
- **Three passes in all** give two-step estimates with Windmeijer's
|
|
63
|
+
correction and the Arellano–Bond tests.
|
|
64
|
+
|
|
65
|
+
The other implementations instead hold every panel's dense instrument
|
|
66
|
+
matrix: xtabond2, xtdpdgmm, plm, pdynmc and pydynpd.
|
|
67
|
+
|
|
68
|
+
## Performance
|
|
69
|
+
|
|
70
|
+
Two-step GMM with Windmeijer-corrected standard errors on simulated panels
|
|
71
|
+
of 8 periods, on an EC2 spot instance (16 vCPUs, 32 GiB):
|
|
72
|
+
|
|
73
|
+
| Panels (rows) | | dpd-stream | pydynpd | plm `pgmm` | pdynmc |
|
|
74
|
+
|---|---|---|---|---|---|
|
|
75
|
+
| 100,000 (800,000) | difference GMM | 0.6 s, 1.3 GB | 15 s, 1.0 GB | 72 s, 5.2 GB | > 1,800 s |
|
|
76
|
+
| 1,000,000 (8 million) | difference GMM | 5.7 s, 1.6 GB | 158 s, 7.7 GB | out of memory | — |
|
|
77
|
+
| 1,000,000 (8 million) | system GMM | 5.3 s, 1.6 GB | 159 s, 8.2 GB | 799 s, 26 GB | — |
|
|
78
|
+
| 10,000,000 (80 million) | system GMM | 80 s, 9.7 GB | out of memory | out of memory | — |
|
|
79
|
+
|
|
80
|
+
Estimates agree to about 1e-13 (system GMM: with pydynpd; plm and pdynmc enter
|
|
81
|
+
the year effects differently). With uncollapsed instruments over 20 periods
|
|
82
|
+
(5,000 panels), dpd-stream takes 0.24 s; pydynpd, plm and pdynmc take 24, 47
|
|
83
|
+
and 441 s, and at 30 periods plm and pdynmc run out of memory. systemgmmkit,
|
|
84
|
+
which builds collapsed instruments only, takes 500 s at 100,000 panels. See the
|
|
85
|
+
benchmarks in the documentation.
|
|
86
|
+
|
|
87
|
+
## Install
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install dpd-stream
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
The documentation is at https://leetucker.github.io/dpd-stream/. Build it
|
|
96
|
+
locally with:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install -e ".[docs]"
|
|
100
|
+
sphinx-build -W --keep-going -b html docs docs/_build/html
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`examples/` has runnable scripts, and `benchmarks/` the performance
|
|
104
|
+
comparisons (see `benchmarks/README.md`).
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""dpd-stream: Arellano-Bond (difference) and Blundell-Bond (system) GMM
|
|
2
|
+
for dynamic panels, out of core. Names and conventions follow Stata's
|
|
3
|
+
xtabond2 where they overlap.
|
|
4
|
+
|
|
5
|
+
The data are read once (pass 0) into blocks of whole panels sorted by
|
|
6
|
+
time; every later pass reads the blocks, builds each panel's rows and
|
|
7
|
+
instruments in a numba kernel, and accumulates the few matrices the
|
|
8
|
+
estimator needs (j x j and j x K, j the number of instruments). Nothing
|
|
9
|
+
the size of the data is held in memory, and no instrument matrix is
|
|
10
|
+
built.
|
|
11
|
+
|
|
12
|
+
Layout
|
|
13
|
+
api.py dpd_stream()
|
|
14
|
+
_frontend.py formula and instrument parsing; Gmm, Iv
|
|
15
|
+
layout.py the instrument matrix's columns (xtabond2's order)
|
|
16
|
+
store.py pass 0: the source -> panel-sorted blocks
|
|
17
|
+
kernels/ numba: a panel's rows and instruments; the passes
|
|
18
|
+
estimator.py the fit: the passes and the estimates
|
|
19
|
+
_passes.py, _absorb.py, _idtests.py, _nl.py
|
|
20
|
+
parts of the fit (mixins of estimator.DPD): the residual
|
|
21
|
+
and Windmeijer passes; absorbed effects; identification
|
|
22
|
+
tests; Ahn-Schmidt moments
|
|
23
|
+
inference.py the algebra after the passes: generalized inverse,
|
|
24
|
+
Windmeijer, Arellano-Bond, Sargan/Hansen, MMSC
|
|
25
|
+
results.py DPDResult, DPDMulti
|
|
26
|
+
simulate.py simulate_dynamic(), truth_dynamic()
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from importlib.metadata import PackageNotFoundError
|
|
30
|
+
from importlib.metadata import version as _version
|
|
31
|
+
|
|
32
|
+
from hdfe_stream.core.multcomp import bonferroni, holm
|
|
33
|
+
from hdfe_stream.core.reporting import etable
|
|
34
|
+
from hdfe_stream.core.workspace import cleanup
|
|
35
|
+
|
|
36
|
+
from ._frontend import Gmm, Iv
|
|
37
|
+
from .api import dpd_stream
|
|
38
|
+
from .results import DPDMulti, DPDResult
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
__version__ = _version("dpd-stream")
|
|
42
|
+
except PackageNotFoundError: # pragma: no cover
|
|
43
|
+
__version__ = "0.1.0"
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"DPDMulti",
|
|
47
|
+
"DPDResult",
|
|
48
|
+
"Gmm",
|
|
49
|
+
"Iv",
|
|
50
|
+
"__version__",
|
|
51
|
+
"bonferroni",
|
|
52
|
+
"cleanup",
|
|
53
|
+
"dpd_stream",
|
|
54
|
+
"etable",
|
|
55
|
+
"holm",
|
|
56
|
+
]
|