systemgmmkit 0.5.8.dev0__tar.gz → 0.5.10__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.
Files changed (73) hide show
  1. systemgmmkit-0.5.10/PKG-INFO +2165 -0
  2. systemgmmkit-0.5.10/README.md +2122 -0
  3. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/pyproject.toml +4 -2
  4. systemgmmkit-0.5.10/src/systemgmmkit/__init__.py +236 -0
  5. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/diagnostics/__init__.py +6 -2
  6. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/diagnostics/gmm.py +3 -1
  7. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/diagnostics/panel.py +3 -1
  8. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/diagnostics.py +0 -2
  9. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/dynamic_panel.py +74 -18
  10. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/estimators/first_difference.py +2 -2
  11. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/fixed_effects.py +1 -3
  12. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/linear.py +5 -7
  13. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/native_gmm.py +1021 -791
  14. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/parity.py +4 -7
  15. systemgmmkit-0.5.10/src/systemgmmkit/postestimation/__init__.py +530 -0
  16. systemgmmkit-0.5.10/src/systemgmmkit/postestimation/plots.py +1363 -0
  17. systemgmmkit-0.5.10/src/systemgmmkit/postestimation/result_plot.py +668 -0
  18. systemgmmkit-0.5.10/src/systemgmmkit/postestimation/sgm_viz.py +228 -0
  19. systemgmmkit-0.5.10/src/systemgmmkit/postestimation/sgm_viz_v2.py +1327 -0
  20. systemgmmkit-0.5.10/src/systemgmmkit/postestimation/standard_gallery.py +604 -0
  21. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/postestimation.py +7 -10
  22. systemgmmkit-0.5.10/src/systemgmmkit/presets.py +510 -0
  23. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/pydynpd_output_parser.py +2 -6
  24. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/reporting/parity.py +9 -2
  25. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/reporting.py +0 -1
  26. systemgmmkit-0.5.10/src/systemgmmkit.egg-info/PKG-INFO +2165 -0
  27. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit.egg-info/SOURCES.txt +11 -0
  28. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit.egg-info/requires.txt +1 -0
  29. systemgmmkit-0.5.10/tests/test_gmm_lag_windows.py +203 -0
  30. systemgmmkit-0.5.10/tests/test_postestimation_graphics.py +202 -0
  31. systemgmmkit-0.5.10/tests/test_result_plot_accessor.py +119 -0
  32. systemgmmkit-0.5.10/tests/test_sgm_viz_v2.py +129 -0
  33. systemgmmkit-0.5.10/tests/test_standard_gallery.py +175 -0
  34. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_xtabond2_system_gmm_parity.py +20 -11
  35. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_xtabond2_system_gmm_uncorrected_se_baseline.py +3 -8
  36. systemgmmkit-0.5.8.dev0/PKG-INFO +0 -624
  37. systemgmmkit-0.5.8.dev0/README.md +0 -582
  38. systemgmmkit-0.5.8.dev0/src/systemgmmkit/__init__.py +0 -109
  39. systemgmmkit-0.5.8.dev0/src/systemgmmkit/presets.py +0 -183
  40. systemgmmkit-0.5.8.dev0/src/systemgmmkit.egg-info/PKG-INFO +0 -624
  41. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/LICENSE +0 -0
  42. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/setup.cfg +0 -0
  43. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/cli.py +0 -0
  44. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/gmm_parity_policy.py +0 -0
  45. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/panel_iv.py +0 -0
  46. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/pydynpd_backend.py +0 -0
  47. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/random_effects.py +0 -0
  48. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/reporting/__init__.py +0 -0
  49. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/spec.py +0 -0
  50. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/suite.py +0 -0
  51. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/tables.py +0 -0
  52. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit/validation.py +0 -0
  53. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit.egg-info/dependency_links.txt +0 -0
  54. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit.egg-info/entry_points.txt +0 -0
  55. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/src/systemgmmkit.egg-info/top_level.txt +0 -0
  56. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_apply_gmm_parity_policy_script.py +0 -0
  57. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_first_difference_reporting.py +0 -0
  58. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_fixed_effects.py +0 -0
  59. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_generic_presets.py +0 -0
  60. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_gmm_parity_policy.py +0 -0
  61. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_ols_postestimation.py +0 -0
  62. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_parity_native_gmm.py +0 -0
  63. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_pydynpd_backend.py +0 -0
  64. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_pydynpd_output_parser.py +0 -0
  65. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_random_effects_iv_tables.py +0 -0
  66. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_spec_command.py +0 -0
  67. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_validation_diagnostics.py +0 -0
  68. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_xtabond2_system_gmm_decomposition_controls_certification.py +0 -0
  69. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_xtabond2_system_gmm_decomposition_controls_scaffold.py +0 -0
  70. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_xtabond2_system_gmm_no_controls_certification.py +0 -0
  71. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_xtabond2_system_gmm_no_controls_scaffold.py +0 -0
  72. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_xtabond2_system_gmm_three_way_controls_certification.py +0 -0
  73. {systemgmmkit-0.5.8.dev0 → systemgmmkit-0.5.10}/tests/test_xtabond2_system_gmm_three_way_controls_scaffold.py +0 -0
