aigroup-econ-mcp 1.4.3__py3-none-any.whl → 2.0.1__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.
- PKG-INFO +344 -322
- README.md +335 -320
- __init__.py +1 -1
- aigroup_econ_mcp-2.0.1.dist-info/METADATA +732 -0
- aigroup_econ_mcp-2.0.1.dist-info/RECORD +170 -0
- cli.py +4 -0
- econometrics/advanced_methods/modern_computing_machine_learning/__init__.py +30 -0
- econometrics/advanced_methods/modern_computing_machine_learning/causal_forest.py +253 -0
- econometrics/advanced_methods/modern_computing_machine_learning/double_ml.py +268 -0
- econometrics/advanced_methods/modern_computing_machine_learning/gradient_boosting.py +249 -0
- econometrics/advanced_methods/modern_computing_machine_learning/hierarchical_clustering.py +243 -0
- econometrics/advanced_methods/modern_computing_machine_learning/kmeans_clustering.py +293 -0
- econometrics/advanced_methods/modern_computing_machine_learning/neural_network.py +264 -0
- econometrics/advanced_methods/modern_computing_machine_learning/random_forest.py +195 -0
- econometrics/advanced_methods/modern_computing_machine_learning/support_vector_machine.py +226 -0
- econometrics/advanced_methods/modern_computing_machine_learning/test_all_modules.py +329 -0
- econometrics/advanced_methods/modern_computing_machine_learning/test_report.md +107 -0
- econometrics/causal_inference/__init__.py +66 -0
- econometrics/causal_inference/causal_identification_strategy/__init__.py +104 -0
- econometrics/causal_inference/causal_identification_strategy/control_function.py +112 -0
- econometrics/causal_inference/causal_identification_strategy/difference_in_differences.py +107 -0
- econometrics/causal_inference/causal_identification_strategy/event_study.py +119 -0
- econometrics/causal_inference/causal_identification_strategy/first_difference.py +89 -0
- econometrics/causal_inference/causal_identification_strategy/fixed_effects.py +103 -0
- econometrics/causal_inference/causal_identification_strategy/hausman_test.py +69 -0
- econometrics/causal_inference/causal_identification_strategy/instrumental_variables.py +145 -0
- econometrics/causal_inference/causal_identification_strategy/mediation_analysis.py +121 -0
- econometrics/causal_inference/causal_identification_strategy/moderation_analysis.py +109 -0
- econometrics/causal_inference/causal_identification_strategy/propensity_score_matching.py +140 -0
- econometrics/causal_inference/causal_identification_strategy/random_effects.py +100 -0
- econometrics/causal_inference/causal_identification_strategy/regression_discontinuity.py +98 -0
- econometrics/causal_inference/causal_identification_strategy/synthetic_control.py +111 -0
- econometrics/causal_inference/causal_identification_strategy/triple_difference.py +86 -0
- econometrics/distribution_analysis/__init__.py +28 -0
- econometrics/distribution_analysis/oaxaca_blinder.py +184 -0
- econometrics/distribution_analysis/time_series_decomposition.py +152 -0
- econometrics/distribution_analysis/variance_decomposition.py +179 -0
- econometrics/missing_data/__init__.py +18 -0
- econometrics/missing_data/imputation_methods.py +219 -0
- econometrics/nonparametric/__init__.py +35 -0
- econometrics/nonparametric/gam_model.py +117 -0
- econometrics/nonparametric/kernel_regression.py +161 -0
- econometrics/nonparametric/quantile_regression.py +249 -0
- econometrics/nonparametric/spline_regression.py +100 -0
- econometrics/spatial_econometrics/__init__.py +68 -0
- econometrics/spatial_econometrics/geographically_weighted_regression.py +211 -0
- econometrics/spatial_econometrics/gwr_simple.py +154 -0
- econometrics/spatial_econometrics/spatial_autocorrelation.py +356 -0
- econometrics/spatial_econometrics/spatial_durbin_model.py +177 -0
- econometrics/spatial_econometrics/spatial_regression.py +315 -0
- econometrics/spatial_econometrics/spatial_weights.py +226 -0
- econometrics/specific_data_modeling/micro_discrete_limited_data/README.md +164 -0
- econometrics/specific_data_modeling/micro_discrete_limited_data/__init__.py +40 -0
- econometrics/specific_data_modeling/micro_discrete_limited_data/count_data_models.py +311 -0
- econometrics/specific_data_modeling/micro_discrete_limited_data/discrete_choice_models.py +294 -0
- econometrics/specific_data_modeling/micro_discrete_limited_data/limited_dependent_variable_models.py +282 -0
- econometrics/statistical_inference/__init__.py +21 -0
- econometrics/statistical_inference/bootstrap_methods.py +162 -0
- econometrics/statistical_inference/permutation_test.py +177 -0
- econometrics/survival_analysis/__init__.py +18 -0
- econometrics/survival_analysis/survival_models.py +259 -0
- econometrics/tests/causal_inference_tests/__init__.py +3 -0
- econometrics/tests/causal_inference_tests/detailed_test.py +441 -0
- econometrics/tests/causal_inference_tests/test_all_methods.py +418 -0
- econometrics/tests/causal_inference_tests/test_causal_identification_strategy.py +202 -0
- econometrics/tests/causal_inference_tests/test_difference_in_differences.py +53 -0
- econometrics/tests/causal_inference_tests/test_instrumental_variables.py +44 -0
- econometrics/tests/specific_data_modeling_tests/test_micro_discrete_limited_data.py +189 -0
- econometrics//321/206/320/254/320/272/321/205/342/225/235/320/220/321/205/320/237/320/241/321/205/320/264/320/267/321/207/342/226/222/342/225/227/321/204/342/225/235/320/250/321/205/320/225/320/230/321/207/342/225/221/320/267/321/205/320/230/320/226/321/206/320/256/320/240.md +544 -0
- pyproject.toml +9 -2
- server.py +15 -1
- tools/__init__.py +75 -1
- tools/causal_inference_adapter.py +658 -0
- tools/distribution_analysis_adapter.py +121 -0
- tools/gwr_simple_adapter.py +54 -0
- tools/machine_learning_adapter.py +567 -0
- tools/mcp_tool_groups/__init__.py +15 -1
- tools/mcp_tool_groups/causal_inference_tools.py +643 -0
- tools/mcp_tool_groups/distribution_analysis_tools.py +169 -0
- tools/mcp_tool_groups/machine_learning_tools.py +422 -0
- tools/mcp_tool_groups/microecon_tools.py +325 -0
- tools/mcp_tool_groups/missing_data_tools.py +117 -0
- tools/mcp_tool_groups/nonparametric_tools.py +225 -0
- tools/mcp_tool_groups/spatial_econometrics_tools.py +323 -0
- tools/mcp_tool_groups/statistical_inference_tools.py +131 -0
- tools/mcp_tools_registry.py +13 -3
- tools/microecon_adapter.py +412 -0
- tools/missing_data_adapter.py +73 -0
- tools/nonparametric_adapter.py +190 -0
- tools/spatial_econometrics_adapter.py +318 -0
- tools/statistical_inference_adapter.py +90 -0
- tools/survival_analysis_adapter.py +46 -0
- aigroup_econ_mcp-1.4.3.dist-info/METADATA +0 -710
- aigroup_econ_mcp-1.4.3.dist-info/RECORD +0 -92
- {aigroup_econ_mcp-1.4.3.dist-info → aigroup_econ_mcp-2.0.1.dist-info}/WHEEL +0 -0
- {aigroup_econ_mcp-1.4.3.dist-info → aigroup_econ_mcp-2.0.1.dist-info}/entry_points.txt +0 -0
- {aigroup_econ_mcp-1.4.3.dist-info → aigroup_econ_mcp-2.0.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Microeconometrics Adapter for Econometrics MCP Tools
|
|
3
|
+
Provides unified interfaces for discrete choice, count data, and limited dependent variable models
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from typing import Union, Optional, Dict, Any, List
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
# Import microeconometrics modules
|
|
13
|
+
from econometrics.specific_data_modeling.micro_discrete_limited_data import (
|
|
14
|
+
LogitModel,
|
|
15
|
+
ProbitModel,
|
|
16
|
+
MultinomialLogit,
|
|
17
|
+
OrderedLogit,
|
|
18
|
+
ConditionalLogit,
|
|
19
|
+
PoissonModel,
|
|
20
|
+
NegativeBinomialModel,
|
|
21
|
+
ZeroInflatedPoissonModel,
|
|
22
|
+
ZeroInflatedNegativeBinomialModel,
|
|
23
|
+
TobitModel,
|
|
24
|
+
HeckmanModel
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from tools.data_loader import DataLoader
|
|
28
|
+
|
|
29
|
+
# Set up logging
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def logit_adapter(
|
|
34
|
+
X_data: Optional[Union[List[float], List[List[float]]]] = None,
|
|
35
|
+
y_data: Optional[List[int]] = None,
|
|
36
|
+
file_path: Optional[str] = None,
|
|
37
|
+
feature_names: Optional[List[str]] = None,
|
|
38
|
+
output_format: str = 'json',
|
|
39
|
+
save_path: Optional[str] = None
|
|
40
|
+
) -> str:
|
|
41
|
+
"""Logistic regression adapter"""
|
|
42
|
+
try:
|
|
43
|
+
if file_path:
|
|
44
|
+
data = DataLoader.load_from_file(file_path)
|
|
45
|
+
X_data = data.get('x_data', data.get('X', data.get('features')))
|
|
46
|
+
y_data = data.get('y_data', data.get('y', data.get('target')))
|
|
47
|
+
if feature_names is None:
|
|
48
|
+
feature_names = data.get('feature_names')
|
|
49
|
+
|
|
50
|
+
if X_data is None or y_data is None:
|
|
51
|
+
raise ValueError("X_data and y_data must be provided")
|
|
52
|
+
|
|
53
|
+
X = np.array(X_data)
|
|
54
|
+
y = np.array(y_data)
|
|
55
|
+
|
|
56
|
+
if X.ndim == 1:
|
|
57
|
+
X = X.reshape(-1, 1)
|
|
58
|
+
|
|
59
|
+
model = LogitModel()
|
|
60
|
+
model.fit(X, y)
|
|
61
|
+
|
|
62
|
+
results = model.results_
|
|
63
|
+
formatted_results = {
|
|
64
|
+
'model_type': 'logit',
|
|
65
|
+
'coefficients': results.params.tolist(),
|
|
66
|
+
'std_errors': results.bse.tolist(),
|
|
67
|
+
'z_values': results.tvalues.tolist(),
|
|
68
|
+
'p_values': results.pvalues.tolist(),
|
|
69
|
+
'pseudo_r_squared': float(results.prsquared),
|
|
70
|
+
'log_likelihood': float(results.llf),
|
|
71
|
+
'aic': float(results.aic),
|
|
72
|
+
'bic': float(results.bic),
|
|
73
|
+
'n_obs': int(results.nobs),
|
|
74
|
+
'feature_names': feature_names or [f'X{i+1}' for i in range(X.shape[1])]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return json.dumps(formatted_results, indent=2, ensure_ascii=False)
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
logger.error(f"Logit failed: {str(e)}")
|
|
81
|
+
return json.dumps({'error': str(e)}, indent=2, ensure_ascii=False)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def probit_adapter(
|
|
85
|
+
X_data: Optional[Union[List[float], List[List[float]]]] = None,
|
|
86
|
+
y_data: Optional[List[int]] = None,
|
|
87
|
+
file_path: Optional[str] = None,
|
|
88
|
+
feature_names: Optional[List[str]] = None,
|
|
89
|
+
output_format: str = 'json',
|
|
90
|
+
save_path: Optional[str] = None
|
|
91
|
+
) -> str:
|
|
92
|
+
"""Probit regression adapter"""
|
|
93
|
+
try:
|
|
94
|
+
if file_path:
|
|
95
|
+
data = DataLoader.load_from_file(file_path)
|
|
96
|
+
X_data = data.get('x_data', data.get('X', data.get('features')))
|
|
97
|
+
y_data = data.get('y_data', data.get('y', data.get('target')))
|
|
98
|
+
if feature_names is None:
|
|
99
|
+
feature_names = data.get('feature_names')
|
|
100
|
+
|
|
101
|
+
if X_data is None or y_data is None:
|
|
102
|
+
raise ValueError("X_data and y_data must be provided")
|
|
103
|
+
|
|
104
|
+
X = np.array(X_data)
|
|
105
|
+
y = np.array(y_data)
|
|
106
|
+
|
|
107
|
+
if X.ndim == 1:
|
|
108
|
+
X = X.reshape(-1, 1)
|
|
109
|
+
|
|
110
|
+
model = ProbitModel()
|
|
111
|
+
model.fit(X, y)
|
|
112
|
+
|
|
113
|
+
results = model.results_
|
|
114
|
+
formatted_results = {
|
|
115
|
+
'model_type': 'probit',
|
|
116
|
+
'coefficients': results.params.tolist(),
|
|
117
|
+
'std_errors': results.bse.tolist(),
|
|
118
|
+
'z_values': results.tvalues.tolist(),
|
|
119
|
+
'p_values': results.pvalues.tolist(),
|
|
120
|
+
'pseudo_r_squared': float(results.prsquared),
|
|
121
|
+
'log_likelihood': float(results.llf),
|
|
122
|
+
'aic': float(results.aic),
|
|
123
|
+
'bic': float(results.bic),
|
|
124
|
+
'n_obs': int(results.nobs),
|
|
125
|
+
'feature_names': feature_names or [f'X{i+1}' for i in range(X.shape[1])]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return json.dumps(formatted_results, indent=2, ensure_ascii=False)
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
logger.error(f"Probit failed: {str(e)}")
|
|
132
|
+
return json.dumps({'error': str(e)}, indent=2, ensure_ascii=False)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def multinomial_logit_adapter(
|
|
136
|
+
X_data: Optional[Union[List[float], List[List[float]]]] = None,
|
|
137
|
+
y_data: Optional[List[int]] = None,
|
|
138
|
+
file_path: Optional[str] = None,
|
|
139
|
+
feature_names: Optional[List[str]] = None,
|
|
140
|
+
output_format: str = 'json',
|
|
141
|
+
save_path: Optional[str] = None
|
|
142
|
+
) -> str:
|
|
143
|
+
"""Multinomial Logit adapter"""
|
|
144
|
+
try:
|
|
145
|
+
if file_path:
|
|
146
|
+
data = DataLoader.load_from_file(file_path)
|
|
147
|
+
X_data = data.get('x_data', data.get('X', data.get('features')))
|
|
148
|
+
y_data = data.get('y_data', data.get('y', data.get('target')))
|
|
149
|
+
if feature_names is None:
|
|
150
|
+
feature_names = data.get('feature_names')
|
|
151
|
+
|
|
152
|
+
if X_data is None or y_data is None:
|
|
153
|
+
raise ValueError("X_data and y_data must be provided")
|
|
154
|
+
|
|
155
|
+
X = np.array(X_data)
|
|
156
|
+
y = np.array(y_data)
|
|
157
|
+
|
|
158
|
+
if X.ndim == 1:
|
|
159
|
+
X = X.reshape(-1, 1)
|
|
160
|
+
|
|
161
|
+
model = MultinomialLogit()
|
|
162
|
+
model.fit(X, y)
|
|
163
|
+
|
|
164
|
+
results = model.results_
|
|
165
|
+
formatted_results = {
|
|
166
|
+
'model_type': 'multinomial_logit',
|
|
167
|
+
'coefficients': results.params.tolist(),
|
|
168
|
+
'std_errors': results.bse.tolist(),
|
|
169
|
+
'z_values': results.tvalues.tolist(),
|
|
170
|
+
'p_values': results.pvalues.tolist(),
|
|
171
|
+
'pseudo_r_squared': float(results.prsquared),
|
|
172
|
+
'log_likelihood': float(results.llf),
|
|
173
|
+
'aic': float(results.aic),
|
|
174
|
+
'bic': float(results.bic),
|
|
175
|
+
'n_obs': int(results.nobs),
|
|
176
|
+
'classes': model.classes_.tolist(),
|
|
177
|
+
'feature_names': feature_names or [f'X{i+1}' for i in range(X.shape[1])]
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return json.dumps(formatted_results, indent=2, ensure_ascii=False)
|
|
181
|
+
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(f"Multinomial Logit failed: {str(e)}")
|
|
184
|
+
return json.dumps({'error': str(e)}, indent=2, ensure_ascii=False)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def poisson_adapter(
|
|
188
|
+
X_data: Optional[Union[List[float], List[List[float]]]] = None,
|
|
189
|
+
y_data: Optional[List[int]] = None,
|
|
190
|
+
file_path: Optional[str] = None,
|
|
191
|
+
feature_names: Optional[List[str]] = None,
|
|
192
|
+
output_format: str = 'json',
|
|
193
|
+
save_path: Optional[str] = None
|
|
194
|
+
) -> str:
|
|
195
|
+
"""Poisson regression adapter"""
|
|
196
|
+
try:
|
|
197
|
+
if file_path:
|
|
198
|
+
data = DataLoader.load_from_file(file_path)
|
|
199
|
+
X_data = data.get('x_data', data.get('X', data.get('features')))
|
|
200
|
+
y_data = data.get('y_data', data.get('y', data.get('target')))
|
|
201
|
+
if feature_names is None:
|
|
202
|
+
feature_names = data.get('feature_names')
|
|
203
|
+
|
|
204
|
+
if X_data is None or y_data is None:
|
|
205
|
+
raise ValueError("X_data and y_data must be provided")
|
|
206
|
+
|
|
207
|
+
X = np.array(X_data)
|
|
208
|
+
y = np.array(y_data).astype(int)
|
|
209
|
+
|
|
210
|
+
if X.ndim == 1:
|
|
211
|
+
X = X.reshape(-1, 1)
|
|
212
|
+
|
|
213
|
+
model = PoissonModel()
|
|
214
|
+
model.fit(X, y)
|
|
215
|
+
|
|
216
|
+
results = model.results_
|
|
217
|
+
formatted_results = {
|
|
218
|
+
'model_type': 'poisson',
|
|
219
|
+
'coefficients': results.params.tolist(),
|
|
220
|
+
'std_errors': results.bse.tolist(),
|
|
221
|
+
'z_values': results.tvalues.tolist(),
|
|
222
|
+
'p_values': results.pvalues.tolist(),
|
|
223
|
+
'pseudo_r_squared': float(results.prsquared),
|
|
224
|
+
'log_likelihood': float(results.llf),
|
|
225
|
+
'aic': float(results.aic),
|
|
226
|
+
'bic': float(results.bic),
|
|
227
|
+
'n_obs': int(results.nobs),
|
|
228
|
+
'feature_names': feature_names or [f'X{i+1}' for i in range(X.shape[1])]
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return json.dumps(formatted_results, indent=2, ensure_ascii=False)
|
|
232
|
+
|
|
233
|
+
except Exception as e:
|
|
234
|
+
logger.error(f"Poisson failed: {str(e)}")
|
|
235
|
+
return json.dumps({'error': str(e)}, indent=2, ensure_ascii=False)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def negative_binomial_adapter(
|
|
239
|
+
X_data: Optional[Union[List[float], List[List[float]]]] = None,
|
|
240
|
+
y_data: Optional[List[int]] = None,
|
|
241
|
+
file_path: Optional[str] = None,
|
|
242
|
+
feature_names: Optional[List[str]] = None,
|
|
243
|
+
distr: str = 'nb2',
|
|
244
|
+
output_format: str = 'json',
|
|
245
|
+
save_path: Optional[str] = None
|
|
246
|
+
) -> str:
|
|
247
|
+
"""Negative Binomial regression adapter"""
|
|
248
|
+
try:
|
|
249
|
+
if file_path:
|
|
250
|
+
data = DataLoader.load_from_file(file_path)
|
|
251
|
+
X_data = data.get('x_data', data.get('X', data.get('features')))
|
|
252
|
+
y_data = data.get('y_data', data.get('y', data.get('target')))
|
|
253
|
+
if feature_names is None:
|
|
254
|
+
feature_names = data.get('feature_names')
|
|
255
|
+
|
|
256
|
+
if X_data is None or y_data is None:
|
|
257
|
+
raise ValueError("X_data and y_data must be provided")
|
|
258
|
+
|
|
259
|
+
X = np.array(X_data)
|
|
260
|
+
y = np.array(y_data).astype(int)
|
|
261
|
+
|
|
262
|
+
if X.ndim == 1:
|
|
263
|
+
X = X.reshape(-1, 1)
|
|
264
|
+
|
|
265
|
+
model = NegativeBinomialModel(distr=distr)
|
|
266
|
+
model.fit(X, y)
|
|
267
|
+
|
|
268
|
+
results = model.results_
|
|
269
|
+
formatted_results = {
|
|
270
|
+
'model_type': 'negative_binomial',
|
|
271
|
+
'distribution': distr,
|
|
272
|
+
'coefficients': results.params.tolist(),
|
|
273
|
+
'std_errors': results.bse.tolist(),
|
|
274
|
+
'z_values': results.tvalues.tolist(),
|
|
275
|
+
'p_values': results.pvalues.tolist(),
|
|
276
|
+
'pseudo_r_squared': float(results.prsquared),
|
|
277
|
+
'log_likelihood': float(results.llf),
|
|
278
|
+
'aic': float(results.aic),
|
|
279
|
+
'bic': float(results.bic),
|
|
280
|
+
'n_obs': int(results.nobs),
|
|
281
|
+
'feature_names': feature_names or [f'X{i+1}' for i in range(X.shape[1])]
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return json.dumps(formatted_results, indent=2, ensure_ascii=False)
|
|
285
|
+
|
|
286
|
+
except Exception as e:
|
|
287
|
+
logger.error(f"Negative Binomial failed: {str(e)}")
|
|
288
|
+
return json.dumps({'error': str(e)}, indent=2, ensure_ascii=False)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def tobit_adapter(
|
|
292
|
+
X_data: Optional[Union[List[float], List[List[float]]]] = None,
|
|
293
|
+
y_data: Optional[List[float]] = None,
|
|
294
|
+
file_path: Optional[str] = None,
|
|
295
|
+
feature_names: Optional[List[str]] = None,
|
|
296
|
+
lower_bound: float = 0.0,
|
|
297
|
+
upper_bound: Optional[float] = None,
|
|
298
|
+
output_format: str = 'json',
|
|
299
|
+
save_path: Optional[str] = None
|
|
300
|
+
) -> str:
|
|
301
|
+
"""Tobit model adapter"""
|
|
302
|
+
try:
|
|
303
|
+
if file_path:
|
|
304
|
+
data = DataLoader.load_from_file(file_path)
|
|
305
|
+
X_data = data.get('x_data', data.get('X', data.get('features')))
|
|
306
|
+
y_data = data.get('y_data', data.get('y', data.get('target')))
|
|
307
|
+
if feature_names is None:
|
|
308
|
+
feature_names = data.get('feature_names')
|
|
309
|
+
|
|
310
|
+
if X_data is None or y_data is None:
|
|
311
|
+
raise ValueError("X_data and y_data must be provided")
|
|
312
|
+
|
|
313
|
+
X = np.array(X_data)
|
|
314
|
+
y = np.array(y_data)
|
|
315
|
+
|
|
316
|
+
if X.ndim == 1:
|
|
317
|
+
X = X.reshape(-1, 1)
|
|
318
|
+
|
|
319
|
+
model = TobitModel(lower_bound=lower_bound, upper_bound=upper_bound)
|
|
320
|
+
model.fit(X, y)
|
|
321
|
+
|
|
322
|
+
results = model.results_
|
|
323
|
+
formatted_results = {
|
|
324
|
+
'model_type': 'tobit',
|
|
325
|
+
'lower_bound': lower_bound,
|
|
326
|
+
'upper_bound': upper_bound,
|
|
327
|
+
'coefficients': results.params.tolist(),
|
|
328
|
+
'std_errors': results.bse.tolist(),
|
|
329
|
+
'z_values': results.tvalues.tolist(),
|
|
330
|
+
'p_values': results.pvalues.tolist(),
|
|
331
|
+
'log_likelihood': float(results.llf),
|
|
332
|
+
'aic': float(results.aic),
|
|
333
|
+
'bic': float(results.bic),
|
|
334
|
+
'n_obs': int(results.nobs),
|
|
335
|
+
'feature_names': feature_names or [f'X{i+1}' for i in range(X.shape[1])]
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return json.dumps(formatted_results, indent=2, ensure_ascii=False)
|
|
339
|
+
|
|
340
|
+
except Exception as e:
|
|
341
|
+
logger.error(f"Tobit failed: {str(e)}")
|
|
342
|
+
return json.dumps({'error': str(e)}, indent=2, ensure_ascii=False)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def heckman_adapter(
|
|
346
|
+
X_select_data: Optional[Union[List[float], List[List[float]]]] = None,
|
|
347
|
+
Z_data: Optional[Union[List[float], List[List[float]]]] = None,
|
|
348
|
+
y_data: Optional[List[float]] = None,
|
|
349
|
+
s_data: Optional[List[int]] = None,
|
|
350
|
+
file_path: Optional[str] = None,
|
|
351
|
+
selection_feature_names: Optional[List[str]] = None,
|
|
352
|
+
outcome_feature_names: Optional[List[str]] = None,
|
|
353
|
+
output_format: str = 'json',
|
|
354
|
+
save_path: Optional[str] = None
|
|
355
|
+
) -> str:
|
|
356
|
+
"""Heckman selection model adapter"""
|
|
357
|
+
try:
|
|
358
|
+
if file_path:
|
|
359
|
+
data = DataLoader.load_from_file(file_path)
|
|
360
|
+
X_select_data = data.get('X_select', data.get('selection_features'))
|
|
361
|
+
Z_data = data.get('Z', data.get('outcome_features'))
|
|
362
|
+
y_data = data.get('y', data.get('outcome'))
|
|
363
|
+
s_data = data.get('s', data.get('selection'))
|
|
364
|
+
if selection_feature_names is None:
|
|
365
|
+
selection_feature_names = data.get('selection_feature_names')
|
|
366
|
+
if outcome_feature_names is None:
|
|
367
|
+
outcome_feature_names = data.get('outcome_feature_names')
|
|
368
|
+
|
|
369
|
+
if X_select_data is None or Z_data is None or y_data is None or s_data is None:
|
|
370
|
+
raise ValueError("All data must be provided")
|
|
371
|
+
|
|
372
|
+
X_select = np.array(X_select_data)
|
|
373
|
+
Z = np.array(Z_data)
|
|
374
|
+
y = np.array(y_data)
|
|
375
|
+
s = np.array(s_data).astype(int)
|
|
376
|
+
|
|
377
|
+
if X_select.ndim == 1:
|
|
378
|
+
X_select = X_select.reshape(-1, 1)
|
|
379
|
+
if Z.ndim == 1:
|
|
380
|
+
Z = Z.reshape(-1, 1)
|
|
381
|
+
|
|
382
|
+
model = HeckmanModel()
|
|
383
|
+
model.fit(X_select, Z, y, s)
|
|
384
|
+
|
|
385
|
+
selection_names = selection_feature_names or [f'SelectX{i+1}' for i in range(X_select.shape[1])]
|
|
386
|
+
outcome_names = outcome_feature_names or [f'OutcomeZ{i+1}' for i in range(Z.shape[1])]
|
|
387
|
+
|
|
388
|
+
formatted_results = {
|
|
389
|
+
'model_type': 'heckman',
|
|
390
|
+
'selection_results': {
|
|
391
|
+
'coefficients': model.selection_results_.params.tolist(),
|
|
392
|
+
'std_errors': model.selection_results_.bse.tolist(),
|
|
393
|
+
'z_values': model.selection_results_.tvalues.tolist(),
|
|
394
|
+
'p_values': model.selection_results_.pvalues.tolist(),
|
|
395
|
+
'feature_names': selection_names
|
|
396
|
+
},
|
|
397
|
+
'outcome_results': {
|
|
398
|
+
'coefficients': model.outcome_results_.params.tolist(),
|
|
399
|
+
'std_errors': model.outcome_results_.bse.tolist(),
|
|
400
|
+
't_values': model.outcome_results_.tvalues.tolist(),
|
|
401
|
+
'p_values': model.outcome_results_.pvalues.tolist(),
|
|
402
|
+
'feature_names': outcome_names
|
|
403
|
+
},
|
|
404
|
+
'n_obs': len(y),
|
|
405
|
+
'n_selected': int(np.sum(s))
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return json.dumps(formatted_results, indent=2, ensure_ascii=False)
|
|
409
|
+
|
|
410
|
+
except Exception as e:
|
|
411
|
+
logger.error(f"Heckman failed: {str(e)}")
|
|
412
|
+
return json.dumps({'error': str(e)}, indent=2, ensure_ascii=False)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
缺失数据处理适配器
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from econometrics.missing_data import (
|
|
9
|
+
simple_imputation,
|
|
10
|
+
multiple_imputation,
|
|
11
|
+
SimpleImputationResult,
|
|
12
|
+
MultipleImputationResult
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from .output_formatter import OutputFormatter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def simple_imputation_adapter(
|
|
19
|
+
data: List[List[float]],
|
|
20
|
+
strategy: str = "mean",
|
|
21
|
+
fill_value: Optional[float] = None,
|
|
22
|
+
output_format: str = "json",
|
|
23
|
+
save_path: Optional[str] = None
|
|
24
|
+
) -> str:
|
|
25
|
+
"""简单插补适配器"""
|
|
26
|
+
|
|
27
|
+
result: SimpleImputationResult = simple_imputation(
|
|
28
|
+
data=data,
|
|
29
|
+
strategy=strategy,
|
|
30
|
+
fill_value=fill_value
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if output_format == "json":
|
|
34
|
+
json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
|
|
35
|
+
if save_path:
|
|
36
|
+
OutputFormatter.save_to_file(json_result, save_path)
|
|
37
|
+
return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
|
|
38
|
+
return json_result
|
|
39
|
+
else:
|
|
40
|
+
formatted = f"""# 简单插补结果\n\n{result.summary}"""
|
|
41
|
+
if save_path:
|
|
42
|
+
OutputFormatter.save_to_file(formatted, save_path)
|
|
43
|
+
return formatted
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def multiple_imputation_adapter(
|
|
47
|
+
data: List[List[float]],
|
|
48
|
+
n_imputations: int = 5,
|
|
49
|
+
max_iter: int = 10,
|
|
50
|
+
random_state: Optional[int] = None,
|
|
51
|
+
output_format: str = "json",
|
|
52
|
+
save_path: Optional[str] = None
|
|
53
|
+
) -> str:
|
|
54
|
+
"""多重插补适配器"""
|
|
55
|
+
|
|
56
|
+
result: MultipleImputationResult = multiple_imputation(
|
|
57
|
+
data=data,
|
|
58
|
+
n_imputations=n_imputations,
|
|
59
|
+
max_iter=max_iter,
|
|
60
|
+
random_state=random_state
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if output_format == "json":
|
|
64
|
+
json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
|
|
65
|
+
if save_path:
|
|
66
|
+
OutputFormatter.save_to_file(json_result, save_path)
|
|
67
|
+
return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
|
|
68
|
+
return json_result
|
|
69
|
+
else:
|
|
70
|
+
formatted = f"""# 多重插补结果\n\n{result.summary}"""
|
|
71
|
+
if save_path:
|
|
72
|
+
OutputFormatter.save_to_file(formatted, save_path)
|
|
73
|
+
return formatted
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
非参数与半参数方法适配器
|
|
3
|
+
将核心算法适配为MCP工具
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
from econometrics.nonparametric import (
|
|
10
|
+
kernel_regression,
|
|
11
|
+
quantile_regression,
|
|
12
|
+
spline_regression,
|
|
13
|
+
gam_model,
|
|
14
|
+
KernelRegressionResult,
|
|
15
|
+
QuantileRegressionResult,
|
|
16
|
+
SplineRegressionResult,
|
|
17
|
+
GAMResult
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .data_loader import DataLoader
|
|
21
|
+
from .output_formatter import OutputFormatter
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def kernel_regression_adapter(
|
|
25
|
+
y_data: Optional[List[float]] = None,
|
|
26
|
+
x_data: Optional[List[List[float]]] = None,
|
|
27
|
+
file_path: Optional[str] = None,
|
|
28
|
+
kernel_type: str = "gaussian",
|
|
29
|
+
bandwidth: Optional[List[float]] = None,
|
|
30
|
+
bandwidth_method: str = "cv_ls",
|
|
31
|
+
variable_type: Optional[str] = None,
|
|
32
|
+
output_format: str = "json",
|
|
33
|
+
save_path: Optional[str] = None
|
|
34
|
+
) -> str:
|
|
35
|
+
"""核回归适配器"""
|
|
36
|
+
|
|
37
|
+
# 数据准备
|
|
38
|
+
if file_path:
|
|
39
|
+
data = DataLoader.load_from_file(file_path)
|
|
40
|
+
y_data = data["y_data"]
|
|
41
|
+
x_data = data["x_data"]
|
|
42
|
+
elif y_data is None or x_data is None:
|
|
43
|
+
raise ValueError("必须提供文件路径(file_path)或直接数据(y_data和x_data)")
|
|
44
|
+
|
|
45
|
+
# 调用核心算法
|
|
46
|
+
result: KernelRegressionResult = kernel_regression(
|
|
47
|
+
y_data=y_data,
|
|
48
|
+
x_data=x_data,
|
|
49
|
+
kernel_type=kernel_type,
|
|
50
|
+
bandwidth=bandwidth,
|
|
51
|
+
bandwidth_method=bandwidth_method,
|
|
52
|
+
variable_type=variable_type
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# 格式化输出
|
|
56
|
+
if output_format == "json":
|
|
57
|
+
json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
|
|
58
|
+
if save_path:
|
|
59
|
+
OutputFormatter.save_to_file(json_result, save_path)
|
|
60
|
+
return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
|
|
61
|
+
return json_result
|
|
62
|
+
else:
|
|
63
|
+
formatted = f"""# 核回归分析结果
|
|
64
|
+
|
|
65
|
+
{result.summary}
|
|
66
|
+
|
|
67
|
+
## 模型信息
|
|
68
|
+
- 核函数: {result.kernel_type}
|
|
69
|
+
- 带宽: {', '.join([f'{b:.4f}' for b in result.bandwidth])}
|
|
70
|
+
- R²: {result.r_squared:.4f}
|
|
71
|
+
"""
|
|
72
|
+
if result.aic:
|
|
73
|
+
formatted += f"- AIC: {result.aic:.2f}\n"
|
|
74
|
+
|
|
75
|
+
if save_path:
|
|
76
|
+
OutputFormatter.save_to_file(formatted, save_path)
|
|
77
|
+
return f"分析完成!\n\n{formatted}\n\n已保存到: {save_path}"
|
|
78
|
+
return formatted
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def quantile_regression_adapter(
|
|
82
|
+
y_data: Optional[List[float]] = None,
|
|
83
|
+
x_data: Optional[List[List[float]]] = None,
|
|
84
|
+
file_path: Optional[str] = None,
|
|
85
|
+
quantile: float = 0.5,
|
|
86
|
+
feature_names: Optional[List[str]] = None,
|
|
87
|
+
confidence_level: float = 0.95,
|
|
88
|
+
output_format: str = "json",
|
|
89
|
+
save_path: Optional[str] = None
|
|
90
|
+
) -> str:
|
|
91
|
+
"""分位数回归适配器"""
|
|
92
|
+
|
|
93
|
+
# 数据准备
|
|
94
|
+
if file_path:
|
|
95
|
+
data = DataLoader.load_from_file(file_path)
|
|
96
|
+
y_data = data["y_data"]
|
|
97
|
+
x_data = data["x_data"]
|
|
98
|
+
feature_names = data.get("feature_names") or feature_names
|
|
99
|
+
elif y_data is None or x_data is None:
|
|
100
|
+
raise ValueError("必须提供文件路径(file_path)或直接数据(y_data和x_data)")
|
|
101
|
+
|
|
102
|
+
# 调用核心算法
|
|
103
|
+
result: QuantileRegressionResult = quantile_regression(
|
|
104
|
+
y_data=y_data,
|
|
105
|
+
x_data=x_data,
|
|
106
|
+
quantile=quantile,
|
|
107
|
+
feature_names=feature_names,
|
|
108
|
+
confidence_level=confidence_level
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# 格式化输出
|
|
112
|
+
if output_format == "json":
|
|
113
|
+
json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
|
|
114
|
+
if save_path:
|
|
115
|
+
OutputFormatter.save_to_file(json_result, save_path)
|
|
116
|
+
return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
|
|
117
|
+
return json_result
|
|
118
|
+
else:
|
|
119
|
+
formatted = f"""# 分位数回归分析结果
|
|
120
|
+
|
|
121
|
+
{result.summary}
|
|
122
|
+
"""
|
|
123
|
+
if save_path:
|
|
124
|
+
OutputFormatter.save_to_file(formatted, save_path)
|
|
125
|
+
return f"分析完成!\n\n{formatted}\n\n已保存到: {save_path}"
|
|
126
|
+
return formatted
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def spline_regression_adapter(
|
|
130
|
+
y_data: List[float],
|
|
131
|
+
x_data: List[float],
|
|
132
|
+
n_knots: int = 5,
|
|
133
|
+
degree: int = 3,
|
|
134
|
+
knots: str = "uniform",
|
|
135
|
+
output_format: str = "json",
|
|
136
|
+
save_path: Optional[str] = None
|
|
137
|
+
) -> str:
|
|
138
|
+
"""样条回归适配器"""
|
|
139
|
+
|
|
140
|
+
result: SplineRegressionResult = spline_regression(
|
|
141
|
+
y_data=y_data,
|
|
142
|
+
x_data=x_data,
|
|
143
|
+
n_knots=n_knots,
|
|
144
|
+
degree=degree,
|
|
145
|
+
knots=knots
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
if output_format == "json":
|
|
149
|
+
json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
|
|
150
|
+
if save_path:
|
|
151
|
+
OutputFormatter.save_to_file(json_result, save_path)
|
|
152
|
+
return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
|
|
153
|
+
return json_result
|
|
154
|
+
else:
|
|
155
|
+
formatted = f"""# 样条回归结果\n\n{result.summary}"""
|
|
156
|
+
if save_path:
|
|
157
|
+
OutputFormatter.save_to_file(formatted, save_path)
|
|
158
|
+
return formatted
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def gam_adapter(
|
|
162
|
+
y_data: List[float],
|
|
163
|
+
x_data: List[List[float]],
|
|
164
|
+
problem_type: str = "regression",
|
|
165
|
+
n_splines: int = 10,
|
|
166
|
+
lam: float = 0.6,
|
|
167
|
+
output_format: str = "json",
|
|
168
|
+
save_path: Optional[str] = None
|
|
169
|
+
) -> str:
|
|
170
|
+
"""GAM模型适配器"""
|
|
171
|
+
|
|
172
|
+
result: GAMResult = gam_model(
|
|
173
|
+
y_data=y_data,
|
|
174
|
+
x_data=x_data,
|
|
175
|
+
problem_type=problem_type,
|
|
176
|
+
n_splines=n_splines,
|
|
177
|
+
lam=lam
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if output_format == "json":
|
|
181
|
+
json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
|
|
182
|
+
if save_path:
|
|
183
|
+
OutputFormatter.save_to_file(json_result, save_path)
|
|
184
|
+
return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
|
|
185
|
+
return json_result
|
|
186
|
+
else:
|
|
187
|
+
formatted = f"""# GAM模型结果\n\n{result.summary}"""
|
|
188
|
+
if save_path:
|
|
189
|
+
OutputFormatter.save_to_file(formatted, save_path)
|
|
190
|
+
return formatted
|