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,341 @@
1
+ """
2
+ Static Validation Renderer.
3
+
4
+ Generates static charts for validation reports using Matplotlib.
5
+ """
6
+
7
+ import base64
8
+ from io import BytesIO
9
+ from typing import Dict, Any, List, Optional, Tuple
10
+ import warnings
11
+
12
+ try:
13
+ import matplotlib
14
+ matplotlib.use('Agg') # Non-interactive backend
15
+ import matplotlib.pyplot as plt
16
+ import numpy as np
17
+ MATPLOTLIB_AVAILABLE = True
18
+ except ImportError:
19
+ MATPLOTLIB_AVAILABLE = False
20
+ warnings.warn(
21
+ "Matplotlib not available. Static charts will not be generated. "
22
+ "Install with: pip install matplotlib"
23
+ )
24
+
25
+
26
+ class StaticValidationRenderer:
27
+ """
28
+ Renders static validation charts using Matplotlib.
29
+
30
+ Creates publication-quality static charts suitable for PDFs,
31
+ printed reports, and non-interactive viewing.
32
+
33
+ Parameters
34
+ ----------
35
+ figure_size : tuple, default=(10, 6)
36
+ Default figure size (width, height) in inches
37
+ dpi : int, default=150
38
+ Resolution in dots per inch
39
+ style : str, default='seaborn-v0_8-darkgrid'
40
+ Matplotlib style to use
41
+
42
+ Examples
43
+ --------
44
+ >>> from panelbox.report.renderers import StaticValidationRenderer
45
+ >>>
46
+ >>> renderer = StaticValidationRenderer(dpi=300)
47
+ >>> charts = renderer.render_validation_charts(validation_data)
48
+ >>> # charts contains base64-encoded PNG images
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ figure_size: Tuple[int, int] = (10, 6),
54
+ dpi: int = 150,
55
+ style: str = 'seaborn-v0_8-darkgrid'
56
+ ):
57
+ """Initialize Static Validation Renderer."""
58
+ if not MATPLOTLIB_AVAILABLE:
59
+ raise ImportError(
60
+ "Matplotlib is required for static chart rendering. "
61
+ "Install with: pip install matplotlib"
62
+ )
63
+
64
+ self.figure_size = figure_size
65
+ self.dpi = dpi
66
+ self.style = style
67
+
68
+ # Set style
69
+ try:
70
+ plt.style.use(style)
71
+ except:
72
+ # Fallback to default if style not available
73
+ plt.style.use('default')
74
+
75
+ def render_validation_charts(
76
+ self,
77
+ validation_data: Dict[str, Any]
78
+ ) -> Dict[str, str]:
79
+ """
80
+ Render all validation charts as base64 PNG images.
81
+
82
+ Parameters
83
+ ----------
84
+ validation_data : dict
85
+ Validation data from ValidationTransformer
86
+
87
+ Returns
88
+ -------
89
+ dict
90
+ Dictionary mapping chart names to base64 image data URIs
91
+
92
+ Examples
93
+ --------
94
+ >>> charts = renderer.render_validation_charts(validation_data)
95
+ >>> overview_img = charts['test_overview']
96
+ >>> # Use in HTML: <img src="{{overview_img}}">
97
+ """
98
+ charts = {}
99
+
100
+ chart_data = validation_data.get('charts', {})
101
+
102
+ if 'test_overview' in chart_data:
103
+ charts['test_overview'] = self._render_test_overview(
104
+ chart_data['test_overview']
105
+ )
106
+
107
+ if 'pvalue_distribution' in chart_data:
108
+ charts['pvalue_distribution'] = self._render_pvalue_distribution(
109
+ chart_data['pvalue_distribution']
110
+ )
111
+
112
+ if 'test_statistics' in chart_data:
113
+ charts['test_statistics'] = self._render_test_statistics(
114
+ chart_data['test_statistics']
115
+ )
116
+
117
+ return charts
118
+
119
+ def _render_test_overview(self, data: Dict[str, Any]) -> str:
120
+ """
121
+ Render test overview bar chart.
122
+
123
+ Parameters
124
+ ----------
125
+ data : dict
126
+ Chart data with categories, passed, and failed counts
127
+
128
+ Returns
129
+ -------
130
+ str
131
+ Base64-encoded PNG image data URI
132
+ """
133
+ fig, ax = plt.subplots(figsize=self.figure_size, dpi=self.dpi)
134
+
135
+ categories = data['categories']
136
+ passed = data['passed']
137
+ failed = data['failed']
138
+
139
+ x = np.arange(len(categories))
140
+ width = 0.35
141
+
142
+ # Stacked bar chart
143
+ ax.bar(x, passed, width, label='Passed', color='#10b981')
144
+ ax.bar(x, failed, width, bottom=passed, label='Failed', color='#ef4444')
145
+
146
+ ax.set_xlabel('Test Category', fontsize=12, fontweight='bold')
147
+ ax.set_ylabel('Number of Tests', fontsize=12, fontweight='bold')
148
+ ax.set_title('Test Results by Category', fontsize=14, fontweight='bold', pad=20)
149
+ ax.set_xticks(x)
150
+ ax.set_xticklabels(categories, rotation=45, ha='right')
151
+ ax.legend(loc='upper right')
152
+ ax.grid(axis='y', alpha=0.3)
153
+
154
+ plt.tight_layout()
155
+
156
+ return self._fig_to_base64(fig)
157
+
158
+ def _render_pvalue_distribution(self, data: Dict[str, Any]) -> str:
159
+ """
160
+ Render p-value distribution chart.
161
+
162
+ Parameters
163
+ ----------
164
+ data : dict
165
+ Chart data with test names and p-values
166
+
167
+ Returns
168
+ -------
169
+ str
170
+ Base64-encoded PNG image data URI
171
+ """
172
+ fig, ax = plt.subplots(figsize=self.figure_size, dpi=self.dpi)
173
+
174
+ test_names = data['test_names']
175
+ pvalues = data['pvalues']
176
+
177
+ x = np.arange(len(test_names))
178
+
179
+ # Color-code by significance
180
+ colors = []
181
+ for pval in pvalues:
182
+ if pval < 0.01:
183
+ colors.append('#ef4444') # Red - highly significant
184
+ elif pval < 0.05:
185
+ colors.append('#f59e0b') # Orange - significant
186
+ elif pval < 0.1:
187
+ colors.append('#eab308') # Yellow - marginally significant
188
+ else:
189
+ colors.append('#10b981') # Green - not significant
190
+
191
+ ax.bar(x, pvalues, color=colors, alpha=0.8, edgecolor='black', linewidth=0.5)
192
+
193
+ # Add significance threshold lines
194
+ ax.axhline(y=0.05, color='red', linestyle='--', linewidth=2, label='α = 0.05')
195
+ ax.axhline(y=0.01, color='darkred', linestyle=':', linewidth=1.5, label='α = 0.01')
196
+
197
+ ax.set_xlabel('Test Name', fontsize=12, fontweight='bold')
198
+ ax.set_ylabel('P-value', fontsize=12, fontweight='bold')
199
+ ax.set_title('P-values by Test', fontsize=14, fontweight='bold', pad=20)
200
+ ax.set_xticks(x)
201
+ ax.set_xticklabels(test_names, rotation=45, ha='right', fontsize=9)
202
+ ax.set_yscale('log')
203
+ ax.legend(loc='upper right')
204
+ ax.grid(axis='y', alpha=0.3, which='both')
205
+
206
+ plt.tight_layout()
207
+
208
+ return self._fig_to_base64(fig)
209
+
210
+ def _render_test_statistics(self, data: Dict[str, Any]) -> str:
211
+ """
212
+ Render test statistics scatter plot.
213
+
214
+ Parameters
215
+ ----------
216
+ data : dict
217
+ Chart data with test names and statistics
218
+
219
+ Returns
220
+ -------
221
+ str
222
+ Base64-encoded PNG image data URI
223
+ """
224
+ fig, ax = plt.subplots(figsize=self.figure_size, dpi=self.dpi)
225
+
226
+ test_names = data['test_names']
227
+ statistics = data['statistics']
228
+
229
+ x = np.arange(len(test_names))
230
+
231
+ ax.scatter(x, statistics, s=100, color='#2563eb', alpha=0.7,
232
+ edgecolors='#1e40af', linewidth=2, zorder=3)
233
+
234
+ ax.set_xlabel('Test Name', fontsize=12, fontweight='bold')
235
+ ax.set_ylabel('Test Statistic', fontsize=12, fontweight='bold')
236
+ ax.set_title('Test Statistics', fontsize=14, fontweight='bold', pad=20)
237
+ ax.set_xticks(x)
238
+ ax.set_xticklabels(test_names, rotation=45, ha='right', fontsize=9)
239
+ ax.grid(axis='y', alpha=0.3)
240
+
241
+ plt.tight_layout()
242
+
243
+ return self._fig_to_base64(fig)
244
+
245
+ def render_summary_chart(
246
+ self,
247
+ summary: Dict[str, Any]
248
+ ) -> str:
249
+ """
250
+ Render summary pie chart.
251
+
252
+ Parameters
253
+ ----------
254
+ summary : dict
255
+ Summary data with total_passed and total_failed
256
+
257
+ Returns
258
+ -------
259
+ str
260
+ Base64-encoded PNG image data URI
261
+
262
+ Examples
263
+ --------
264
+ >>> summary_chart = renderer.render_summary_chart(summary)
265
+ """
266
+ fig, ax = plt.subplots(figsize=(8, 8), dpi=self.dpi)
267
+
268
+ passed = summary['total_passed']
269
+ failed = summary['total_failed']
270
+
271
+ sizes = [passed, failed]
272
+ labels = [f'Passed ({passed})', f'Failed ({failed})']
273
+ colors = ['#10b981', '#ef4444']
274
+ explode = (0.05, 0.05)
275
+
276
+ wedges, texts, autotexts = ax.pie(
277
+ sizes,
278
+ explode=explode,
279
+ labels=labels,
280
+ colors=colors,
281
+ autopct='%1.1f%%',
282
+ shadow=True,
283
+ startangle=90
284
+ )
285
+
286
+ # Enhance text
287
+ for text in texts:
288
+ text.set_fontsize(12)
289
+ text.set_fontweight('bold')
290
+
291
+ for autotext in autotexts:
292
+ autotext.set_color('white')
293
+ autotext.set_fontsize(12)
294
+ autotext.set_fontweight('bold')
295
+
296
+ ax.set_title(
297
+ f'Test Results Summary\n({summary["total_tests"]} total tests)',
298
+ fontsize=14,
299
+ fontweight='bold',
300
+ pad=20
301
+ )
302
+
303
+ plt.tight_layout()
304
+
305
+ return self._fig_to_base64(fig)
306
+
307
+ def _fig_to_base64(self, fig) -> str:
308
+ """
309
+ Convert matplotlib figure to base64 data URI.
310
+
311
+ Parameters
312
+ ----------
313
+ fig : matplotlib.figure.Figure
314
+ Figure to convert
315
+
316
+ Returns
317
+ -------
318
+ str
319
+ Base64-encoded PNG data URI
320
+ """
321
+ buf = BytesIO()
322
+ fig.savefig(buf, format='png', dpi=self.dpi, bbox_inches='tight')
323
+ buf.seek(0)
324
+
325
+ # Encode as base64
326
+ img_base64 = base64.b64encode(buf.read()).decode('utf-8')
327
+
328
+ # Close figure to free memory
329
+ plt.close(fig)
330
+
331
+ # Create data URI
332
+ return f"data:image/png;base64,{img_base64}"
333
+
334
+ def __repr__(self) -> str:
335
+ """String representation."""
336
+ return (
337
+ f"StaticValidationRenderer("
338
+ f"figure_size={self.figure_size}, "
339
+ f"dpi={self.dpi}, "
340
+ f"style='{self.style}')"
341
+ )