@@ -0,0 +1,2165 @@
1
+ Metadata-Version: 2.4
2
+ Name: systemgmmkit
3
+ Version: 0.5.10
4
+ Summary: Generic panel-data econometrics workflow helpers for FE, RE, IV/2SLS, and Difference/System GMM in Python.
5
+ Author: Oluwajuwon Mayomi Akanbi
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Akanom/systemgmmkit
8
+ Project-URL: Repository, https://github.com/Akanom/systemgmmkit
9
+ Project-URL: Issues, https://github.com/Akanom/systemgmmkit/issues
10
+ Keywords: econometrics,panel-data,fixed-effects,random-effects,panel-iv,2sls,system-gmm,difference-gmm,dynamic-panel,arellano-bond,blundell-bond,stata,xtabond2
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: matplotlib>=3.8
23
+ Requires-Dist: numpy>=1.23
24
+ Requires-Dist: pandas>=1.5
25
+ Requires-Dist: scipy>=1.9
26
+ Provides-Extra: pydynpd
27
+ Requires-Dist: pydynpd>=0.2.2; extra == "pydynpd"
28
+ Provides-Extra: fe
29
+ Requires-Dist: linearmodels>=5; extra == "fe"
30
+ Provides-Extra: reports
31
+ Requires-Dist: tabulate>=0.9; extra == "reports"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=8; extra == "dev"
34
+ Requires-Dist: ruff>=0.6; extra == "dev"
35
+ Requires-Dist: mypy>=1.10; extra == "dev"
36
+ Requires-Dist: build>=1.2; extra == "dev"
37
+ Requires-Dist: twine>=5; extra == "dev"
38
+ Provides-Extra: all
39
+ Requires-Dist: pydynpd>=0.2.2; extra == "all"
40
+ Requires-Dist: linearmodels>=5; extra == "all"
41
+ Requires-Dist: tabulate>=0.9; extra == "all"
42
+ Dynamic: license-file
43
+
44
+ # systemgmmkit
45
+
46
+ [![PyPI version](https://img.shields.io/pypi/v/systemgmmkit.svg)](https://pypi.org/project/systemgmmkit/)
47
+ [![Python versions](https://img.shields.io/pypi/pyversions/systemgmmkit.svg)](https://pypi.org/project/systemgmmkit/)
48
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
49
+ [![CI](https://github.com/Akanom/systemgmmkit/actions/workflows/ci.yml/badge.svg)](https://github.com/Akanom/systemgmmkit/actions/workflows/ci.yml)
50
+ [![Publish](https://github.com/Akanom/systemgmmkit/actions/workflows/publish.yml/badge.svg)](https://github.com/Akanom/systemgmmkit/actions/workflows/publish.yml)
51
+
52
+ ---
53
+
54
+ # systemgmmkit 0.5.10
55
+
56
+ `systemgmmkit` is a Python package for applied panel-data econometrics. It provides a unified workflow for baseline linear models, panel estimators, instrumental-variable models, dynamic-panel GMM estimation, diagnostics, post-estimation, visualization, and reproducible reporting.
57
+
58
+ The package currently supports:
59
+
60
+ * Ordinary Least Squares;
61
+ * Pooled OLS;
62
+ * Fixed Effects;
63
+ * Random Effects;
64
+ * Panel IV / 2SLS;
65
+ * Difference GMM;
66
+ * System GMM;
67
+ * Windmeijer-corrected two-step dynamic GMM;
68
+ * Hansen, Sargan, AR(1), and AR(2) diagnostics;
69
+ * post-estimation utilities;
70
+ * standard post-estimation graphics;
71
+ * SGM-Viz diagnostic dashboards;
72
+ * result-level plotting accessors;
73
+ * HTML / PNG / SVG / PDF-compatible reporting workflows;
74
+ * integration with `universal-output-hub`.
75
+
76
+ `systemgmmkit` is designed for empirical researchers working in economics, finance, management, operations, productivity analysis, public policy, development economics, political economy, industrial organization, and other panel-data settings.
77
+
78
+ ---
79
+
80
+ # Why systemgmmkit?
81
+
82
+ Applied panel-data research often requires several disconnected tools:
83
+
84
+ * baseline OLS models;
85
+ * pooled panel regressions;
86
+ * fixed-effects and random-effects models;
87
+ * instrumental-variable models;
88
+ * dynamic-panel GMM models;
89
+ * diagnostic tests;
90
+ * post-estimation analysis;
91
+ * publication tables;
92
+ * diagnostic figures;
93
+ * reproducible exports.
94
+
95
+ `systemgmmkit` aims to bring these pieces into a consistent Python API.
96
+
97
+ The package is built around five principles:
98
+
99
+ 1. **Explicit model specification**
100
+ Model assumptions should be visible in the code, not hidden inside estimation defaults.
101
+
102
+ 2. **Reproducible empirical workflows**
103
+ Estimation, diagnostics, post-estimation, visualization, and reporting should be easy to rerun and inspect.
104
+
105
+ 3. **Transparent dynamic-panel diagnostics**
106
+ Dynamic-panel GMM results should expose sample size, instrument count, AR tests, Hansen/Sargan tests, covariance type, backend metadata, and instrument architecture.
107
+
108
+ 4. **Verification against reference implementations**
109
+ Estimators are benchmarked against established Stata implementations where practical.
110
+
111
+ 5. **Publication-oriented communication**
112
+ Results should be interpretable not only as coefficient tables, but also through diagnostics, model-health dashboards, persistence plots, instrument-architecture displays, and report-ready graphics.
113
+
114
+ The objective is not only to estimate models. The objective is to make modelling choices clear enough for replication, review, publication, and applied decision-making.
115
+
116
+ ---
117
+
118
+ # What's New in 0.5.10
119
+
120
+ Version `0.5.10` is a post-estimation, graphics, and diagnostic-reporting release.
121
+
122
+ It builds on the `0.5.9` estimator and post-estimation foundation by adding a complete visualization layer for applied panel-data workflows.
123
+
124
+ ## SGM-Viz v2 diagnostic dashboards
125
+
126
+ New flagship visualization APIs include:
127
+
128
+ * `HealthMetrics`
129
+ * `InstrumentArchitecture`
130
+ * `PersistenceAnalytics`
131
+ * `model_health_dashboard_v2()`
132
+ * `dynamic_persistence_dashboard_v2()`
133
+ * `instrument_architecture_dashboard_v2()`
134
+ * `effect_surface_dashboard_v2()`
135
+ * `publication_panel_v2()`
136
+ * `export_sgm_viz_v2_gallery()`
137
+ * `export_sgm_viz_report()`
138
+ * `model_comparison_dashboard_v2()`
139
+
140
+ These plots are designed specifically for dynamic-panel workflows. They are not generic chart wrappers.
141
+
142
+ ## Result-level plotting API
143
+
144
+ Version `0.5.10` adds result-level plotting accessors:
145
+
146
+ ```python
147
+ result.plot.health()
148
+ result.plot.persistence()
149
+ result.plot.instruments()
150
+ result.plot.publication_panel()
151
+ result.plot.standard_gallery()
152
+ result.plot.export_all()
153
+ ```
154
+
155
+ For result objects that do not support direct `.plot` attachment, use:
156
+
157
+ ```python
158
+ from systemgmmkit.postestimation import plot_accessor
159
+
160
+ viz = plot_accessor(result)
161
+
162
+ viz.health()
163
+ viz.persistence()
164
+ viz.instruments()
165
+ viz.publication_panel()
166
+ viz.standard_gallery("outputs/standard_gallery")
167
+ ```
168
+
169
+ ## Standard post-estimation graphics gallery
170
+
171
+ Version `0.5.10` also adds a standard R/Stata-style post-estimation graphics gallery:
172
+
173
+ * coefficient / parameter impact plot;
174
+ * marginal effects plot;
175
+ * margins / prediction plot;
176
+ * conditional effects plot;
177
+ * interaction plot;
178
+ * residuals vs fitted plot;
179
+ * QQ plot of residuals;
180
+ * residual histogram / density profile;
181
+ * panel trajectory plot;
182
+ * fixed-effects / unit-effects plot;
183
+ * instrument count plot;
184
+ * Hansen / Sargan / AR diagnostics plot;
185
+ * counterfactual scenario plot;
186
+ * 3D / effect-surface plot.
187
+
188
+ The full gallery can be exported in one command:
189
+
190
+ ```python
191
+ result.plot.standard_gallery(
192
+ "outputs/standard_gallery",
193
+ prefix="model",
194
+ )
195
+ ```
196
+
197
+ or:
198
+
199
+ ```python
200
+ from systemgmmkit.postestimation import export_standard_postestimation_gallery
201
+
202
+ gallery = export_standard_postestimation_gallery(
203
+ result,
204
+ output_dir="outputs/standard_gallery",
205
+ prefix="model",
206
+ )
207
+ ```
208
+
209
+ ## Report modes
210
+
211
+ SGM-Viz HTML reports support three report modes:
212
+
213
+ ```text
214
+ dashboard = individual dashboards only
215
+ publication = composed publication panel only
216
+ full = all figures, including repeated components
217
+ ```
218
+
219
+ Example:
220
+
221
+ ```python
222
+ result.plot.export_all(
223
+ "outputs/sgm_report",
224
+ gallery_mode="dashboard",
225
+ )
226
+ ```
227
+
228
+ For a paper-style one-page summary figure:
229
+
230
+ ```python
231
+ result.plot.export_all(
232
+ "outputs/paper_report",
233
+ gallery_mode="publication",
234
+ )
235
+ ```
236
+
237
+ ## Single-plot usage
238
+
239
+ Users can generate one figure directly without creating a full gallery:
240
+
241
+ ```python
242
+ result.plot.health(save="outputs/health.png")
243
+ result.plot.persistence(save="outputs/persistence.png")
244
+ result.plot.instruments(save="outputs/instruments.png")
245
+ result.plot.publication_panel(save="outputs/publication_panel.png")
246
+ ```
247
+
248
+ Standard plots are also directly callable:
249
+
250
+ ```python
251
+ from systemgmmkit.postestimation import coefficient_plot
252
+
253
+ coefficient_plot(
254
+ result,
255
+ save="outputs/coefficient_plot.png",
256
+ )
257
+ ```
258
+
259
+ ---
260
+
261
+ # Core Estimator Coverage
262
+
263
+ ## Linear Models
264
+
265
+ * Ordinary Least Squares
266
+ * Robust OLS
267
+ * Pooled OLS
268
+ * Clustered OLS
269
+
270
+ ## Panel Models
271
+
272
+ * One-way Fixed Effects
273
+ * Two-way Fixed Effects
274
+ * Random Effects
275
+ * Panel IV / 2SLS
276
+
277
+ ## Dynamic Panel Models
278
+
279
+ * Difference GMM
280
+ * System GMM
281
+ * One-step estimation
282
+ * Two-step estimation
283
+ * Windmeijer-corrected standard errors
284
+ * Collapsed instruments
285
+ * Restricted GMM lag windows
286
+ * Hansen diagnostics
287
+ * Sargan diagnostics
288
+ * Arellano-Bond AR(1) diagnostics
289
+ * Arellano-Bond AR(2) diagnostics
290
+
291
+ ## Post-Estimation
292
+
293
+ * Predictions
294
+ * Fitted values
295
+ * Residuals
296
+ * Variance-covariance extraction
297
+ * Confidence intervals
298
+ * Linear combinations
299
+ * Wald tests
300
+ * Marginal effects for linear estimators
301
+
302
+ ## Graphics and Diagnostics
303
+
304
+ * Coefficient plots
305
+ * Marginal effects plots
306
+ * Prediction / margins plots
307
+ * Interaction plots
308
+ * Conditional effects plots
309
+ * Residual diagnostics
310
+ * Fixed-effects plots
311
+ * Panel trajectory plots
312
+ * Instrument count plots
313
+ * Hansen / AR diagnostic plots
314
+ * Counterfactual scenario plots
315
+ * 3D effect surfaces
316
+ * SGM-Viz model-health dashboards
317
+ * SGM-Viz persistence analytics
318
+ * SGM-Viz instrument architecture dashboards
319
+ * SGM-Viz publication panels
320
+ * HTML gallery export
321
+ * PNG / SVG / PDF-compatible figure export
322
+
323
+ ## Reporting
324
+
325
+ * Markdown export
326
+ * CSV export
327
+ * LaTeX export
328
+ * HTML figure galleries
329
+ * Structured result objects
330
+ * Integration with `universal-output-hub`
331
+
332
+ ---
333
+
334
+ # Installation
335
+
336
+ Stable release:
337
+
338
+ ```bash
339
+ pip install systemgmmkit
340
+ ```
341
+
342
+ Development version:
343
+
344
+ ```bash
345
+ pip install git+https://github.com/Akanom/systemgmmkit.git
346
+ ```
347
+
348
+ Local development installation:
349
+
350
+ ```bash
351
+ pip install -e ".[dev,all]"
352
+ ```
353
+
354
+ For graphics support, ensure `matplotlib` is available:
355
+
356
+ ```bash
357
+ pip install matplotlib
358
+ ```
359
+
360
+ Check the installed version:
361
+
362
+ ```python
363
+ import systemgmmkit
364
+
365
+ print(systemgmmkit.__version__)
366
+ ```
367
+
368
+ ---
369
+
370
+ # Verification Philosophy
371
+
372
+ Verification is a core design principle of `systemgmmkit`.
373
+
374
+ Where practical, estimators are benchmarked against established Stata implementations, including:
375
+
376
+ * `regress`;
377
+ * `xtreg`;
378
+ * `ivregress`;
379
+ * `xtabond2`;
380
+ * `xtdpdgmm`.
381
+
382
+ Benchmark scripts, comparison workflows, and validation artifacts are maintained in the repository.
383
+
384
+ The goal is not merely to produce estimates. The goal is to provide transparent evidence that estimates match trusted reference implementations under maintained benchmark specifications.
385
+
386
+ ---
387
+
388
+ # Current Validation Status
389
+
390
+ The major estimation paths currently exposed through the public API have either been directly benchmarked against Stata reference implementations or have dedicated comparison workflows maintained in the repository.
391
+
392
+ | Component | Status |
393
+ | -------------------------- | --------------------- |
394
+ | OLS | PASS_STATA_PARITY |
395
+ | Robust OLS | PASS_STATA_PARITY |
396
+ | Clustered OLS | PASS_STATA_PARITY |
397
+ | Confidence intervals | PASS_STATA_PARITY |
398
+ | `lincom` | PASS_STATA_PARITY |
399
+ | Wald / F tests | PASS_STATA_PARITY |
400
+ | Fixed Effects | PASS_STATA_COMPARISON |
401
+ | Random Effects | PASS_STATA_COMPARISON |
402
+ | Panel IV / 2SLS | PASS_STATA_COMPARISON |
403
+ | Difference GMM | PASS_XTABOND2_PARITY |
404
+ | System GMM | PASS_XTABOND2_PARITY |
405
+ | Windmeijer standard errors | PASS_XTABOND2_PARITY |
406
+ | Hansen diagnostics | PASS_XTABOND2_PARITY |
407
+ | Sargan diagnostics | PASS_XTABOND2_PARITY |
408
+ | AR(1) diagnostics | PASS_XTABOND2_PARITY |
409
+ | AR(2) diagnostics | PASS_XTABOND2_PARITY |
410
+ | SGM-Viz v2 dashboards | PASS_TESTED_EXPORT |
411
+ | Standard graphics gallery | PASS_TESTED_EXPORT |
412
+ | Result plot accessors | PASS_TESTED_EXPORT |
413
+
414
+ Validation claims apply to the maintained benchmark specifications and validation workflows in the repository. The controlled `xtabond2` benchmark is used for strict certification. The CMAPSS FD001 application is used as an external validation case. Users should still inspect their own model diagnostics, instrument counts, sample construction, lag-window choices, and identification assumptions.
415
+
416
+ ---
417
+
418
+ # System GMM xtabond2 Certification
419
+
420
+ The native System GMM implementation has been certified against Stata `xtabond2` on a maintained collapsed two-step benchmark specification.
421
+
422
+ Certified components include:
423
+
424
+ * coefficient estimates;
425
+ * Windmeijer-corrected two-step standard errors;
426
+ * sample size;
427
+ * instrument count;
428
+ * Hansen overidentification diagnostic;
429
+ * Sargan overidentification diagnostic;
430
+ * Arellano-Bond AR(1) diagnostic;
431
+ * Arellano-Bond AR(2) diagnostic.
432
+
433
+ The maintained benchmark uses a controlled dynamic-panel specification with:
434
+
435
+ * collapsed instruments;
436
+ * restricted GMM lag windows;
437
+ * two-step robust estimation;
438
+ * Windmeijer correction;
439
+ * strict numerical comparison against `xtabond2`.
440
+
441
+ Under this maintained benchmark, the native implementation reproduces the `xtabond2` reference results within declared strict numerical tolerance.
442
+
443
+ ---
444
+
445
+ # External CMAPSS FD001 Validation
446
+
447
+ In addition to the controlled benchmark, System GMM was externally validated on CMAPSS FD001 publication-style panel specifications.
448
+
449
+ Two validation models were used.
450
+
451
+ Risk model:
452
+
453
+ ```text
454
+ risk ~ L1.risk + degradation_index + sensor_mean_z + pc2 + op_setting1 + op_setting2
455
+ ```
456
+
457
+ Degradation model:
458
+
459
+ ```text
460
+ degradation_index ~ L1.degradation_index + sensor_mean_z + pc2 + pc3 + op_setting1 + op_setting2
461
+ ```
462
+
463
+ Across both FD001 validation models, `systemgmmkit` reproduces `xtabond2` results for:
464
+
465
+ * coefficient estimates;
466
+ * Windmeijer-corrected standard errors;
467
+ * sample size;
468
+ * instrument count;
469
+ * Hansen diagnostics;
470
+ * Sargan diagnostics;
471
+ * AR(1) and AR(2) diagnostics within declared external-validation tolerance.
472
+
473
+ The FD001 validation is used as an independent application check. The controlled `xtabond2` benchmark remains the strict certification benchmark.
474
+
475
+ ---
476
+
477
+ # Verified OLS Benchmark
478
+
479
+ The OLS and pooled OLS implementations have been verified against Stata using a real FD001 panel-data benchmark.
480
+
481
+ Benchmark model:
482
+
483
+ ```text
484
+ risk ~ degradation_index + sensor_mean_z + pc2 + op_setting1 + op_setting2
485
+ ```
486
+
487
+ Panel structure:
488
+
489
+ ```text
490
+ entity = unit
491
+ time = cycle
492
+ ```
493
+
494
+ Observed agreement:
495
+
496
+ | Metric | Result |
497
+ | --------------------------------- | -------: |
498
+ | Maximum coefficient difference | 4.64e-14 |
499
+ | Maximum standard-error difference | 2.04e-14 |
500
+
501
+ These differences represent machine-precision agreement with Stata under the maintained benchmark specification.
502
+
503
+ ---
504
+
505
+ # Quick Start
506
+
507
+ ## Ordinary Least Squares
508
+
509
+ ```python
510
+ from systemgmmkit import OLSSpec, run_ols
511
+
512
+ spec = OLSSpec(
513
+ dependent="y",
514
+ regressors=["x1", "x2"],
515
+ controls=["z1", "z2"],
516
+ covariance="robust",
517
+ )
518
+
519
+ result = run_ols(spec, df)
520
+
521
+ print(result.summary_frame())
522
+ ```
523
+
524
+ Equivalent Stata idea:
525
+
526
+ ```stata
527
+ regress y x1 x2 z1 z2, vce(robust)
528
+ ```
529
+
530
+ ---
531
+
532
+ ## Pooled OLS
533
+
534
+ ```python
535
+ from systemgmmkit import PooledOLSSpec, run_pooled_ols
536
+
537
+ spec = PooledOLSSpec(
538
+ dependent="y",
539
+ regressors=["x1", "x2"],
540
+ controls=["z1"],
541
+ covariance="clustered",
542
+ )
543
+
544
+ result = run_pooled_ols(
545
+ spec,
546
+ df,
547
+ entity="firm_id",
548
+ time="year",
549
+ )
550
+
551
+ print(result.summary_frame())
552
+ ```
553
+
554
+ Equivalent Stata idea:
555
+
556
+ ```stata
557
+ regress y x1 x2 z1, vce(cluster firm_id)
558
+ ```
559
+
560
+ ---
561
+
562
+ ## Fixed Effects
563
+
564
+ ```python
565
+ from systemgmmkit import build_fixed_effects_spec, run_fixed_effects
566
+
567
+ spec = build_fixed_effects_spec(
568
+ dependent="y",
569
+ regressors=["x1", "x2"],
570
+ controls=["z1"],
571
+ )
572
+
573
+ result = run_fixed_effects(
574
+ spec,
575
+ df,
576
+ entity="firm_id",
577
+ time="year",
578
+ )
579
+
580
+ print(result.summary_frame())
581
+ ```
582
+
583
+ Equivalent Stata idea:
584
+
585
+ ```stata
586
+ xtset firm_id year
587
+ xtreg y x1 x2 z1, fe
588
+ ```
589
+
590
+ For two-way fixed effects, use the time-effect option supported by the installed version and confirm the returned model metadata before reporting results.
591
+
592
+ ---
593
+
594
+ ## Random Effects
595
+
596
+ ```python
597
+ from systemgmmkit import RandomEffectsSpec, run_random_effects
598
+
599
+ spec = RandomEffectsSpec(
600
+ dependent="y",
601
+ regressors=["x1", "x2"],
602
+ controls=["z1"],
603
+ )
604
+
605
+ result = run_random_effects(
606
+ spec,
607
+ df,
608
+ entity="firm_id",
609
+ time="year",
610
+ )
611
+
612
+ print(result.summary_frame())
613
+ ```
614
+
615
+ Equivalent Stata idea:
616
+
617
+ ```stata
618
+ xtset firm_id year
619
+ xtreg y x1 x2 z1, re
620
+ ```
621
+
622
+ ---
623
+
624
+ ## Panel IV / 2SLS
625
+
626
+ ```python
627
+ from systemgmmkit import PanelIVSpec, run_panel_2sls
628
+
629
+ spec = PanelIVSpec(
630
+ dependent="y",
631
+ exogenous=["x1", "z1"],
632
+ endogenous=["x2"],
633
+ instruments=["z2"],
634
+ )
635
+
636
+ result = run_panel_2sls(
637
+ spec,
638
+ df,
639
+ entity="firm_id",
640
+ time="year",
641
+ )
642
+
643
+ print(result.summary_frame())
644
+ ```
645
+
646
+ Equivalent Stata idea:
647
+
648
+ ```stata
649
+ ivregress 2sls y x1 z1 (x2 = z2)
650
+ ```
651
+
652
+ ---
653
+
654
+ # Dynamic Panel GMM Estimation
655
+
656
+ `systemgmmkit` supports both Difference GMM and System GMM for dynamic panel-data analysis.
657
+
658
+ The recommended workflow is:
659
+
660
+ 1. Define the structural model.
661
+ 2. Create any lagged variables that should enter the model equation.
662
+ 3. Classify regressors as endogenous, predetermined, or exogenous.
663
+ 4. Define the GMM instrument strategy.
664
+ 5. Run the estimator.
665
+ 6. Inspect diagnostics before interpreting coefficients.
666
+ 7. Export tables and diagnostic figures.
667
+
668
+ The same workflow applies whether estimating Difference GMM or System GMM.
669
+
670
+ ---
671
+
672
+ # Variable Classification Guide
673
+
674
+ Correct variable classification is one of the most important modelling decisions in dynamic-panel estimation.
675
+
676
+ | Classification | Interpretation | Typical instrument treatment | Examples |
677
+ | -------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
678
+ | Endogenous | May be correlated with current and past disturbances | GMM-style instruments using deeper lags | investment, aid, leverage, R&D, production decisions |
679
+ | Predetermined | May react to past shocks but not current shocks | GMM-style instruments, often allowing shorter lag windows than endogenous variables | cash flow, backlog, lagged policy variables, delayed implementation measures |
680
+ | Exogenous | Assumed independent of the disturbance process | IV-style instruments by default | firm size, year dummies, industry dummies, externally determined controls |
681
+
682
+ Researchers should perform robustness checks using alternative classifications when the theoretical justification is uncertain.
683
+
684
+ ---
685
+
686
+ # Structural Lags vs Instrument Lags
687
+
688
+ Dynamic GMM has two different uses of lags. They must not be confused.
689
+
690
+ | Use | Meaning | Example |
691
+ | ---------------------------- | ----------------------------------------------------- | ------------------------------- |
692
+ | Lagged variable in the model | The lag enters the structural equation as a regressor | `L1_investment` in `regressors` |
693
+ | Lagged value as instrument | Past values are used internally as GMM instruments | `gmm_lags=(2, 4)` |
694
+
695
+ This distinction is central.
696
+
697
+ ```python
698
+ regressors = ["L1_investment"]
699
+ ```
700
+
701
+ means:
702
+
703
+ ```text
704
+ Include lagged investment as an explanatory variable in the model equation.
705
+ ```
706
+
707
+ while:
708
+
709
+ ```python
710
+ gmm_lags = (2, 4)
711
+ ```
712
+
713
+ means:
714
+
715
+ ```text
716
+ Use lags 2 through 4 as GMM instruments.
717
+ ```
718
+
719
+ Safe rule:
720
+
721
+ ```text
722
+ Create lagged regressors yourself when they belong in the model equation.
723
+ Classify each lagged regressor according to its maintained exogeneity assumption.
724
+ Use GMM lag-window arguments only to control instrument construction.
725
+ ```
726
+
727
+ ---
728
+
729
+ # Create Structural Lags Before Estimation
730
+
731
+ The public API treats lagged regressors as ordinary columns supplied by the user. It does not automatically create structural `L1_` or `L2_` model variables.
732
+
733
+ ```python
734
+ df = df.sort_values(["firm_id", "year"]).copy()
735
+
736
+ df["L1_y"] = df.groupby("firm_id")["y"].shift(1)
737
+ df["L1_investment"] = df.groupby("firm_id")["investment"].shift(1)
738
+ df["L2_investment"] = df.groupby("firm_id")["investment"].shift(2)
739
+ df["L1_cashflow"] = df.groupby("firm_id")["cashflow"].shift(1)
740
+ df["L2_cashflow"] = df.groupby("firm_id")["cashflow"].shift(2)
741
+ df["L1_firm_size"] = df.groupby("firm_id")["firm_size"].shift(1)
742
+
743
+ df = df.dropna(
744
+ subset=[
745
+ "L1_y",
746
+ "L1_investment",
747
+ "L2_investment",
748
+ "L1_cashflow",
749
+ "L2_cashflow",
750
+ "L1_firm_size",
751
+ ]
752
+ )
753
+ ```
754
+
755
+ ---
756
+
757
+ # Instrumenting Endogenous Variables
758
+
759
+ Use endogenous classification when a regressor may be correlated with current shocks.
760
+
761
+ Common endogenous variables include:
762
+
763
+ * lagged dependent variables;
764
+ * investment;
765
+ * aid flows;
766
+ * leverage;
767
+ * credit supply;
768
+ * R&D spending;
769
+ * production decisions;
770
+ * project implementation decisions.
771
+
772
+ Example:
773
+
774
+ ```python
775
+ regressors = [
776
+ "L1_y",
777
+ "investment",
778
+ "L1_investment",
779
+ "L2_investment",
780
+ "firm_size",
781
+ ]
782
+
783
+ endogenous = [
784
+ "L1_y",
785
+ "investment",
786
+ "L1_investment",
787
+ "L2_investment",
788
+ ]
789
+
790
+ predetermined = []
791
+
792
+ exogenous = [
793
+ "firm_size",
794
+ ]
795
+ ```
796
+
797
+ In this example, `L1_investment` and `L2_investment` are included directly in the model equation and are also classified as endogenous. Lagging an endogenous variable does not automatically make it exogenous.
798
+
799
+ ---
800
+
801
+ # Instrumenting Predetermined Variables
802
+
803
+ Use predetermined classification when a variable may respond to past shocks but is assumed not to respond contemporaneously.
804
+
805
+ Common predetermined variables include:
806
+
807
+ * cash flow;
808
+ * backlog;
809
+ * delayed implementation measures;
810
+ * lagged policy variables;
811
+ * maintenance workload;
812
+ * past operational pressure.
813
+
814
+ Example:
815
+
816
+ ```python
817
+ regressors = [
818
+ "L1_y",
819
+ "cashflow",
820
+ "L1_cashflow",
821
+ "L2_cashflow",
822
+ "firm_size",
823
+ ]
824
+
825
+ endogenous = [
826
+ "L1_y",
827
+ ]
828
+
829
+ predetermined = [
830
+ "cashflow",
831
+ "L1_cashflow",
832
+ "L2_cashflow",
833
+ ]
834
+
835
+ exogenous = [
836
+ "firm_size",
837
+ ]
838
+ ```
839
+
840
+ Lagging a predetermined variable does not automatically make it exogenous. A lagged predetermined variable should usually remain classified as predetermined unless strict exogeneity is theoretically defensible.
841
+
842
+ ---
843
+
844
+ # Instrumenting Exogenous Variables
845
+
846
+ Exogenous variables are treated as IV-style instruments by default.
847
+
848
+ Example:
849
+
850
+ ```python
851
+ regressors = [
852
+ "L1_y",
853
+ "investment",
854
+ "firm_size",
855
+ "L1_firm_size",
856
+ ]
857
+
858
+ endogenous = [
859
+ "L1_y",
860
+ "investment",
861
+ ]
862
+
863
+ predetermined = []
864
+
865
+ exogenous = [
866
+ "firm_size",
867
+ "L1_firm_size",
868
+ ]
869
+ ```
870
+
871
+ A lagged exogenous variable may be classified as exogenous only if strict exogeneity across time is defensible. If strict exogeneity is too strong, classify the lagged variable as predetermined instead.
872
+
873
+ ---
874
+
875
+ # GMM Lag-Window Strategy in 0.5.10
876
+
877
+ Version `0.5.10` supports three levels of GMM lag-window control:
878
+
879
+ 1. global GMM lag window;
880
+ 2. role-specific GMM lag windows;
881
+ 3. variable-specific GMM lag windows.
882
+
883
+ The precedence rule is:
884
+
885
+ ```text
886
+ gmm_lags_by_variable > gmm_lags_by_role > gmm_lags
887
+ ```
888
+
889
+ Meaning:
890
+
891
+ * if a variable has a variable-specific lag window, use that;
892
+ * otherwise, if its role has a role-specific lag window, use that;
893
+ * otherwise, fall back to the global `gmm_lags`.
894
+
895
+ ---
896
+
897
+ ## Global GMM Lag Window
898
+
899
+ The global GMM lag window applies to all GMM-style variables unless overridden.
900
+
901
+ ```python
902
+ gmm_lags = (2, 4)
903
+ ```
904
+
905
+ This means GMM-style instruments are constructed using lags 2 through 4.
906
+
907
+ Example:
908
+
909
+ ```python
910
+ spec = build_system_gmm_spec(
911
+ dependent="y",
912
+ regressors=[
913
+ "L1_y",
914
+ "investment",
915
+ "cashflow",
916
+ "firm_size",
917
+ ],
918
+ endogenous=[
919
+ "L1_y",
920
+ "investment",
921
+ ],
922
+ predetermined=[
923
+ "cashflow",
924
+ ],
925
+ exogenous=[
926
+ "firm_size",
927
+ ],
928
+ gmm_lags=(2, 4),
929
+ collapse=True,
930
+ windmeijer=True,
931
+ )
932
+ ```
933
+
934
+ Equivalent Stata idea:
935
+
936
+ ```stata
937
+ xtabond2 y L.y investment cashflow firm_size, ///
938
+ gmm(L.y investment cashflow, lag(2 4) collapse) ///
939
+ iv(firm_size) ///
940
+ twostep robust small
941
+ ```
942
+
943
+ ---
944
+
945
+ ## Role-Specific GMM Lag Windows
946
+
947
+ Role-specific GMM lag windows allow endogenous and predetermined variables to use different instrument lag ranges.
948
+
949
+ This is useful because predetermined variables may be validly instrumented with shorter lags than fully endogenous variables, depending on the maintained timing assumptions.
950
+
951
+ ```python
952
+ spec = build_system_gmm_spec(
953
+ dependent="y",
954
+ regressors=[
955
+ "L1_y",
956
+ "investment",
957
+ "L1_investment",
958
+ "cashflow",
959
+ "L1_cashflow",
960
+ "firm_size",
961
+ ],
962
+ endogenous=[
963
+ "L1_y",
964
+ "investment",
965
+ "L1_investment",
966
+ ],
967
+ predetermined=[
968
+ "cashflow",
969
+ "L1_cashflow",
970
+ ],
971
+ exogenous=[
972
+ "firm_size",
973
+ ],
974
+ gmm_lags=(2, 4),
975
+ gmm_lags_by_role={
976
+ "endogenous": (2, 5),
977
+ "predetermined": (1, 3),
978
+ },
979
+ collapse=True,
980
+ windmeijer=True,
981
+ )
982
+ ```
983
+
984
+ Equivalent Stata idea:
985
+
986
+ ```stata
987
+ xtabond2 y L.y investment L.investment cashflow L.cashflow firm_size, ///
988
+ gmm(L.y investment L.investment, lag(2 5) collapse) ///
989
+ gmm(cashflow L.cashflow, lag(1 3) collapse) ///
990
+ iv(firm_size) ///
991
+ twostep robust small
992
+ ```
993
+
994
+ In this example:
995
+
996
+ * endogenous variables use lags 2 through 5;
997
+ * predetermined variables use lags 1 through 3;
998
+ * exogenous variables remain IV-style by default.
999
+
1000
+ ---
1001
+
1002
+ ## Variable-Specific GMM Lag Windows
1003
+
1004
+ Variable-specific GMM lag windows allow individual variables to override both role-specific and global lag windows.
1005
+
1006
+ This is useful when some variables are more persistent, more weakly instrumented, or require a more conservative instrument strategy.
1007
+
1008
+ ```python
1009
+ spec = build_system_gmm_spec(
1010
+ dependent="y",
1011
+ regressors=[
1012
+ "L1_y",
1013
+ "investment",
1014
+ "L1_investment",
1015
+ "cashflow",
1016
+ "L1_cashflow",
1017
+ "firm_size",
1018
+ ],
1019
+ endogenous=[
1020
+ "L1_y",
1021
+ "investment",
1022
+ "L1_investment",
1023
+ ],
1024
+ predetermined=[
1025
+ "cashflow",
1026
+ "L1_cashflow",
1027
+ ],
1028
+ exogenous=[
1029
+ "firm_size",
1030
+ ],
1031
+ gmm_lags=(2, 4),
1032
+ gmm_lags_by_role={
1033
+ "endogenous": (2, 5),
1034
+ "predetermined": (1, 3),
1035
+ },
1036
+ gmm_lags_by_variable={
1037
+ "L1_y": (2, 4),
1038
+ "investment": (3, 5),
1039
+ "L1_investment": (3, 6),
1040
+ "cashflow": (1, 2),
1041
+ "L1_cashflow": (2, 3),
1042
+ },
1043
+ collapse=True,
1044
+ windmeijer=True,
1045
+ )
1046
+ ```
1047
+
1048
+ In this example:
1049
+
1050
+ | Variable | Role | Effective GMM lag window | Reason |
1051
+ | --------------- | ------------- | -----------------------: | -------------------------- |
1052
+ | `L1_y` | endogenous | `(2, 4)` | variable-specific override |
1053
+ | `investment` | endogenous | `(3, 5)` | variable-specific override |
1054
+ | `L1_investment` | endogenous | `(3, 6)` | variable-specific override |
1055
+ | `cashflow` | predetermined | `(1, 2)` | variable-specific override |
1056
+ | `L1_cashflow` | predetermined | `(2, 3)` | variable-specific override |
1057
+
1058
+ The variable-specific settings override both:
1059
+
1060
+ ```python
1061
+ gmm_lags_by_role={
1062
+ "endogenous": (2, 5),
1063
+ "predetermined": (1, 3),
1064
+ }
1065
+ ```
1066
+
1067
+ and:
1068
+
1069
+ ```python
1070
+ gmm_lags=(2, 4)
1071
+ ```
1072
+
1073
+ ---
1074
+
1075
+ # Difference GMM
1076
+
1077
+ Difference GMM is often appropriate when:
1078
+
1079
+ * the model contains a lagged dependent variable;
1080
+ * regressors may be endogenous or predetermined;
1081
+ * unobserved individual effects must be removed;
1082
+ * the panel has many entities and relatively few time periods.
1083
+
1084
+ The public API consists of:
1085
+
1086
+ ```python
1087
+ from systemgmmkit import (
1088
+ build_difference_gmm_spec,
1089
+ run_difference_gmm,
1090
+ )
1091
+ ```
1092
+
1093
+ ---
1094
+
1095
+ ## Basic Difference GMM
1096
+
1097
+ ```python
1098
+ from systemgmmkit import build_difference_gmm_spec, run_difference_gmm
1099
+
1100
+ spec = build_difference_gmm_spec(
1101
+ dependent="y",
1102
+ regressors=[
1103
+ "L1_y",
1104
+ "investment",
1105
+ "firm_size",
1106
+ ],
1107
+ endogenous=[
1108
+ "L1_y",
1109
+ "investment",
1110
+ ],
1111
+ exogenous=[
1112
+ "firm_size",
1113
+ ],
1114
+ gmm_lags=(2, 4),
1115
+ collapse=True,
1116
+ )
1117
+
1118
+ result = run_difference_gmm(
1119
+ spec,
1120
+ data=df,
1121
+ entity="firm_id",
1122
+ time="year",
1123
+ backend="auto",
1124
+ )
1125
+
1126
+ print(result)
1127
+ ```
1128
+
1129
+ Equivalent Stata idea:
1130
+
1131
+ ```stata
1132
+ xtabond2 y L.y investment firm_size, ///
1133
+ gmm(L.y investment, lag(2 4) collapse) ///
1134
+ iv(firm_size) ///
1135
+ robust
1136
+ ```
1137
+
1138
+ ---
1139
+
1140
+ ## Difference GMM with Role-Specific Instruments
1141
+
1142
+ ```python
1143
+ spec = build_difference_gmm_spec(
1144
+ dependent="y",
1145
+ regressors=[
1146
+ "L1_y",
1147
+ "investment",
1148
+ "cashflow",
1149
+ "firm_size",
1150
+ ],
1151
+ endogenous=[
1152
+ "L1_y",
1153
+ "investment",
1154
+ ],
1155
+ predetermined=[
1156
+ "cashflow",
1157
+ ],
1158
+ exogenous=[
1159
+ "firm_size",
1160
+ ],
1161
+ gmm_lags=(2, 4),
1162
+ gmm_lags_by_role={
1163
+ "endogenous": (2, 5),
1164
+ "predetermined": (1, 3),
1165
+ },
1166
+ collapse=True,
1167
+ )
1168
+ ```
1169
+
1170
+ Equivalent Stata idea:
1171
+
1172
+ ```stata
1173
+ xtabond2 y L.y investment cashflow firm_size, ///
1174
+ gmm(L.y investment, lag(2 5) collapse) ///
1175
+ gmm(cashflow, lag(1 3) collapse) ///
1176
+ iv(firm_size) ///
1177
+ robust
1178
+ ```
1179
+
1180
+ ---
1181
+
1182
+ ## Difference GMM with Variable-Specific Instruments
1183
+
1184
+ ```python
1185
+ spec = build_difference_gmm_spec(
1186
+ dependent="y",
1187
+ regressors=[
1188
+ "L1_y",
1189
+ "investment",
1190
+ "L1_investment",
1191
+ "cashflow",
1192
+ "firm_size",
1193
+ ],
1194
+ endogenous=[
1195
+ "L1_y",
1196
+ "investment",
1197
+ "L1_investment",
1198
+ ],
1199
+ predetermined=[
1200
+ "cashflow",
1201
+ ],
1202
+ exogenous=[
1203
+ "firm_size",
1204
+ ],
1205
+ gmm_lags=(2, 4),
1206
+ gmm_lags_by_role={
1207
+ "endogenous": (2, 5),
1208
+ "predetermined": (1, 3),
1209
+ },
1210
+ gmm_lags_by_variable={
1211
+ "L1_y": (2, 4),
1212
+ "investment": (3, 5),
1213
+ "L1_investment": (3, 6),
1214
+ "cashflow": (1, 2),
1215
+ },
1216
+ collapse=True,
1217
+ )
1218
+ ```
1219
+
1220
+ Equivalent Stata idea:
1221
+
1222
+ ```stata
1223
+ xtabond2 y L.y investment L.investment cashflow firm_size, ///
1224
+ gmm(L.y, lag(2 4) collapse) ///
1225
+ gmm(investment, lag(3 5) collapse) ///
1226
+ gmm(L.investment, lag(3 6) collapse) ///
1227
+ gmm(cashflow, lag(1 2) collapse) ///
1228
+ iv(firm_size) ///
1229
+ robust
1230
+ ```
1231
+
1232
+ ---
1233
+
1234
+ # System GMM
1235
+
1236
+ System GMM extends Difference GMM by combining:
1237
+
1238
+ * the differenced equation; and
1239
+ * the levels equation.
1240
+
1241
+ System GMM is often preferred when variables are highly persistent and lagged levels are weak instruments for differenced variables.
1242
+
1243
+ The public API consists of:
1244
+
1245
+ ```python
1246
+ from systemgmmkit import (
1247
+ build_system_gmm_spec,
1248
+ run_system_gmm,
1249
+ )
1250
+ ```
1251
+
1252
+ ---
1253
+
1254
+ ## Basic System GMM
1255
+
1256
+ ```python
1257
+ from systemgmmkit import build_system_gmm_spec, run_system_gmm
1258
+
1259
+ spec = build_system_gmm_spec(
1260
+ dependent="y",
1261
+ regressors=[
1262
+ "L1_y",
1263
+ "investment",
1264
+ "firm_size",
1265
+ ],
1266
+ endogenous=[
1267
+ "L1_y",
1268
+ "investment",
1269
+ ],
1270
+ exogenous=[
1271
+ "firm_size",
1272
+ ],
1273
+ gmm_lags=(2, 4),
1274
+ collapse=True,
1275
+ windmeijer=True,
1276
+ )
1277
+
1278
+ result = run_system_gmm(
1279
+ spec,
1280
+ data=df,
1281
+ entity="firm_id",
1282
+ time="year",
1283
+ backend="auto",
1284
+ )
1285
+
1286
+ print(result)
1287
+ ```
1288
+
1289
+ Equivalent Stata idea:
1290
+
1291
+ ```stata
1292
+ xtabond2 y L.y investment firm_size, ///
1293
+ gmm(L.y investment, lag(2 4) collapse) ///
1294
+ iv(firm_size) ///
1295
+ twostep robust small
1296
+ ```
1297
+
1298
+ ---
1299
+
1300
+ ## System GMM with Endogenous, Predetermined, and Exogenous Variables
1301
+
1302
+ ```python
1303
+ spec = build_system_gmm_spec(
1304
+ dependent="y",
1305
+ regressors=[
1306
+ "L1_y",
1307
+ "investment",
1308
+ "cashflow",
1309
+ "firm_size",
1310
+ "year2022",
1311
+ "year2023",
1312
+ ],
1313
+ endogenous=[
1314
+ "L1_y",
1315
+ "investment",
1316
+ ],
1317
+ predetermined=[
1318
+ "cashflow",
1319
+ ],
1320
+ exogenous=[
1321
+ "firm_size",
1322
+ "year2022",
1323
+ "year2023",
1324
+ ],
1325
+ gmm_lags=(2, 4),
1326
+ collapse=True,
1327
+ windmeijer=True,
1328
+ )
1329
+ ```
1330
+
1331
+ ---
1332
+
1333
+ ## System GMM with Role-Specific Instruments
1334
+
1335
+ ```python
1336
+ spec = build_system_gmm_spec(
1337
+ dependent="y",
1338
+ regressors=[
1339
+ "L1_y",
1340
+ "investment",
1341
+ "L1_investment",
1342
+ "cashflow",
1343
+ "L1_cashflow",
1344
+ "firm_size",
1345
+ ],
1346
+ endogenous=[
1347
+ "L1_y",
1348
+ "investment",
1349
+ "L1_investment",
1350
+ ],
1351
+ predetermined=[
1352
+ "cashflow",
1353
+ "L1_cashflow",
1354
+ ],
1355
+ exogenous=[
1356
+ "firm_size",
1357
+ ],
1358
+ gmm_lags=(2, 4),
1359
+ gmm_lags_by_role={
1360
+ "endogenous": (2, 5),
1361
+ "predetermined": (1, 3),
1362
+ },
1363
+ collapse=True,
1364
+ windmeijer=True,
1365
+ )
1366
+ ```
1367
+
1368
+ Equivalent Stata idea:
1369
+
1370
+ ```stata
1371
+ xtabond2 y L.y investment L.investment cashflow L.cashflow firm_size, ///
1372
+ gmm(L.y investment L.investment, lag(2 5) collapse) ///
1373
+ gmm(cashflow L.cashflow, lag(1 3) collapse) ///
1374
+ iv(firm_size) ///
1375
+ twostep robust small
1376
+ ```
1377
+
1378
+ ---
1379
+
1380
+ ## System GMM with Variable-Specific Instruments
1381
+
1382
+ ```python
1383
+ spec = build_system_gmm_spec(
1384
+ dependent="y",
1385
+ regressors=[
1386
+ "L1_y",
1387
+ "investment",
1388
+ "L1_investment",
1389
+ "cashflow",
1390
+ "L1_cashflow",
1391
+ "firm_size",
1392
+ ],
1393
+ endogenous=[
1394
+ "L1_y",
1395
+ "investment",
1396
+ "L1_investment",
1397
+ ],
1398
+ predetermined=[
1399
+ "cashflow",
1400
+ "L1_cashflow",
1401
+ ],
1402
+ exogenous=[
1403
+ "firm_size",
1404
+ ],
1405
+ gmm_lags=(2, 4),
1406
+ gmm_lags_by_role={
1407
+ "endogenous": (2, 5),
1408
+ "predetermined": (1, 3),
1409
+ },
1410
+ gmm_lags_by_variable={
1411
+ "L1_y": (2, 4),
1412
+ "investment": (3, 5),
1413
+ "L1_investment": (3, 6),
1414
+ "cashflow": (1, 2),
1415
+ "L1_cashflow": (2, 3),
1416
+ },
1417
+ collapse=True,
1418
+ windmeijer=True,
1419
+ )
1420
+ ```
1421
+
1422
+ Equivalent Stata idea:
1423
+
1424
+ ```stata
1425
+ xtabond2 y L.y investment L.investment cashflow L.cashflow firm_size, ///
1426
+ gmm(L.y, lag(2 4) collapse) ///
1427
+ gmm(investment, lag(3 5) collapse) ///
1428
+ gmm(L.investment, lag(3 6) collapse) ///
1429
+ gmm(cashflow, lag(1 2) collapse) ///
1430
+ gmm(L.cashflow, lag(2 3) collapse) ///
1431
+ iv(firm_size) ///
1432
+ twostep robust small
1433
+ ```
1434
+
1435
+ ---
1436
+
1437
+ # Exogenous Variables Remain IV-Style by Default
1438
+
1439
+ Exogenous variables should remain IV-style by default.
1440
+
1441
+ ```python
1442
+ exogenous = [
1443
+ "firm_size",
1444
+ "L1_firm_size",
1445
+ ]
1446
+ ```
1447
+
1448
+ The package should not force exogenous variables into GMM-style instrumentation.
1449
+
1450
+ If users need lagged exogenous variables in the model equation, they should create those structural lags manually:
1451
+
1452
+ ```python
1453
+ df["L1_firm_size"] = df.groupby("firm_id")["firm_size"].shift(1)
1454
+ ```
1455
+
1456
+ Then include them as regressors and classify them as exogenous if strict exogeneity is defensible:
1457
+
1458
+ ```python
1459
+ regressors = [
1460
+ "firm_size",
1461
+ "L1_firm_size",
1462
+ ]
1463
+
1464
+ exogenous = [
1465
+ "firm_size",
1466
+ "L1_firm_size",
1467
+ ]
1468
+ ```
1469
+
1470
+ If strict exogeneity is too strong, the lagged variable should be classified as predetermined instead.
1471
+
1472
+ ---
1473
+
1474
+ # Instrument Control
1475
+
1476
+ Instrument count should be controlled to reduce overfitting and avoid weakening the Hansen test.
1477
+
1478
+ Recommended practice:
1479
+
1480
+ * keep the instrument count below the number of groups where possible;
1481
+ * use collapsed instruments when appropriate;
1482
+ * restrict GMM lag windows;
1483
+ * use role-specific lag windows where theoretically justified;
1484
+ * use variable-specific lag windows where persistence or weak-instrument concerns differ by variable;
1485
+ * report the number of instruments;
1486
+ * report AR(1), AR(2), Hansen, and Sargan diagnostics;
1487
+ * compare alternative lag-window choices as robustness checks.
1488
+
1489
+ ---
1490
+
1491
+ # Instrument Architecture Reporting
1492
+
1493
+ Version `0.5.10` supports instrument-architecture reporting through SGM-Viz.
1494
+
1495
+ ```python
1496
+ from systemgmmkit.postestimation import InstrumentArchitecture
1497
+
1498
+ architecture = InstrumentArchitecture(
1499
+ estimator="System GMM",
1500
+ difference_equation=(
1501
+ "L2.y",
1502
+ "L3.y",
1503
+ "L2.investment",
1504
+ "L3.investment",
1505
+ ),
1506
+ level_equation=(
1507
+ "D.y",
1508
+ "D.investment",
1509
+ ),
1510
+ standard_instruments=(
1511
+ "firm_size",
1512
+ "time effects",
1513
+ ),
1514
+ lag_range=(2, 4),
1515
+ collapsed=True,
1516
+ transformation="FOD",
1517
+ total_instruments=result.instruments,
1518
+ groups=result.groups,
1519
+ )
1520
+
1521
+ result.plot.instruments(
1522
+ architecture=architecture,
1523
+ save="outputs/instrument_architecture.png",
1524
+ )
1525
+ ```
1526
+
1527
+ For role-specific or variable-specific instrument designs, the instrument architecture should document the actual instrument blocks used in the model.
1528
+
1529
+ ---
1530
+
1531
+ # Dynamic GMM Diagnostics
1532
+
1533
+ For dynamic-panel GMM models, users should inspect diagnostics before interpreting coefficients.
1534
+
1535
+ Recommended diagnostics include:
1536
+
1537
+ * number of observations;
1538
+ * number of groups;
1539
+ * number of instruments;
1540
+ * instrument/group ratio;
1541
+ * AR(1) test;
1542
+ * AR(2) test;
1543
+ * Hansen test;
1544
+ * Sargan test;
1545
+ * covariance estimator;
1546
+ * one-step or two-step setting;
1547
+ * Windmeijer correction status;
1548
+ * transformation;
1549
+ * estimation backend;
1550
+ * instrument architecture.
1551
+
1552
+ A statistically significant AR(1) test is expected in many differenced dynamic-panel models. The AR(2) test is usually more important for checking second-order serial correlation in differenced residuals.
1553
+
1554
+ The Hansen and Sargan tests should not be interpreted mechanically. Very high Hansen p-values can indicate instrument proliferation, while very low p-values may indicate invalid instruments or misspecification.
1555
+
1556
+ ---
1557
+
1558
+ # Recommended Dynamic GMM Reporting
1559
+
1560
+ For published research, report:
1561
+
1562
+ * dependent variable;
1563
+ * sample period;
1564
+ * number of observations;
1565
+ * number of groups;
1566
+ * estimator type;
1567
+ * transformation;
1568
+ * lagged dependent variable treatment;
1569
+ * endogenous variables;
1570
+ * predetermined variables;
1571
+ * exogenous variables;
1572
+ * structural lags included in the model;
1573
+ * global GMM lag window;
1574
+ * role-specific GMM lag windows, if used;
1575
+ * variable-specific GMM lag windows, if used;
1576
+ * collapse setting;
1577
+ * number of instruments;
1578
+ * instrument/group ratio;
1579
+ * AR(1) diagnostic;
1580
+ * AR(2) diagnostic;
1581
+ * Hansen test;
1582
+ * Sargan test;
1583
+ * covariance estimator;
1584
+ * Windmeijer correction status;
1585
+ * estimation backend;
1586
+ * package version;
1587
+ * model-health dashboard or equivalent diagnostics.
1588
+
1589
+ ---
1590
+
1591
+ # Post-Estimation Utilities
1592
+
1593
+ Version `0.5.10` includes public post-estimation utilities.
1594
+
1595
+ ```python
1596
+ from systemgmmkit import (
1597
+ predict,
1598
+ fitted_values,
1599
+ residuals,
1600
+ vcov,
1601
+ confint,
1602
+ lincom,
1603
+ wald_test,
1604
+ marginal_effects,
1605
+ )
1606
+ ```
1607
+
1608
+ ## Predictions
1609
+
1610
+ ```python
1611
+ pred = predict(result)
1612
+ ```
1613
+
1614
+ or:
1615
+
1616
+ ```python
1617
+ pred = result.predict()
1618
+ ```
1619
+
1620
+ ## Fitted Values
1621
+
1622
+ ```python
1623
+ fit = fitted_values(result)
1624
+ ```
1625
+
1626
+ ## Residuals
1627
+
1628
+ ```python
1629
+ resid = residuals(result)
1630
+ ```
1631
+
1632
+ ## Variance-Covariance Matrix
1633
+
1634
+ ```python
1635
+ V = vcov(result)
1636
+ ```
1637
+
1638
+ ## Confidence Intervals
1639
+
1640
+ ```python
1641
+ ci = confint(result)
1642
+ ```
1643
+
1644
+ ## Linear Combinations
1645
+
1646
+ Equivalent Stata idea:
1647
+
1648
+ ```stata
1649
+ lincom x1 + x2
1650
+ ```
1651
+
1652
+ Python:
1653
+
1654
+ ```python
1655
+ effect = lincom(
1656
+ result,
1657
+ {
1658
+ "x1": 1,
1659
+ "x2": 1,
1660
+ },
1661
+ )
1662
+
1663
+ print(effect)
1664
+ ```
1665
+
1666
+ ## Wald Tests
1667
+
1668
+ Equivalent Stata idea:
1669
+
1670
+ ```stata
1671
+ test x1 x2
1672
+ ```
1673
+
1674
+ Python:
1675
+
1676
+ ```python
1677
+ test_result = wald_test(
1678
+ result,
1679
+ R=[
1680
+ [0, 1, 0],
1681
+ [0, 0, 1],
1682
+ ],
1683
+ )
1684
+
1685
+ print(test_result)
1686
+ ```
1687
+
1688
+ ## Marginal Effects
1689
+
1690
+ ```python
1691
+ me = marginal_effects(result)
1692
+
1693
+ print(me)
1694
+ ```
1695
+
1696
+ For linear estimators, marginal effects correspond to estimated slopes.
1697
+
1698
+ ---
1699
+
1700
+ # Standard Post-Estimation Graphics
1701
+
1702
+ Version `0.5.10` includes standard post-estimation plot functions.
1703
+
1704
+ ## Coefficient plot
1705
+
1706
+ ```python
1707
+ from systemgmmkit.postestimation import coefficient_plot
1708
+
1709
+ coefficient_plot(
1710
+ result,
1711
+ style="sgm",
1712
+ preset="paper",
1713
+ save="outputs/coefficient_plot.png",
1714
+ )
1715
+ ```
1716
+
1717
+ ## Marginal effects plot
1718
+
1719
+ ```python
1720
+ from systemgmmkit.postestimation import marginal_effects_plot
1721
+
1722
+ marginal_effects_plot(
1723
+ effects_df,
1724
+ style="sgm",
1725
+ preset="paper",
1726
+ save="outputs/marginal_effects.png",
1727
+ )
1728
+ ```
1729
+
1730
+ Expected input:
1731
+
1732
+ ```python
1733
+ effects_df = pd.DataFrame({
1734
+ "term": ["techshare", "polity", "fragility"],
1735
+ "effect": [0.18, 0.04, -0.09],
1736
+ "std_error": [0.04, 0.02, 0.03],
1737
+ })
1738
+ ```
1739
+
1740
+ ## Margins / prediction plot
1741
+
1742
+ ```python
1743
+ from systemgmmkit.postestimation import margins_prediction_plot
1744
+
1745
+ margins_prediction_plot(
1746
+ margins_df,
1747
+ x="techshare",
1748
+ y="pred",
1749
+ lower="lo",
1750
+ upper="hi",
1751
+ group="polity_group",
1752
+ save="outputs/prediction_plot.png",
1753
+ )
1754
+ ```
1755
+
1756
+ ## Interaction plot
1757
+
1758
+ ```python
1759
+ from systemgmmkit.postestimation import interaction_plot
1760
+
1761
+ interaction_plot(
1762
+ interaction_df,
1763
+ x="techshare",
1764
+ y="pred",
1765
+ moderator="fragility_group",
1766
+ lower="lo",
1767
+ upper="hi",
1768
+ save="outputs/interaction_plot.png",
1769
+ )
1770
+ ```
1771
+
1772
+ ## Residual diagnostics
1773
+
1774
+ ```python
1775
+ from systemgmmkit.postestimation import (
1776
+ residuals_vs_fitted_plot,
1777
+ qq_residual_plot,
1778
+ residual_histogram,
1779
+ )
1780
+
1781
+ residuals_vs_fitted_plot(result, save="outputs/residuals_vs_fitted.png")
1782
+ qq_residual_plot(result.residuals, save="outputs/qq_residuals.png")
1783
+ residual_histogram(result.residuals, save="outputs/residual_histogram.png")
1784
+ ```
1785
+
1786
+ ## Hansen / AR diagnostic plot
1787
+
1788
+ ```python
1789
+ from systemgmmkit.postestimation import hansen_ar_diagnostic_plot
1790
+
1791
+ hansen_ar_diagnostic_plot(
1792
+ {
1793
+ "Hansen": result.hansen_p,
1794
+ "Sargan": result.sargan_p,
1795
+ "AR(1)": result.ar1_p,
1796
+ "AR(2)": result.ar2_p,
1797
+ },
1798
+ save="outputs/hansen_ar_diagnostics.png",
1799
+ )
1800
+ ```
1801
+
1802
+ ---
1803
+
1804
+ # Standard Post-Estimation Gallery
1805
+
1806
+ To export the complete standard plot gallery:
1807
+
1808
+ ```python
1809
+ from systemgmmkit.postestimation import export_standard_postestimation_gallery
1810
+
1811
+ gallery = export_standard_postestimation_gallery(
1812
+ result,
1813
+ output_dir="outputs/standard_gallery",
1814
+ prefix="model",
1815
+ )
1816
+ ```
1817
+
1818
+ Using the result accessor:
1819
+
1820
+ ```python
1821
+ result.plot.standard_gallery(
1822
+ "outputs/standard_gallery",
1823
+ prefix="model",
1824
+ )
1825
+ ```
1826
+
1827
+ The standard gallery is the R/Stata-style plot collection. It is useful for detailed diagnostic review and publication appendix workflows.
1828
+
1829
+ ---
1830
+
1831
+ # SGM-Viz v2 Diagnostic Dashboards
1832
+
1833
+ SGM-Viz is the package's higher-level diagnostic visualization system.
1834
+
1835
+ It combines:
1836
+
1837
+ * econometric diagnostic discipline;
1838
+ * publication-quality layout;
1839
+ * dashboard-style readability;
1840
+ * dynamic-panel-specific interpretation.
1841
+
1842
+ ## Model health dashboard
1843
+
1844
+ ```python
1845
+ result.plot.health(
1846
+ save="outputs/model_health.png",
1847
+ )
1848
+ ```
1849
+
1850
+ This figure summarizes:
1851
+
1852
+ * Hansen diagnostic;
1853
+ * Sargan diagnostic;
1854
+ * AR(1);
1855
+ * AR(2);
1856
+ * observations;
1857
+ * groups;
1858
+ * instruments;
1859
+ * instrument/group ratio;
1860
+ * collapse status.
1861
+
1862
+ ## Dynamic persistence dashboard
1863
+
1864
+ ```python
1865
+ result.plot.persistence(
1866
+ phi=result.params["L1.y"],
1867
+ save="outputs/persistence.png",
1868
+ )
1869
+ ```
1870
+
1871
+ This figure reports:
1872
+
1873
+ * persistence coefficient;
1874
+ * shock decay path;
1875
+ * half-life;
1876
+ * long-run multiplier;
1877
+ * persistence class.
1878
+
1879
+ ## Instrument architecture dashboard
1880
+
1881
+ ```python
1882
+ from systemgmmkit.postestimation import InstrumentArchitecture
1883
+
1884
+ architecture = InstrumentArchitecture(
1885
+ estimator="System GMM",
1886
+ difference_equation=("L2.y", "L3.y"),
1887
+ level_equation=("D.y",),
1888
+ standard_instruments=("x", "w", "time effects"),
1889
+ lag_range=(2, 3),
1890
+ collapsed=True,
1891
+ transformation="FOD",
1892
+ total_instruments=result.instruments,
1893
+ groups=result.groups,
1894
+ )
1895
+
1896
+ result.plot.instruments(
1897
+ architecture=architecture,
1898
+ save="outputs/instruments.png",
1899
+ )
1900
+ ```
1901
+
1902
+ This figure communicates:
1903
+
1904
+ * difference-equation instruments;
1905
+ * level-equation instruments;
1906
+ * standard instruments;
1907
+ * lag range;
1908
+ * collapse status;
1909
+ * total instrument count;
1910
+ * instrument/group ratio.
1911
+
1912
+ ## Publication panel
1913
+
1914
+ ```python
1915
+ result.plot.publication_panel(
1916
+ architecture=architecture,
1917
+ phi=result.params["L1.y"],
1918
+ save="outputs/publication_panel.png",
1919
+ )
1920
+ ```
1921
+
1922
+ The publication panel combines:
1923
+
1924
+ * model health;
1925
+ * dynamic persistence;
1926
+ * instrument architecture;
1927
+ * parameter impact.
1928
+
1929
+ ---
1930
+
1931
+ # One-Command SGM-Viz Report
1932
+
1933
+ ```python
1934
+ result.plot.export_all(
1935
+ "outputs/sgm_report",
1936
+ prefix="model",
1937
+ architecture=architecture,
1938
+ gallery_mode="dashboard",
1939
+ )
1940
+ ```
1941
+
1942
+ Report modes:
1943
+
1944
+ ```text
1945
+ dashboard = individual dashboards only
1946
+ publication = composed publication panel only
1947
+ full = all figures
1948
+ ```
1949
+
1950
+ Recommended use:
1951
+
1952
+ ```python
1953
+ result.plot.export_all(
1954
+ "outputs/sgm_report",
1955
+ gallery_mode="dashboard",
1956
+ )
1957
+ ```
1958
+
1959
+ For a one-page paper figure:
1960
+
1961
+ ```python
1962
+ result.plot.export_all(
1963
+ "outputs/paper_report",
1964
+ gallery_mode="publication",
1965
+ )
1966
+ ```
1967
+
1968
+ ---
1969
+
1970
+ # Model Comparison Dashboard
1971
+
1972
+ ```python
1973
+ from systemgmmkit.postestimation import model_comparison_dashboard_v2
1974
+
1975
+ model_comparison_dashboard_v2(
1976
+ [baseline_result, robustness_result],
1977
+ labels=["Baseline", "Robustness"],
1978
+ save="outputs/model_comparison.png",
1979
+ )
1980
+ ```
1981
+
1982
+ This is designed for screening alternative GMM specifications by:
1983
+
1984
+ * Hansen p-value;
1985
+ * Sargan p-value;
1986
+ * AR(2) p-value;
1987
+ * instrument/group ratio.
1988
+
1989
+ ---
1990
+
1991
+ # Reporting and Export
1992
+
1993
+ Results can be exported to:
1994
+
1995
+ * Markdown;
1996
+ * CSV;
1997
+ * LaTeX;
1998
+ * PNG;
1999
+ * SVG;
2000
+ * PDF-compatible figures;
2001
+ * HTML galleries.
2002
+
2003
+ `systemgmmkit` is designed to integrate with:
2004
+
2005
+ * `universal-output-hub`;
2006
+ * publication pipelines;
2007
+ * reproducible research workflows;
2008
+ * model-comparison tables;
2009
+ * diagnostic figure workflows.
2010
+
2011
+ ---
2012
+
2013
+ # Example Reporting Workflow with universal-output-hub
2014
+
2015
+ ```python
2016
+ from universal_output_hub import outreg
2017
+
2018
+ outreg(
2019
+ [result],
2020
+ model_names=["System GMM"],
2021
+ path="tables/system_gmm_results.md",
2022
+ )
2023
+ ```
2024
+
2025
+ The reporting layer is intentionally separate from estimation. This allows users to estimate models in `systemgmmkit` and export publication-style tables through `universal-output-hub`.
2026
+
2027
+ ---
2028
+
2029
+ # Practical Modelling Guidance
2030
+
2031
+ ## Use OLS and panel estimators as baselines
2032
+
2033
+ Dynamic GMM should not usually be the first model estimated.
2034
+
2035
+ A defensible empirical workflow often starts with:
2036
+
2037
+ 1. OLS or pooled OLS;
2038
+ 2. fixed effects;
2039
+ 3. random effects where appropriate;
2040
+ 4. IV / 2SLS where identification requires external instruments;
2041
+ 5. Difference GMM or System GMM for dynamic-panel settings.
2042
+
2043
+ This helps users understand how estimates change across assumptions.
2044
+
2045
+ ## Control instrument proliferation
2046
+
2047
+ Instrument proliferation is one of the most common problems in applied dynamic GMM.
2048
+
2049
+ Recommended practice:
2050
+
2051
+ * use `collapse=True`;
2052
+ * restrict `gmm_lags`;
2053
+ * report instrument count;
2054
+ * compare instrument count with number of groups;
2055
+ * check whether Hansen p-values are suspiciously high;
2056
+ * run robustness checks with alternative lag windows;
2057
+ * inspect the SGM-Viz instrument architecture dashboard.
2058
+
2059
+ ## Do not overinterpret one specification
2060
+
2061
+ Dynamic GMM estimates are sensitive to:
2062
+
2063
+ * lag-window choices;
2064
+ * variable classification;
2065
+ * transformation choice;
2066
+ * instrument count;
2067
+ * sample construction;
2068
+ * missing-value handling;
2069
+ * persistence of the dependent variable;
2070
+ * weak instruments.
2071
+
2072
+ Users should treat dynamic GMM as part of a specification family, not as a single automatic estimator.
2073
+
2074
+ ---
2075
+
2076
+ # Recommended Dynamic GMM Reporting
2077
+
2078
+ For published research, report:
2079
+
2080
+ * dependent variable;
2081
+ * sample period;
2082
+ * number of observations;
2083
+ * number of groups;
2084
+ * estimator type;
2085
+ * transformation;
2086
+ * lagged dependent variable treatment;
2087
+ * endogenous variables;
2088
+ * predetermined variables;
2089
+ * exogenous variables;
2090
+ * structural lags included in the model;
2091
+ * GMM lag window;
2092
+ * collapse setting;
2093
+ * number of instruments;
2094
+ * AR(1) diagnostic;
2095
+ * AR(2) diagnostic;
2096
+ * Hansen test;
2097
+ * Sargan test;
2098
+ * covariance estimator;
2099
+ * Windmeijer correction status;
2100
+ * estimation backend;
2101
+ * package version;
2102
+ * model-health dashboard or equivalent diagnostics.
2103
+
2104
+ ---
2105
+
2106
+ # Roadmap
2107
+
2108
+ The next technical extension should focus on variable-level and role-level GMM instrument design.
2109
+
2110
+ Planned features:
2111
+
2112
+ * role-specific GMM lag windows;
2113
+ * variable-specific GMM lag windows;
2114
+ * explicit precedence rule:
2115
+
2116
+ ```text
2117
+ gmm_lags_by_variable > gmm_lags_by_role > gmm_lags
2118
+ ```
2119
+
2120
+ * tests confirming exogenous variables remain IV-style unless explicitly handled otherwise;
2121
+ * Difference GMM and System GMM parity checks using separate `gmm()` blocks;
2122
+ * instrument-count and instrument-name validation;
2123
+ * documentation examples;
2124
+ * Stata comparison scripts.
2125
+
2126
+ These features should not be documented as current functionality until implemented and validated.
2127
+
2128
+ ---
2129
+
2130
+ # Citation
2131
+
2132
+ If you use `systemgmmkit` in academic work, please cite:
2133
+
2134
+ ```text
2135
+ Akanbi, Oluwajuwon Mayomi.
2136
+
2137
+ systemgmmkit:
2138
+ Panel Data Econometrics and Dynamic GMM Workflows in Python.
2139
+
2140
+ Version 0.5.10.
2141
+
2142
+ https://github.com/Akanom/systemgmmkit
2143
+ ```
2144
+
2145
+ BibTeX:
2146
+
2147
+ ```bibtex
2148
+ @software{akanbi_systemgmmkit,
2149
+ author = {Akanbi, Oluwajuwon Mayomi},
2150
+ title = {systemgmmkit: Panel Data Econometrics and Dynamic GMM Workflows in Python},
2151
+ year = {2026},
2152
+ url = {https://github.com/Akanom/systemgmmkit},
2153
+ version = {0.5.10}
2154
+ }
2155
+ ```
2156
+
2157
+ Replace or supplement this citation with DOI information once a Zenodo archive or software paper is available.
2158
+
2159
+ ---
2160
+
2161
+ # License
2162
+
2163
+ MIT License.
2164
+
2165
+ See `LICENSE` for details.