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,146 @@
1
+ <section class="report-section">
2
+ <h3 class="section-subtitle">Model Information</h3>
3
+
4
+ <div class="info-grid">
5
+ <div class="info-item">
6
+ <span class="info-label">Model Type:</span>
7
+ <span class="info-value">{{ model_info.model_type }}</span>
8
+ </div>
9
+
10
+ {% if model_info.formula %}
11
+ <div class="info-item">
12
+ <span class="info-label">Formula:</span>
13
+ <code class="info-value">{{ model_info.formula }}</code>
14
+ </div>
15
+ {% endif %}
16
+
17
+ <div class="info-item">
18
+ <span class="info-label">Observations:</span>
19
+ <span class="info-value">{{ model_info.nobs_formatted or model_info.nobs }}</span>
20
+ </div>
21
+
22
+ {% if model_info.n_entities %}
23
+ <div class="info-item">
24
+ <span class="info-label">Entities:</span>
25
+ <span class="info-value">{{ model_info.n_entities_formatted or model_info.n_entities }}</span>
26
+ </div>
27
+ {% endif %}
28
+
29
+ {% if model_info.n_periods %}
30
+ <div class="info-item">
31
+ <span class="info-label">Time Periods:</span>
32
+ <span class="info-value">{{ model_info.n_periods_formatted or model_info.n_periods }}</span>
33
+ </div>
34
+ {% endif %}
35
+
36
+ {% if model_info.balanced %}
37
+ <div class="info-item">
38
+ <span class="info-label">Panel Type:</span>
39
+ <span class="info-value">
40
+ {% if model_info.balanced %}Balanced{% else %}Unbalanced{% endif %}
41
+ </span>
42
+ </div>
43
+ {% endif %}
44
+ </div>
45
+ </section>
46
+
47
+ <section class="report-section mt-6">
48
+ <h3 class="section-subtitle">Tests by Category</h3>
49
+
50
+ <div class="grid grid-cols-2 gap-4">
51
+ <!-- Specification Tests -->
52
+ <div class="test-category-card">
53
+ <h4 class="test-category-title">Specification Tests</h4>
54
+ <div class="test-category-count">
55
+ {{ summary.failed_by_category.specification }} /
56
+ {{ tests|selectattr('category', 'equalto', 'Specification')|list|length }} failed
57
+ </div>
58
+ <div class="test-category-status">
59
+ {% if summary.failed_by_category.specification == 0 %}
60
+ <span class="badge badge-success">All Passed</span>
61
+ {% else %}
62
+ <span class="badge badge-danger">Issues Detected</span>
63
+ {% endif %}
64
+ </div>
65
+ </div>
66
+
67
+ <!-- Serial Correlation Tests -->
68
+ <div class="test-category-card">
69
+ <h4 class="test-category-title">Serial Correlation</h4>
70
+ <div class="test-category-count">
71
+ {{ summary.failed_by_category.serial }} /
72
+ {{ tests|selectattr('category', 'equalto', 'Serial Correlation')|list|length }} failed
73
+ </div>
74
+ <div class="test-category-status">
75
+ {% if summary.failed_by_category.serial == 0 %}
76
+ <span class="badge badge-success">All Passed</span>
77
+ {% else %}
78
+ <span class="badge badge-danger">Issues Detected</span>
79
+ {% endif %}
80
+ </div>
81
+ </div>
82
+
83
+ <!-- Heteroskedasticity Tests -->
84
+ <div class="test-category-card">
85
+ <h4 class="test-category-title">Heteroskedasticity</h4>
86
+ <div class="test-category-count">
87
+ {{ summary.failed_by_category.heteroskedasticity }} /
88
+ {{ tests|selectattr('category', 'equalto', 'Heteroskedasticity')|list|length }} failed
89
+ </div>
90
+ <div class="test-category-status">
91
+ {% if summary.failed_by_category.heteroskedasticity == 0 %}
92
+ <span class="badge badge-success">All Passed</span>
93
+ {% else %}
94
+ <span class="badge badge-danger">Issues Detected</span>
95
+ {% endif %}
96
+ </div>
97
+ </div>
98
+
99
+ <!-- Cross-Sectional Dependence Tests -->
100
+ <div class="test-category-card">
101
+ <h4 class="test-category-title">Cross-Sectional Dependence</h4>
102
+ <div class="test-category-count">
103
+ {{ summary.failed_by_category.cross_sectional }} /
104
+ {{ tests|selectattr('category', 'equalto', 'Cross-Sectional Dependence')|list|length }} failed
105
+ </div>
106
+ <div class="test-category-status">
107
+ {% if summary.failed_by_category.cross_sectional == 0 %}
108
+ <span class="badge badge-success">All Passed</span>
109
+ {% else %}
110
+ <span class="badge badge-danger">Issues Detected</span>
111
+ {% endif %}
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </section>
116
+
117
+ <section class="report-section mt-6">
118
+ <h3 class="section-subtitle">Quick Summary</h3>
119
+
120
+ {% if summary.has_issues %}
121
+ <div class="summary-box summary-box-warning">
122
+ <h4>Issues Detected</h4>
123
+ <p>Your model has failed {{ summary.total_failed }} validation test(s). Review the detailed results and recommendations to address these issues.</p>
124
+
125
+ <ul class="mt-3">
126
+ {% if summary.failed_by_category.specification > 0 %}
127
+ <li><strong>Specification:</strong> {{ summary.failed_by_category.specification }} test(s) failed</li>
128
+ {% endif %}
129
+ {% if summary.failed_by_category.serial > 0 %}
130
+ <li><strong>Serial Correlation:</strong> {{ summary.failed_by_category.serial }} test(s) failed</li>
131
+ {% endif %}
132
+ {% if summary.failed_by_category.heteroskedasticity > 0 %}
133
+ <li><strong>Heteroskedasticity:</strong> {{ summary.failed_by_category.heteroskedasticity }} test(s) failed</li>
134
+ {% endif %}
135
+ {% if summary.failed_by_category.cross_sectional > 0 %}
136
+ <li><strong>Cross-Sectional Dependence:</strong> {{ summary.failed_by_category.cross_sectional }} test(s) failed</li>
137
+ {% endif %}
138
+ </ul>
139
+ </div>
140
+ {% else %}
141
+ <div class="summary-box summary-box-success">
142
+ <h4>All Tests Passed</h4>
143
+ <p>Your panel data model has successfully passed all {{ summary.total_tests }} validation tests. The model specification appears appropriate and the data shows no major violations of panel data assumptions.</p>
144
+ </div>
145
+ {% endif %}
146
+ </section>
@@ -0,0 +1,101 @@
1
+ <section class="report-section">
2
+ <h3 class="section-subtitle">Recommendations</h3>
3
+
4
+ <p class="text-muted mb-6">
5
+ Based on the validation test results, here are specific recommendations to address detected issues.
6
+ Each recommendation includes actionable suggestions to improve your panel data model.
7
+ </p>
8
+
9
+ {% for rec in recommendations %}
10
+ <div class="recommendation-card recommendation-{{ rec.severity }} mb-6">
11
+ <div class="recommendation-header">
12
+ <div class="recommendation-category">
13
+ <span class="recommendation-icon">
14
+ {% if rec.severity == 'critical' %}⚠️{% elif rec.severity == 'high' %}🔴{% elif rec.severity == 'medium' %}🟡{% else %}🔵{% endif %}
15
+ </span>
16
+ <span class="recommendation-category-text">{{ rec.category }}</span>
17
+ </div>
18
+ <span class="badge badge-{{ rec.severity }}">{{ rec.severity|upper }}</span>
19
+ </div>
20
+
21
+ <div class="recommendation-body">
22
+ <h4 class="recommendation-title">{{ rec.issue }}</h4>
23
+
24
+ <div class="recommendation-tests">
25
+ <strong>Failed Tests:</strong>
26
+ <ul class="test-list">
27
+ {% for test in rec.tests %}
28
+ <li>{{ test }}</li>
29
+ {% endfor %}
30
+ </ul>
31
+ </div>
32
+
33
+ <div class="recommendation-suggestions">
34
+ <strong>Suggested Actions:</strong>
35
+ <ol class="suggestion-list">
36
+ {% for suggestion in rec.suggestions %}
37
+ <li>{{ suggestion }}</li>
38
+ {% endfor %}
39
+ </ol>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ {% endfor %}
44
+
45
+ <!-- General Best Practices -->
46
+ <div class="info-box mt-8">
47
+ <h4 class="info-box-title">General Best Practices</h4>
48
+ <div class="info-box-content">
49
+ <ul class="info-box-list">
50
+ <li>
51
+ <strong>Robust Standard Errors:</strong>
52
+ Always consider using robust standard errors (clustered, HAC, or Driscoll-Kraay) to
53
+ account for potential violations of i.i.d. assumptions.
54
+ </li>
55
+ <li>
56
+ <strong>Specification Testing:</strong>
57
+ Run specification tests (Hausman, Mundlak) to validate your choice between Fixed Effects
58
+ and Random Effects models.
59
+ </li>
60
+ <li>
61
+ <strong>Time Effects:</strong>
62
+ Consider including time fixed effects to control for common shocks and time trends that
63
+ affect all entities.
64
+ </li>
65
+ <li>
66
+ <strong>Model Diagnostics:</strong>
67
+ Examine residual plots and influential observations to identify outliers and
68
+ potential model misspecification.
69
+ </li>
70
+ <li>
71
+ <strong>Theory-Driven Specification:</strong>
72
+ Base your model specification on economic theory and prior research, not just
73
+ statistical tests.
74
+ </li>
75
+ </ul>
76
+ </div>
77
+ </div>
78
+
79
+ <!-- References -->
80
+ <div class="references-box mt-6">
81
+ <h4 class="references-title">Further Reading</h4>
82
+ <ul class="references-list">
83
+ <li>
84
+ Wooldridge, J. M. (2010). <em>Econometric Analysis of Cross Section and Panel Data</em> (2nd ed.).
85
+ MIT Press.
86
+ </li>
87
+ <li>
88
+ Baltagi, B. H. (2021). <em>Econometric Analysis of Panel Data</em> (6th ed.).
89
+ Springer.
90
+ </li>
91
+ <li>
92
+ Cameron, A. C., & Trivedi, P. K. (2005). <em>Microeconometrics: Methods and Applications</em>.
93
+ Cambridge University Press.
94
+ </li>
95
+ <li>
96
+ Pesaran, M. H. (2015). Testing Weak Cross-Sectional Dependence in Large Panels.
97
+ <em>Econometric Reviews</em>, 34(6-10), 1089-1117.
98
+ </li>
99
+ </ul>
100
+ </div>
101
+ </section>
@@ -0,0 +1,231 @@
1
+ <section class="report-section">
2
+ <h3 class="section-subtitle">Detailed Test Results</h3>
3
+
4
+ <p class="text-muted mb-4">
5
+ This table shows detailed results for all {{ tests|length }} validation tests performed on your panel data model.
6
+ Tests marked as "REJECT" indicate potential issues that may require attention.
7
+ </p>
8
+
9
+ <!-- Specification Tests -->
10
+ {% set spec_tests = tests|selectattr('category', 'equalto', 'Specification')|list %}
11
+ {% if spec_tests %}
12
+ <div class="test-group mb-6">
13
+ <h4 class="test-group-title">Specification Tests</h4>
14
+ <p class="test-group-description">
15
+ Tests for model specification, including choice between Fixed Effects and Random Effects.
16
+ </p>
17
+
18
+ <div class="table-container">
19
+ <table class="data-table">
20
+ <thead>
21
+ <tr>
22
+ <th>Test Name</th>
23
+ <th>Statistic</th>
24
+ <th>P-value</th>
25
+ <th>DF</th>
26
+ <th>Result</th>
27
+ <th>Conclusion</th>
28
+ </tr>
29
+ </thead>
30
+ <tbody>
31
+ {% for test in spec_tests %}
32
+ <tr class="test-row test-row-{{ test.result_class }}">
33
+ <td class="font-medium">{{ test.name }}</td>
34
+ <td>{{ test.statistic_formatted }}</td>
35
+ <td>
36
+ {{ test.pvalue_formatted }}
37
+ {% if test.significance %}
38
+ <span class="significance-stars">{{ test.significance }}</span>
39
+ {% endif %}
40
+ </td>
41
+ <td>{{ test.df or 'N/A' }}</td>
42
+ <td>
43
+ <span class="badge badge-{{ test.result_class }}">
44
+ {{ test.result }}
45
+ </span>
46
+ </td>
47
+ <td class="text-sm">{{ test.conclusion }}</td>
48
+ </tr>
49
+ {% endfor %}
50
+ </tbody>
51
+ </table>
52
+ </div>
53
+ </div>
54
+ {% endif %}
55
+
56
+ <!-- Serial Correlation Tests -->
57
+ {% set serial_tests = tests|selectattr('category', 'equalto', 'Serial Correlation')|list %}
58
+ {% if serial_tests %}
59
+ <div class="test-group mb-6">
60
+ <h4 class="test-group-title">Serial Correlation Tests</h4>
61
+ <p class="test-group-description">
62
+ Tests for autocorrelation in residuals across time periods.
63
+ </p>
64
+
65
+ <div class="table-container">
66
+ <table class="data-table">
67
+ <thead>
68
+ <tr>
69
+ <th>Test Name</th>
70
+ <th>Statistic</th>
71
+ <th>P-value</th>
72
+ <th>DF</th>
73
+ <th>Result</th>
74
+ <th>Conclusion</th>
75
+ </tr>
76
+ </thead>
77
+ <tbody>
78
+ {% for test in serial_tests %}
79
+ <tr class="test-row test-row-{{ test.result_class }}">
80
+ <td class="font-medium">{{ test.name }}</td>
81
+ <td>{{ test.statistic_formatted }}</td>
82
+ <td>
83
+ {{ test.pvalue_formatted }}
84
+ {% if test.significance %}
85
+ <span class="significance-stars">{{ test.significance }}</span>
86
+ {% endif %}
87
+ </td>
88
+ <td>{{ test.df or 'N/A' }}</td>
89
+ <td>
90
+ <span class="badge badge-{{ test.result_class }}">
91
+ {{ test.result }}
92
+ </span>
93
+ </td>
94
+ <td class="text-sm">{{ test.conclusion }}</td>
95
+ </tr>
96
+ {% endfor %}
97
+ </tbody>
98
+ </table>
99
+ </div>
100
+ </div>
101
+ {% endif %}
102
+
103
+ <!-- Heteroskedasticity Tests -->
104
+ {% set het_tests = tests|selectattr('category', 'equalto', 'Heteroskedasticity')|list %}
105
+ {% if het_tests %}
106
+ <div class="test-group mb-6">
107
+ <h4 class="test-group-title">Heteroskedasticity Tests</h4>
108
+ <p class="test-group-description">
109
+ Tests for non-constant variance in residuals.
110
+ </p>
111
+
112
+ <div class="table-container">
113
+ <table class="data-table">
114
+ <thead>
115
+ <tr>
116
+ <th>Test Name</th>
117
+ <th>Statistic</th>
118
+ <th>P-value</th>
119
+ <th>DF</th>
120
+ <th>Result</th>
121
+ <th>Conclusion</th>
122
+ </tr>
123
+ </thead>
124
+ <tbody>
125
+ {% for test in het_tests %}
126
+ <tr class="test-row test-row-{{ test.result_class }}">
127
+ <td class="font-medium">{{ test.name }}</td>
128
+ <td>{{ test.statistic_formatted }}</td>
129
+ <td>
130
+ {{ test.pvalue_formatted }}
131
+ {% if test.significance %}
132
+ <span class="significance-stars">{{ test.significance }}</span>
133
+ {% endif %}
134
+ </td>
135
+ <td>{{ test.df or 'N/A' }}</td>
136
+ <td>
137
+ <span class="badge badge-{{ test.result_class }}">
138
+ {{ test.result }}
139
+ </span>
140
+ </td>
141
+ <td class="text-sm">{{ test.conclusion }}</td>
142
+ </tr>
143
+ {% endfor %}
144
+ </tbody>
145
+ </table>
146
+ </div>
147
+ </div>
148
+ {% endif %}
149
+
150
+ <!-- Cross-Sectional Dependence Tests -->
151
+ {% set cd_tests = tests|selectattr('category', 'equalto', 'Cross-Sectional Dependence')|list %}
152
+ {% if cd_tests %}
153
+ <div class="test-group mb-6">
154
+ <h4 class="test-group-title">Cross-Sectional Dependence Tests</h4>
155
+ <p class="test-group-description">
156
+ Tests for correlation across entities (cross-sectional units).
157
+ </p>
158
+
159
+ <div class="table-container">
160
+ <table class="data-table">
161
+ <thead>
162
+ <tr>
163
+ <th>Test Name</th>
164
+ <th>Statistic</th>
165
+ <th>P-value</th>
166
+ <th>DF</th>
167
+ <th>Result</th>
168
+ <th>Conclusion</th>
169
+ </tr>
170
+ </thead>
171
+ <tbody>
172
+ {% for test in cd_tests %}
173
+ <tr class="test-row test-row-{{ test.result_class }}">
174
+ <td class="font-medium">{{ test.name }}</td>
175
+ <td>{{ test.statistic_formatted }}</td>
176
+ <td>
177
+ {{ test.pvalue_formatted }}
178
+ {% if test.significance %}
179
+ <span class="significance-stars">{{ test.significance }}</span>
180
+ {% endif %}
181
+ </td>
182
+ <td>{{ test.df or 'N/A' }}</td>
183
+ <td>
184
+ <span class="badge badge-{{ test.result_class }}">
185
+ {{ test.result }}
186
+ </span>
187
+ </td>
188
+ <td class="text-sm">{{ test.conclusion }}</td>
189
+ </tr>
190
+ {% endfor %}
191
+ </tbody>
192
+ </table>
193
+ </div>
194
+ </div>
195
+ {% endif %}
196
+
197
+ <!-- Legend -->
198
+ <div class="legend-box mt-6">
199
+ <h4 class="legend-title">Result Legend</h4>
200
+ <div class="legend-items">
201
+ <div class="legend-item">
202
+ <span class="badge badge-accept">ACCEPT</span>
203
+ <span class="legend-text">Test passed - no issues detected</span>
204
+ </div>
205
+ <div class="legend-item">
206
+ <span class="badge badge-reject">REJECT</span>
207
+ <span class="legend-text">Test failed - potential issue detected</span>
208
+ </div>
209
+ </div>
210
+
211
+ <h4 class="legend-title mt-4">Significance Levels</h4>
212
+ <div class="legend-items">
213
+ <div class="legend-item">
214
+ <span class="significance-stars">***</span>
215
+ <span class="legend-text">p < 0.001</span>
216
+ </div>
217
+ <div class="legend-item">
218
+ <span class="significance-stars">**</span>
219
+ <span class="legend-text">p < 0.01</span>
220
+ </div>
221
+ <div class="legend-item">
222
+ <span class="significance-stars">*</span>
223
+ <span class="legend-text">p < 0.05</span>
224
+ </div>
225
+ <div class="legend-item">
226
+ <span class="significance-stars">.</span>
227
+ <span class="legend-text">p < 0.1</span>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </section>
File without changes
@@ -0,0 +1,172 @@
1
+ """
2
+ Formatting utilities for output display.
3
+
4
+ This module provides functions for formatting statistical output
5
+ in a readable, publication-quality format.
6
+ """
7
+
8
+ from typing import Union
9
+
10
+
11
+ def format_pvalue(pvalue: float, digits: int = 4) -> str:
12
+ """
13
+ Format p-value for display.
14
+
15
+ Parameters
16
+ ----------
17
+ pvalue : float
18
+ p-value
19
+ digits : int, default=4
20
+ Number of decimal places
21
+
22
+ Returns
23
+ -------
24
+ str
25
+ Formatted p-value string
26
+
27
+ Examples
28
+ --------
29
+ >>> format_pvalue(0.0001)
30
+ '<0.0001'
31
+ >>> format_pvalue(0.1234)
32
+ '0.1234'
33
+ """
34
+ threshold = 10 ** (-digits)
35
+
36
+ if pvalue < threshold:
37
+ return f"<{threshold:.{digits}f}"
38
+ else:
39
+ return f"{pvalue:.{digits}f}"
40
+
41
+
42
+ def format_number(
43
+ value: Union[int, float],
44
+ decimals: int = 4,
45
+ width: int = 10
46
+ ) -> str:
47
+ """
48
+ Format number for tabular display.
49
+
50
+ Parameters
51
+ ----------
52
+ value : int or float
53
+ Number to format
54
+ decimals : int, default=4
55
+ Number of decimal places
56
+ width : int, default=10
57
+ Total width of formatted string
58
+
59
+ Returns
60
+ -------
61
+ str
62
+ Formatted number string
63
+ """
64
+ if isinstance(value, int):
65
+ return f"{value:>{width},}"
66
+ else:
67
+ return f"{value:>{width}.{decimals}f}"
68
+
69
+
70
+ def significance_stars(pvalue: float) -> str:
71
+ """
72
+ Return significance stars based on p-value.
73
+
74
+ Parameters
75
+ ----------
76
+ pvalue : float
77
+ p-value
78
+
79
+ Returns
80
+ -------
81
+ str
82
+ Significance stars
83
+
84
+ Examples
85
+ --------
86
+ >>> significance_stars(0.0001)
87
+ '***'
88
+ >>> significance_stars(0.01)
89
+ '**'
90
+ >>> significance_stars(0.04)
91
+ '*'
92
+ >>> significance_stars(0.08)
93
+ '.'
94
+ >>> significance_stars(0.20)
95
+ ''
96
+ """
97
+ if pvalue < 0.001:
98
+ return '***'
99
+ elif pvalue < 0.01:
100
+ return '**'
101
+ elif pvalue < 0.05:
102
+ return '*'
103
+ elif pvalue < 0.10:
104
+ return '.'
105
+ else:
106
+ return ''
107
+
108
+
109
+ def format_coefficient_table(
110
+ params,
111
+ std_errors,
112
+ tvalues,
113
+ pvalues,
114
+ conf_int=None
115
+ ) -> str:
116
+ """
117
+ Format coefficient table for display.
118
+
119
+ Parameters
120
+ ----------
121
+ params : pd.Series
122
+ Coefficients
123
+ std_errors : pd.Series
124
+ Standard errors
125
+ tvalues : pd.Series
126
+ t-statistics
127
+ pvalues : pd.Series
128
+ p-values
129
+ conf_int : pd.DataFrame, optional
130
+ Confidence intervals
131
+
132
+ Returns
133
+ -------
134
+ str
135
+ Formatted table
136
+ """
137
+ lines = []
138
+
139
+ # Header
140
+ if conf_int is not None:
141
+ lines.append(
142
+ f"{'Variable':<15} {'Coef.':<12} {'Std.Err.':<12} {'t':<8} "
143
+ f"{'P>|t|':<8} {'[0.025':<10} {'0.975]':<10}"
144
+ )
145
+ else:
146
+ lines.append(
147
+ f"{'Variable':<15} {'Coef.':<12} {'Std.Err.':<12} {'t':<8} {'P>|t|':<8}"
148
+ )
149
+
150
+ lines.append("-" * 78)
151
+
152
+ # Rows
153
+ for var in params.index:
154
+ coef = params[var]
155
+ se = std_errors[var]
156
+ t = tvalues[var]
157
+ p = pvalues[var]
158
+ stars = significance_stars(p)
159
+
160
+ if conf_int is not None:
161
+ ci_lower = conf_int.loc[var, 'lower']
162
+ ci_upper = conf_int.loc[var, 'upper']
163
+ lines.append(
164
+ f"{var:<15} {coef:>11.4f} {se:>11.4f} {t:>7.3f} "
165
+ f"{p:>7.4f} {ci_lower:>9.4f} {ci_upper:>9.4f} {stars}"
166
+ )
167
+ else:
168
+ lines.append(
169
+ f"{var:<15} {coef:>11.4f} {se:>11.4f} {t:>7.3f} {p:>7.4f} {stars}"
170
+ )
171
+
172
+ return "\n".join(lines)