panelbox 0.2.0__py3-none-any.whl

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 (90) hide show
  1. panelbox/__init__.py +67 -0
  2. panelbox/__version__.py +14 -0
  3. panelbox/cli/__init__.py +0 -0
  4. panelbox/cli/{commands}/__init__.py +0 -0
  5. panelbox/core/__init__.py +0 -0
  6. panelbox/core/base_model.py +164 -0
  7. panelbox/core/formula_parser.py +318 -0
  8. panelbox/core/panel_data.py +387 -0
  9. panelbox/core/results.py +366 -0
  10. panelbox/datasets/__init__.py +0 -0
  11. panelbox/datasets/{data}/__init__.py +0 -0
  12. panelbox/gmm/__init__.py +65 -0
  13. panelbox/gmm/difference_gmm.py +645 -0
  14. panelbox/gmm/estimator.py +562 -0
  15. panelbox/gmm/instruments.py +580 -0
  16. panelbox/gmm/results.py +550 -0
  17. panelbox/gmm/system_gmm.py +621 -0
  18. panelbox/gmm/tests.py +535 -0
  19. panelbox/models/__init__.py +11 -0
  20. panelbox/models/dynamic/__init__.py +0 -0
  21. panelbox/models/iv/__init__.py +0 -0
  22. panelbox/models/static/__init__.py +13 -0
  23. panelbox/models/static/fixed_effects.py +516 -0
  24. panelbox/models/static/pooled_ols.py +298 -0
  25. panelbox/models/static/random_effects.py +512 -0
  26. panelbox/report/__init__.py +61 -0
  27. panelbox/report/asset_manager.py +410 -0
  28. panelbox/report/css_manager.py +472 -0
  29. panelbox/report/exporters/__init__.py +15 -0
  30. panelbox/report/exporters/html_exporter.py +440 -0
  31. panelbox/report/exporters/latex_exporter.py +510 -0
  32. panelbox/report/exporters/markdown_exporter.py +446 -0
  33. panelbox/report/renderers/__init__.py +11 -0
  34. panelbox/report/renderers/static/__init__.py +0 -0
  35. panelbox/report/renderers/static_validation_renderer.py +341 -0
  36. panelbox/report/report_manager.py +502 -0
  37. panelbox/report/template_manager.py +337 -0
  38. panelbox/report/transformers/__init__.py +0 -0
  39. panelbox/report/transformers/static/__init__.py +0 -0
  40. panelbox/report/validation_transformer.py +449 -0
  41. panelbox/standard_errors/__init__.py +0 -0
  42. panelbox/templates/__init__.py +0 -0
  43. panelbox/templates/assets/css/base_styles.css +382 -0
  44. panelbox/templates/assets/css/report_components.css +747 -0
  45. panelbox/templates/assets/js/tab-navigation.js +161 -0
  46. panelbox/templates/assets/js/utils.js +276 -0
  47. panelbox/templates/common/footer.html +24 -0
  48. panelbox/templates/common/header.html +44 -0
  49. panelbox/templates/common/meta.html +5 -0
  50. panelbox/templates/validation/interactive/index.html +272 -0
  51. panelbox/templates/validation/interactive/partials/charts.html +58 -0
  52. panelbox/templates/validation/interactive/partials/methodology.html +201 -0
  53. panelbox/templates/validation/interactive/partials/overview.html +146 -0
  54. panelbox/templates/validation/interactive/partials/recommendations.html +101 -0
  55. panelbox/templates/validation/interactive/partials/test_results.html +231 -0
  56. panelbox/utils/__init__.py +0 -0
  57. panelbox/utils/formatting.py +172 -0
  58. panelbox/utils/matrix_ops.py +233 -0
  59. panelbox/utils/statistical.py +173 -0
  60. panelbox/validation/__init__.py +58 -0
  61. panelbox/validation/base.py +175 -0
  62. panelbox/validation/cointegration/__init__.py +0 -0
  63. panelbox/validation/cross_sectional_dependence/__init__.py +13 -0
  64. panelbox/validation/cross_sectional_dependence/breusch_pagan_lm.py +222 -0
  65. panelbox/validation/cross_sectional_dependence/frees.py +297 -0
  66. panelbox/validation/cross_sectional_dependence/pesaran_cd.py +188 -0
  67. panelbox/validation/heteroskedasticity/__init__.py +13 -0
  68. panelbox/validation/heteroskedasticity/breusch_pagan.py +222 -0
  69. panelbox/validation/heteroskedasticity/modified_wald.py +172 -0
  70. panelbox/validation/heteroskedasticity/white.py +208 -0
  71. panelbox/validation/instruments/__init__.py +0 -0
  72. panelbox/validation/robustness/__init__.py +0 -0
  73. panelbox/validation/serial_correlation/__init__.py +13 -0
  74. panelbox/validation/serial_correlation/baltagi_wu.py +220 -0
  75. panelbox/validation/serial_correlation/breusch_godfrey.py +260 -0
  76. panelbox/validation/serial_correlation/wooldridge_ar.py +200 -0
  77. panelbox/validation/specification/__init__.py +16 -0
  78. panelbox/validation/specification/chow.py +273 -0
  79. panelbox/validation/specification/hausman.py +264 -0
  80. panelbox/validation/specification/mundlak.py +331 -0
  81. panelbox/validation/specification/reset.py +273 -0
  82. panelbox/validation/unit_root/__init__.py +0 -0
  83. panelbox/validation/validation_report.py +257 -0
  84. panelbox/validation/validation_suite.py +401 -0
  85. panelbox-0.2.0.dist-info/METADATA +337 -0
  86. panelbox-0.2.0.dist-info/RECORD +90 -0
  87. panelbox-0.2.0.dist-info/WHEEL +5 -0
  88. panelbox-0.2.0.dist-info/entry_points.txt +2 -0
  89. panelbox-0.2.0.dist-info/licenses/LICENSE +21 -0
  90. panelbox-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,366 @@
