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,161 @@
1
+ /**
2
+ * PanelBox Reports - Tab Navigation
3
+ * ==================================
4
+ *
5
+ * Handles tab switching for interactive reports.
6
+ * Lightweight vanilla JavaScript implementation.
7
+ *
8
+ * Version: 1.0.0
9
+ */
10
+
11
+ (function() {
12
+ 'use strict';
13
+
14
+ /**
15
+ * Initialize tab navigation for all tab containers
16
+ */
17
+ function initTabs() {
18
+ const tabContainers = document.querySelectorAll('.tabs-container');
19
+
20
+ tabContainers.forEach(container => {
21
+ const tabButtons = container.querySelectorAll('.tab-button');
22
+ const tabContents = container.querySelectorAll('.tab-content');
23
+
24
+ // Set up click handlers
25
+ tabButtons.forEach(button => {
26
+ button.addEventListener('click', function() {
27
+ const targetId = this.dataset.tab;
28
+ switchTab(container, targetId);
29
+ });
30
+ });
31
+
32
+ // Activate first tab by default
33
+ if (tabButtons.length > 0) {
34
+ const firstTab = tabButtons[0].dataset.tab;
35
+ switchTab(container, firstTab);
36
+ }
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Switch to a specific tab
42
+ * @param {HTMLElement} container - The tabs container element
43
+ * @param {string} targetId - The ID of the tab to activate
44
+ */
45
+ function switchTab(container, targetId) {
46
+ const tabButtons = container.querySelectorAll('.tab-button');
47
+ const tabContents = container.querySelectorAll('.tab-content');
48
+
49
+ // Deactivate all tabs
50
+ tabButtons.forEach(btn => btn.classList.remove('active'));
51
+ tabContents.forEach(content => content.classList.remove('active'));
52
+
53
+ // Activate target tab
54
+ const targetButton = container.querySelector(`[data-tab="${targetId}"]`);
55
+ const targetContent = document.getElementById(targetId);
56
+
57
+ if (targetButton && targetContent) {
58
+ targetButton.classList.add('active');
59
+ targetContent.classList.add('active');
60
+
61
+ // Scroll tab into view if needed
62
+ targetButton.scrollIntoView({
63
+ behavior: 'smooth',
64
+ block: 'nearest',
65
+ inline: 'center'
66
+ });
67
+
68
+ // Dispatch custom event for other components
69
+ const event = new CustomEvent('tabChanged', {
70
+ detail: { tabId: targetId }
71
+ });
72
+ container.dispatchEvent(event);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Add keyboard navigation support
78
+ */
79
+ function initKeyboardNavigation() {
80
+ document.addEventListener('keydown', function(e) {
81
+ const activeTab = document.querySelector('.tab-button.active');
82
+
83
+ if (!activeTab) return;
84
+
85
+ const container = activeTab.closest('.tabs-container');
86
+ const tabButtons = Array.from(container.querySelectorAll('.tab-button'));
87
+ const currentIndex = tabButtons.indexOf(activeTab);
88
+
89
+ let nextIndex;
90
+
91
+ // Arrow key navigation
92
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
93
+ e.preventDefault();
94
+ nextIndex = currentIndex > 0 ? currentIndex - 1 : tabButtons.length - 1;
95
+ } else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
96
+ e.preventDefault();
97
+ nextIndex = currentIndex < tabButtons.length - 1 ? currentIndex + 1 : 0;
98
+ } else if (e.key === 'Home') {
99
+ e.preventDefault();
100
+ nextIndex = 0;
101
+ } else if (e.key === 'End') {
102
+ e.preventDefault();
103
+ nextIndex = tabButtons.length - 1;
104
+ }
105
+
106
+ if (nextIndex !== undefined) {
107
+ const nextTab = tabButtons[nextIndex];
108
+ const targetId = nextTab.dataset.tab;
109
+ switchTab(container, targetId);
110
+ nextTab.focus();
111
+ }
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Save/restore tab state in URL hash
117
+ */
118
+ function initHashNavigation() {
119
+ // Restore tab from hash on load
120
+ if (window.location.hash) {
121
+ const tabId = window.location.hash.substring(1);
122
+ const tabButton = document.querySelector(`[data-tab="${tabId}"]`);
123
+ if (tabButton) {
124
+ const container = tabButton.closest('.tabs-container');
125
+ switchTab(container, tabId);
126
+ }
127
+ }
128
+
129
+ // Update hash when tab changes
130
+ document.addEventListener('tabChanged', function(e) {
131
+ const tabId = e.detail.tabId;
132
+ if (history.replaceState) {
133
+ history.replaceState(null, null, '#' + tabId);
134
+ } else {
135
+ window.location.hash = tabId;
136
+ }
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Initialize everything when DOM is ready
142
+ */
143
+ if (document.readyState === 'loading') {
144
+ document.addEventListener('DOMContentLoaded', function() {
145
+ initTabs();
146
+ initKeyboardNavigation();
147
+ initHashNavigation();
148
+ });
149
+ } else {
150
+ initTabs();
151
+ initKeyboardNavigation();
152
+ initHashNavigation();
153
+ }
154
+
155
+ // Export functions for external use
156
+ window.PanelBoxTabs = {
157
+ init: initTabs,
158
+ switchTab: switchTab
159
+ };
160
+
161
+ })();
@@ -0,0 +1,276 @@
1
+ /**
2
+ * PanelBox Reports - Utility Functions
3
+ * =====================================
4
+ *
5
+ * Common utility functions for reports.
6
+ *
7
+ * Version: 1.0.0
8
+ */
9
+
10
+ (function() {
11
+ 'use strict';
12
+
13
+ /**
14
+ * Format a number with specified decimal places
15
+ * @param {number} value - The number to format
16
+ * @param {number} decimals - Number of decimal places
17
+ * @returns {string} Formatted number
18
+ */
19
+ function formatNumber(value, decimals = 3) {
20
+ if (value === null || value === undefined || isNaN(value)) {
21
+ return 'N/A';
22
+ }
23
+ return value.toFixed(decimals);
24
+ }
25
+
26
+ /**
27
+ * Format a p-value with scientific notation if small
28
+ * @param {number} pvalue - The p-value to format
29
+ * @returns {string} Formatted p-value
30
+ */
31
+ function formatPValue(pvalue) {
32
+ if (pvalue === null || pvalue === undefined || isNaN(pvalue)) {
33
+ return 'N/A';
34
+ }
35
+ if (pvalue < 0.001) {
36
+ return pvalue.toExponential(2);
37
+ }
38
+ return pvalue.toFixed(4);
39
+ }
40
+
41
+ /**
42
+ * Format a percentage
43
+ * @param {number} value - The value to format as percentage
44
+ * @param {number} decimals - Number of decimal places
45
+ * @returns {string} Formatted percentage
46
+ */
47
+ function formatPercentage(value, decimals = 2) {
48
+ if (value === null || value === undefined || isNaN(value)) {
49
+ return 'N/A';
50
+ }
51
+ return (value * 100).toFixed(decimals) + '%';
52
+ }
53
+
54
+ /**
55
+ * Add significance stars to a p-value
56
+ * @param {number} pvalue - The p-value
57
+ * @returns {string} Stars string
58
+ */
59
+ function significanceStars(pvalue) {
60
+ if (pvalue < 0.001) return '***';
61
+ if (pvalue < 0.01) return '**';
62
+ if (pvalue < 0.05) return '*';
63
+ if (pvalue < 0.1) return '.';
64
+ return '';
65
+ }
66
+
67
+ /**
68
+ * Copy text to clipboard
69
+ * @param {string} text - Text to copy
70
+ * @returns {Promise<void>}
71
+ */
72
+ async function copyToClipboard(text) {
73
+ try {
74
+ await navigator.clipboard.writeText(text);
75
+ showNotification('Copied to clipboard!', 'success');
76
+ } catch (err) {
77
+ // Fallback for older browsers
78
+ const textarea = document.createElement('textarea');
79
+ textarea.value = text;
80
+ textarea.style.position = 'fixed';
81
+ textarea.style.opacity = '0';
82
+ document.body.appendChild(textarea);
83
+ textarea.select();
84
+ try {
85
+ document.execCommand('copy');
86
+ showNotification('Copied to clipboard!', 'success');
87
+ } catch (err2) {
88
+ showNotification('Failed to copy', 'danger');
89
+ }
90
+ document.body.removeChild(textarea);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Show a temporary notification
96
+ * @param {string} message - Message to show
97
+ * @param {string} type - Type: success, warning, danger, info
98
+ * @param {number} duration - Duration in ms
99
+ */
100
+ function showNotification(message, type = 'info', duration = 3000) {
101
+ const notification = document.createElement('div');
102
+ notification.className = `notification notification-${type}`;
103
+ notification.textContent = message;
104
+ notification.style.cssText = `
105
+ position: fixed;
106
+ top: 20px;
107
+ right: 20px;
108
+ padding: 12px 24px;
109
+ border-radius: 8px;
110
+ background: white;
111
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
112
+ z-index: 9999;
113
+ animation: slideInRight 0.3s ease;
114
+ `;
115
+
116
+ // Set color based on type
117
+ const colors = {
118
+ success: '#10b981',
119
+ warning: '#f59e0b',
120
+ danger: '#ef4444',
121
+ info: '#3b82f6'
122
+ };
123
+ notification.style.borderLeft = `4px solid ${colors[type] || colors.info}`;
124
+
125
+ document.body.appendChild(notification);
126
+
127
+ setTimeout(() => {
128
+ notification.style.animation = 'slideOutRight 0.3s ease';
129
+ setTimeout(() => {
130
+ document.body.removeChild(notification);
131
+ }, 300);
132
+ }, duration);
133
+ }
134
+
135
+ /**
136
+ * Export table to CSV
137
+ * @param {string} tableId - ID of the table element
138
+ * @param {string} filename - Name for the CSV file
139
+ */
140
+ function exportTableToCSV(tableId, filename = 'table.csv') {
141
+ const table = document.getElementById(tableId);
142
+ if (!table) return;
143
+
144
+ const rows = Array.from(table.querySelectorAll('tr'));
145
+ const csv = rows.map(row => {
146
+ const cells = Array.from(row.querySelectorAll('th, td'));
147
+ return cells.map(cell => {
148
+ let text = cell.textContent.trim();
149
+ // Escape quotes and wrap in quotes if contains comma
150
+ if (text.includes(',') || text.includes('"')) {
151
+ text = '"' + text.replace(/"/g, '""') + '"';
152
+ }
153
+ return text;
154
+ }).join(',');
155
+ }).join('\n');
156
+
157
+ // Download CSV
158
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
159
+ const link = document.createElement('a');
160
+ const url = URL.createObjectURL(blob);
161
+ link.setAttribute('href', url);
162
+ link.setAttribute('download', filename);
163
+ link.style.visibility = 'hidden';
164
+ document.body.appendChild(link);
165
+ link.click();
166
+ document.body.removeChild(link);
167
+ URL.revokeObjectURL(url);
168
+
169
+ showNotification('Table exported to CSV', 'success');
170
+ }
171
+
172
+ /**
173
+ * Print the report
174
+ */
175
+ function printReport() {
176
+ window.print();
177
+ }
178
+
179
+ /**
180
+ * Toggle element visibility
181
+ * @param {string} elementId - ID of element to toggle
182
+ */
183
+ function toggleVisibility(elementId) {
184
+ const element = document.getElementById(elementId);
185
+ if (element) {
186
+ element.style.display = element.style.display === 'none' ? '' : 'none';
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Smooth scroll to element
192
+ * @param {string} elementId - ID of element to scroll to
193
+ */
194
+ function scrollToElement(elementId) {
195
+ const element = document.getElementById(elementId);
196
+ if (element) {
197
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' });
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Debounce function
203
+ * @param {Function} func - Function to debounce
204
+ * @param {number} wait - Wait time in ms
205
+ * @returns {Function} Debounced function
206
+ */
207
+ function debounce(func, wait = 300) {
208
+ let timeout;
209
+ return function executedFunction(...args) {
210
+ const later = () => {
211
+ clearTimeout(timeout);
212
+ func(...args);
213
+ };
214
+ clearTimeout(timeout);
215
+ timeout = setTimeout(later, wait);
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Check if element is in viewport
221
+ * @param {HTMLElement} element - Element to check
222
+ * @returns {boolean} True if in viewport
223
+ */
224
+ function isInViewport(element) {
225
+ const rect = element.getBoundingClientRect();
226
+ return (
227
+ rect.top >= 0 &&
228
+ rect.left >= 0 &&
229
+ rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
230
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth)
231
+ );
232
+ }
233
+
234
+ // Add CSS for animations
235
+ const style = document.createElement('style');
236
+ style.textContent = `
237
+ @keyframes slideInRight {
238
+ from {
239
+ transform: translateX(100%);
240
+ opacity: 0;
241
+ }
242
+ to {
243
+ transform: translateX(0);
244
+ opacity: 1;
245
+ }
246
+ }
247
+ @keyframes slideOutRight {
248
+ from {
249
+ transform: translateX(0);
250
+ opacity: 1;
251
+ }
252
+ to {
253
+ transform: translateX(100%);
254
+ opacity: 0;
255
+ }
256
+ }
257
+ `;
258
+ document.head.appendChild(style);
259
+
260
+ // Export utilities
261
+ window.PanelBoxUtils = {
262
+ formatNumber,
263
+ formatPValue,
264
+ formatPercentage,
265
+ significanceStars,
266
+ copyToClipboard,
267
+ showNotification,
268
+ exportTableToCSV,
269
+ printReport,
270
+ toggleVisibility,
271
+ scrollToElement,
272
+ debounce,
273
+ isInViewport
274
+ };
275
+
276
+ })();
@@ -0,0 +1,24 @@
1
+ <div class="report-footer">
2
+ <div class="report-footer-actions no-print">
3
+ <button class="btn btn-secondary btn-sm" onclick="window.print()">
4
+ Print Report
5
+ </button>
6
+ {% if show_export_buttons %}
7
+ <button class="btn btn-secondary btn-sm" onclick="PanelBoxUtils.exportTableToCSV('summary-table', 'panelbox_results.csv')">
8
+ Export to CSV
9
+ </button>
10
+ {% endif %}
11
+ </div>
12
+
13
+ <div class="report-footer-text">
14
+ <p>
15
+ Generated with <strong>PanelBox {{ panelbox_version }}</strong> |
16
+ <a href="https://github.com/panelbox/panelbox" target="_blank">Documentation</a>
17
+ </p>
18
+ <p class="text-xs text-gray-500">
19
+ Report Type: {{ report_type }} |
20
+ Generated: {{ generation_date }} |
21
+ Python {{ python_version }}
22
+ </p>
23
+ </div>
24
+ </div>
@@ -0,0 +1,44 @@
1
+ <div class="report-header">
2
+ <h1 class="report-title">{{ report_title }}</h1>
3
+ {% if report_subtitle %}
4
+ <p class="report-subtitle">{{ report_subtitle }}</p>
5
+ {% endif %}
6
+
7
+ <div class="report-meta">
8
+ <div class="report-meta-item">
9
+ <span class="report-meta-label">Model:</span>
10
+ <span>{{ model_type }}</span>
11
+ </div>
12
+
13
+ {% if formula %}
14
+ <div class="report-meta-item">
15
+ <span class="report-meta-label">Formula:</span>
16
+ <code class="text-sm">{{ formula }}</code>
17
+ </div>
18
+ {% endif %}
19
+
20
+ <div class="report-meta-item">
21
+ <span class="report-meta-label">Observations:</span>
22
+ <span>{{ nobs|number_format }}</span>
23
+ </div>
24
+
25
+ {% if n_entities %}
26
+ <div class="report-meta-item">
27
+ <span class="report-meta-label">Entities:</span>
28
+ <span>{{ n_entities|number_format }}</span>
29
+ </div>
30
+ {% endif %}
31
+
32
+ {% if n_periods %}
33
+ <div class="report-meta-item">
34
+ <span class="report-meta-label">Periods:</span>
35
+ <span>{{ n_periods }}</span>
36
+ </div>
37
+ {% endif %}
38
+
39
+ <div class="report-meta-item">
40
+ <span class="report-meta-label">Generated:</span>
41
+ <span>{{ generation_date }}</span>
42
+ </div>
43
+ </div>
44
+ </div>
@@ -0,0 +1,5 @@
1
+ <meta charset="UTF-8">
2
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
3
+ <meta name="generator" content="PanelBox {{ panelbox_version }}">
4
+ <meta name="description" content="PanelBox {{ report_type }} Report{% if report_title %} - {{ report_title }}{% endif %}">
5
+ <title>{% if report_title %}{{ report_title }} - {% endif %}PanelBox Report</title>