1
+ """
2
+ Results container for panel econometric models.
3
+
4
+ This module provides the PanelResults class which stores estimation results
5
+ and provides methods for inference, prediction, and reporting.
6
+ """
7
+
8
+ from typing import Optional, Dict, Any, List
9
+ import numpy as np
10
+ import pandas as pd
11
+ from scipy import stats
12
+
13
+
14
+ class PanelResults:
15
+ """
16
+ Container for panel model estimation results.
17
+
18
+ This class stores all estimation results and provides methods for
19
+ inference, testing, prediction, and reporting.
20
+
21
+ Parameters
22
+ ----------
23
+ params : pd.Series
24
+ Estimated coefficients
25
+ std_errors : pd.Series
26
+ Standard errors
27
+ cov_params : pd.DataFrame
28
+ Covariance matrix of parameters
29
+ resid : np.ndarray
30
+ Residuals
31
+ fittedvalues : np.ndarray
32
+ Fitted values
33
+ model_info : dict
34
+ Dictionary with model information
35
+ data_info : dict
36
+ Dictionary with data information
37
+
38
+ Attributes
39
+ ----------
40
+ params : pd.Series
41
+ Estimated coefficients with parameter names
42
+ std_errors : pd.Series
43
+ Standard errors
44
+ tvalues : pd.Series
45
+ t-statistics
46
+ pvalues : pd.Series
47
+ p-values for two-sided t-tests
48
+ cov_params : pd.DataFrame
49
+ Covariance matrix of parameters
50
+ resid : np.ndarray
51
+ Residuals
52
+ fittedvalues : np.ndarray
53
+ Fitted values
54
+ nobs : int
55
+ Number of observations
56
+ n_entities : int
57
+ Number of entities
58
+ n_periods : int
59
+ Number of time periods
60
+ df_model : int
61
+ Degrees of freedom for model
62
+ df_resid : int
63
+ Degrees of freedom for residuals
64
+ rsquared : float
65
+ R-squared
66
+ rsquared_adj : float
67
+ Adjusted R-squared
68
+ rsquared_within : float
69
+ Within R-squared (for panel models)
70
+ rsquared_between : float
71
+ Between R-squared (for panel models)
72
+ rsquared_overall : float
73
+ Overall R-squared (for panel models)
74
+ """
75
+
76
+ def __init__(
77
+ self,
78
+ params: pd.Series,
79
+ std_errors: pd.Series,
80
+ cov_params: pd.DataFrame,
81
+ resid: np.ndarray,
82
+ fittedvalues: np.ndarray,
83
+ model_info: Dict[str, Any],
84
+ data_info: Dict[str, Any],
85
+ rsquared_dict: Optional[Dict[str, float]] = None,
86
+ model: Optional[Any] = None
87
+ ):
88
+ # Parameter estimates
89
+ self.params = params
90
+ self.std_errors = std_errors
91
+ self.cov_params = cov_params
92
+
93
+ # Residuals and fitted values
94
+ self.resid = resid
95
+ self.fittedvalues = fittedvalues
96
+
97
+ # Model information
98
+ self.model_type = model_info.get('model_type', 'Unknown')
99
+ self.formula = model_info.get('formula', '')
100
+ self.cov_type = model_info.get('cov_type', 'nonrobust')
101
+ self.cov_kwds = model_info.get('cov_kwds', {})
102
+
103
+ # Data information
104
+ self.nobs = data_info['nobs']
105
+ self.n_entities = data_info['n_entities']
106
+ self.n_periods = data_info.get('n_periods', None)
107
+ self.df_model = data_info['df_model']
108
+ self.df_resid = data_info['df_resid']
109
+
110
+ # Entity and time indices (for validation tests)
111
+ self.entity_index = data_info.get('entity_index', None)
112
+ self.time_index = data_info.get('time_index', None)
113
+
114
+ # Store reference to model for validation tests
115
+ self._model = model
116
+
117
+ # Compute t-values and p-values (after df_resid is defined)
118
+ self.tvalues = self.params / self.std_errors
119
+ # Ensure pvalues is a pandas Series with the same index as params
120
+ pvalues_array = 2 * (1 - stats.t.cdf(np.abs(self.tvalues.values), self.df_resid))
121
+ self.pvalues = pd.Series(pvalues_array, index=self.params.index)
122
+
123
+ # R-squared statistics
124
+ if rsquared_dict is not None:
125
+ self.rsquared = rsquared_dict.get('rsquared', np.nan)
126
+ self.rsquared_adj = rsquared_dict.get('rsquared_adj', np.nan)
127
+ self.rsquared_within = rsquared_dict.get('rsquared_within', np.nan)
128
+ self.rsquared_between = rsquared_dict.get('rsquared_between', np.nan)
129
+ self.rsquared_overall = rsquared_dict.get('rsquared_overall', np.nan)
130
+ else:
131
+ self.rsquared = np.nan
132
+ self.rsquared_adj = np.nan
133
+ self.rsquared_within = np.nan
134
+ self.rsquared_between = np.nan
135
+ self.rsquared_overall = np.nan
136
+
137
+ def conf_int(self, alpha: float = 0.05) -> pd.DataFrame:
138
+ """
139
+ Compute confidence intervals for parameters.
140
+
141
+ Parameters
142
+ ----------
143
+ alpha : float, default=0.05
144
+ Significance level (e.g., 0.05 for 95% CI)
145
+
146
+ Returns
147
+ -------
148
+ pd.DataFrame
149
+ Confidence intervals with columns 'lower' and 'upper'
150
+
151
+ Examples
152
+ --------
153
+ >>> ci = results.conf_int(alpha=0.05)
154
+ >>> print(ci)
155
+ """
156
+ t_critical = stats.t.ppf(1 - alpha/2, self.df_resid)
157
+ margin = t_critical * self.std_errors
158
+
159
+ ci = pd.DataFrame({
160
+ 'lower': self.params - margin,
161
+ 'upper': self.params + margin
162
+ }, index=self.params.index)
163
+
164
+ return ci
165
+
166
+ def predict(self, newdata: Optional[pd.DataFrame] = None) -> np.ndarray:
167
+ """
168
+ Generate predictions.
169
+
170
+ Parameters
171
+ ----------
172
+ newdata : pd.DataFrame, optional
173
+ New data for prediction. If None, returns fitted values.
174
+
175
+ Returns
176
+ -------
177
+ np.ndarray
178
+ Predicted values
179
+
180
+ Examples
181
+ --------
182
+ >>> predictions = results.predict()
183
+ >>> new_predictions = results.predict(new_data)
184
+ """
185
+ if newdata is None:
186
+ return self.fittedvalues
187
+ else:
188
+ raise NotImplementedError("Prediction on new data not yet implemented")
189
+
190
+ def summary(self, title: Optional[str] = None) -> str:
191
+ """
192
+ Generate formatted summary of results.
193
+
194
+ Parameters
195
+ ----------
196
+ title : str, optional
197
+ Custom title for summary table
198
+
199
+ Returns
200
+ -------
201
+ str
202
+ Formatted summary table
203
+
204
+ Examples
205
+ --------
206
+ >>> print(results.summary())
207
+ """
208
+ lines = []
209
+
210
+ # Header
211
+ lines.append("=" * 78)
212
+ if title is None:
213
+ title = f"{self.model_type} Estimation Results"
214
+ lines.append(title.center(78))
215
+ lines.append("=" * 78)
216
+
217
+ # Model information
218
+ lines.append(f"Formula: {self.formula}")
219
+ lines.append(f"Model: {self.model_type}")
220
+ lines.append("-" * 78)
221
+
222
+ # Sample information
223
+ lines.append(f"No. Observations: {self.nobs:>10,}")
224
+ lines.append(f"No. Entities: {self.n_entities:>10,}")
225
+ if self.n_periods is not None:
226
+ lines.append(f"No. Time Periods: {self.n_periods:>10,}")
227
+ lines.append(f"Degrees of Freedom: {self.df_resid:>10,}")
228
+
229
+ # R-squared
230
+ if not np.isnan(self.rsquared):
231
+ lines.append(f"R-squared: {self.rsquared:>10.4f}")
232
+ if not np.isnan(self.rsquared_adj):
233
+ lines.append(f"Adj. R-squared: {self.rsquared_adj:>10.4f}")
234
+ if not np.isnan(self.rsquared_within):
235
+ lines.append(f"R-squared (within): {self.rsquared_within:>10.4f}")
236
+ if not np.isnan(self.rsquared_between):
237
+ lines.append(f"R-squared (between): {self.rsquared_between:>10.4f}")
238
+ if not np.isnan(self.rsquared_overall):
239
+ lines.append(f"R-squared (overall): {self.rsquared_overall:>10.4f}")
240
+
241
+ # Standard errors type
242
+ lines.append(f"Standard Errors: {self.cov_type:>10}")
243
+
244
+ lines.append("=" * 78)
245
+
246
+ # Coefficient table
247
+ lines.append(f"{'Variable':<15} {'Coef.':<12} {'Std.Err.':<12} {'t':<8} {'P>|t|':<8} {'[0.025':<10} {'0.975]':<10}")
248
+ lines.append("-" * 78)
249
+
250
+ ci = self.conf_int(alpha=0.05)
251
+
252
+ for var in self.params.index:
253
+ coef = self.params[var]
254
+ se = self.std_errors[var]
255
+ t = self.tvalues[var]
256
+ p = self.pvalues[var]
257
+ ci_lower = ci.loc[var, 'lower']
258
+ ci_upper = ci.loc[var, 'upper']
259
+
260
+ # Significance stars
261
+ if p < 0.001:
262
+ stars = '***'
263
+ elif p < 0.01:
264
+ stars = '**'
265
+ elif p < 0.05:
266
+ stars = '*'
267
+ elif p < 0.10:
268
+ stars = '.'
269
+ else:
270
+ stars = ''
271
+
272
+ lines.append(
273
+ f"{var:<15} {coef:>11.4f} {se:>11.4f} {t:>7.3f} "
274
+ f"{p:>7.4f} {ci_lower:>9.4f} {ci_upper:>9.4f} {stars}"
275
+ )
276
+
277
+ lines.append("=" * 78)
278
+ lines.append("Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1")
279
+ lines.append("")
280
+
281
+ return "\n".join(lines)
282
+
283
+ def to_dict(self) -> Dict[str, Any]:
284
+ """
285
+ Export results as dictionary.
286
+
287
+ Returns
288
+ -------
289
+ dict
290
+ Dictionary with all results
291
+ """
292
+ return {
293
+ 'params': self.params.to_dict(),
294
+ 'std_errors': self.std_errors.to_dict(),
295
+ 'tvalues': self.tvalues.to_dict(),
296
+ 'pvalues': self.pvalues.to_dict(),
297
+ 'model_info': {
298
+ 'model_type': self.model_type,
299
+ 'formula': self.formula,
300
+ 'cov_type': self.cov_type,
301
+ },
302
+ 'sample_info': {
303
+ 'nobs': self.nobs,
304
+ 'n_entities': self.n_entities,
305
+ 'n_periods': self.n_periods,
306
+ 'df_model': self.df_model,
307
+ 'df_resid': self.df_resid,
308
+ },
309
+ 'rsquared': {
310
+ 'rsquared': self.rsquared,
311
+ 'rsquared_adj': self.rsquared_adj,
312
+ 'rsquared_within': self.rsquared_within,
313
+ 'rsquared_between': self.rsquared_between,
314
+ 'rsquared_overall': self.rsquared_overall,
315
+ }
316
+ }
317
+
318
+ def validate(
319
+ self,
320
+ tests: str = 'default',
321
+ alpha: float = 0.05,
322
+ verbose: bool = False
323
+ ) -> 'ValidationReport':
324
+ """
325
+ Run validation tests on model results.
326
+
327
+ Parameters
328
+ ----------
329
+ tests : str or list of str, default='default'
330
+ Which tests to run:
331
+ - 'all': Run all available tests
332
+ - 'default': Run recommended tests for this model type
333
+ - 'serial': Serial correlation tests only
334
+ - 'het': Heteroskedasticity tests only
335
+ - 'cd': Cross-sectional dependence tests only
336
+ alpha : float, default=0.05
337
+ Significance level for tests
338
+ verbose : bool, default=False
339
+ If True, print progress during testing
340
+
341
+ Returns
342
+ -------
343
+ ValidationReport
344
+ Report containing all test results
345
+
346
+ Examples
347
+ --------
348
+ >>> results = fe.fit()
349
+ >>> validation = results.validate(tests='all', verbose=True)
350
+ >>> print(validation)
351
+ """
352
+ from panelbox.validation.validation_suite import ValidationSuite
353
+
354
+ suite = ValidationSuite(self)
355
+ return suite.run(tests=tests, alpha=alpha, verbose=verbose)
356
+
357
+ def __repr__(self) -> str:
358
+ """String representation."""
359
+ return (f"PanelResults("
360
+ f"model='{self.model_type}', "
361
+ f"nobs={self.nobs}, "
362
+ f"k_params={len(self.params)})")
363
+
364
+ def __str__(self) -> str:
365
+ """String representation (calls summary)."""
366
+ return self.summary()
File without changes
File without changes
@@ -0,0 +1,65 @@
1
+ """
2
+ PanelBox GMM Module
3
+ ===================
4
+
5
+ Dynamic panel data models using Generalized Method of Moments (GMM).
6
+
7
+ This module implements:
8
+ - Difference GMM (Arellano-Bond, 1991)
9
+ - System GMM (Blundell-Bond, 1998)
10
+ - One-step, two-step, and iterative estimation
11
+ - Windmeijer (2005) finite-sample correction
12
+ - Specification tests: Hansen J, Sargan, AR(1), AR(2)
13
+
14
+ Classes
15
+ -------
16
+ DifferenceGMM : Arellano-Bond (1991) Difference GMM estimator
17
+ SystemGMM : Blundell-Bond (1998) System GMM estimator
18
+ GMMResults : Results from GMM estimation
19
+ InstrumentBuilder : Generates instrument matrices
20
+ GMMEstimator : Low-level GMM estimation routines
21
+ GMMTests : Specification tests
22
+
23
+ Examples
24
+ --------
25
+ >>> from panelbox.gmm import DifferenceGMM
26
+ >>> model = DifferenceGMM(
27
+ ... data=abdata,
28
+ ... dep_var='n',
29
+ ... lags=[1],
30
+ ... exog_vars=['w', 'k'],
31
+ ... time_dummies=True
32
+ ... )
33
+ >>> results = model.fit()
34
+ >>> print(results.summary())
35
+
36
+ References
37
+ ----------
38
+ .. [1] Arellano, M., & Bond, S. (1991). "Some Tests of Specification for Panel
39
+ Data: Monte Carlo Evidence and an Application to Employment Equations."
40
+ Review of Economic Studies, 58(2), 277-297.
41
+
42
+ .. [2] Blundell, R., & Bond, S. (1998). "Initial Conditions and Moment
43
+ Restrictions in Dynamic Panel Data Models." Journal of Econometrics,
44
+ 87(1), 115-143.
45
+
46
+ .. [3] Roodman, D. (2009). "How to do xtabond2: An Introduction to Difference
47
+ and System GMM in Stata." Stata Journal, 9(1), 86-136.
48
+
49
+ .. [4] Windmeijer, F. (2005). "A Finite Sample Correction for the Variance of
50
+ Linear Efficient Two-Step GMM Estimators." Journal of Econometrics,
51
+ 126(1), 25-51.
52
+ """
53
+
54
+ from panelbox.gmm.results import GMMResults, TestResult
55
+ from panelbox.gmm.difference_gmm import DifferenceGMM
56
+ from panelbox.gmm.system_gmm import SystemGMM
57
+
58
+ __all__ = [
59
+ 'DifferenceGMM',
60
+ 'SystemGMM',
61
+ 'GMMResults',
62
+ 'TestResult',
63
+ ]
64
+
65
+ __version__ = '0.1.0'