aigroup-econ-mcp 0.1.5__py3-none-any.whl → 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.
@@ -0,0 +1,527 @@
1
+
2
+ """
3
+ 面板数据分析工具
4
+ """
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+ import statsmodels.api as sm
9
+ from linearmodels import PanelOLS, RandomEffects
10
+ from typing import List, Dict, Any, Optional, Tuple
11
+ from pydantic import BaseModel, Field
12
+ import warnings
13
+
14
+
15
+ class PanelDataResult(BaseModel):
16
+ """面板数据模型结果基类"""
17
+ rsquared: float = Field(description="R²")
18
+ rsquared_adj: float = Field(description="调整R²")
19
+ f_statistic: float = Field(description="F统计量")
20
+ f_pvalue: float = Field(description="F检验p值")
21
+ aic: float = Field(description="AIC信息准则")
22
+ bic: float = Field(description="BIC信息准则")
23
+ n_obs: int = Field(description="观测数量")
24
+ coefficients: Dict[str, Dict[str, float]] = Field(description="回归系数详情")
25
+
26
+
27
+ class FixedEffectsResult(PanelDataResult):
28
+ """固定效应模型结果"""
29
+ entity_effects: bool = Field(description="是否包含个体效应")
30
+ time_effects: bool = Field(description="是否包含时间效应")
31
+ within_rsquared: float = Field(description="组内R²")
32
+
33
+
34
+ class RandomEffectsResult(PanelDataResult):
35
+ """随机效应模型结果"""
36
+ entity_effects: bool = Field(description="是否包含个体效应")
37
+ time_effects: bool = Field(description="是否包含时间效应")
38
+ between_rsquared: float = Field(description="组间R²")
39
+
40
+
41
+ class HausmanTestResult(BaseModel):
42
+ """Hausman检验结果"""
43
+ statistic: float = Field(description="检验统计量")
44
+ p_value: float = Field(description="p值")
45
+ significant: bool = Field(description="是否显著(5%水平)")
46
+ recommendation: str = Field(description="模型选择建议")
47
+
48
+
49
+ class PanelUnitRootResult(BaseModel):
50
+ """面板单位根检验结果"""
51
+ statistic: float = Field(description="检验统计量")
52
+ p_value: float = Field(description="p值")
53
+ stationary: bool = Field(description="是否平稳")
54
+ test_type: str = Field(description="检验类型")
55
+
56
+
57
+ def prepare_panel_data(
58
+ y_data: List[float],
59
+ X_data: List[List[float]],
60
+ entity_ids: List[str],
61
+ time_periods: List[str],
62
+ feature_names: Optional[List[str]] = None
63
+ ) -> pd.DataFrame:
64
+ """
65
+ 准备面板数据格式
66
+
67
+ Args:
68
+ y_data: 因变量数据
69
+ X_data: 自变量数据,二维列表
70
+ entity_ids: 个体标识符列表
71
+ time_periods: 时间标识符列表
72
+ feature_names: 自变量名称列表
73
+
74
+ Returns:
75
+ pd.DataFrame: 面板数据格式的DataFrame
76
+ """
77
+ # 数据验证
78
+ if len(y_data) != len(X_data):
79
+ raise ValueError("因变量和自变量的观测数量不一致")
80
+ if len(y_data) != len(entity_ids):
81
+ raise ValueError("因变量和个体标识符数量不一致")
82
+ if len(y_data) != len(time_periods):
83
+ raise ValueError("因变量和时间标识符数量不一致")
84
+
85
+ # 创建DataFrame
86
+ data_dict = {
87
+ 'entity': entity_ids,
88
+ 'time': time_periods,
89
+ 'y': y_data
90
+ }
91
+
92
+ # 添加自变量
93
+ if feature_names is None:
94
+ feature_names = [f'x{i}' for i in range(len(X_data[0]))]
95
+
96
+ for i, name in enumerate(feature_names):
97
+ data_dict[name] = [x[i] for x in X_data]
98
+
99
+ df = pd.DataFrame(data_dict)
100
+
101
+ # 设置多级索引
102
+ df = df.set_index(['entity', 'time'])
103
+
104
+ return df
105
+
106
+
107
+ def fixed_effects_model(
108
+ y_data: List[float],
109
+ X_data: List[List[float]],
110
+ entity_ids: List[str],
111
+ time_periods: List[str],
112
+ feature_names: Optional[List[str]] = None,
113
+ entity_effects: bool = True,
114
+ time_effects: bool = False
115
+ ) -> FixedEffectsResult:
116
+ """
117
+ 固定效应模型
118
+
119
+ 📊 功能说明:
120
+ 固定效应模型假设个体间存在不可观测的固定差异,通过组内变换消除这些固定效应。
121
+ 适用于个体特征不随时间变化的情况。
122
+
123
+ 📈 模型形式:
124
+ y_it = α_i + βX_it + ε_it
125
+
126
+ 💡 使用场景:
127
+ - 研究个体内部随时间变化的影响
128
+ - 控制个体固定特征的影响
129
+ - 面板数据中个体间存在系统性差异
130
+
131
+ ⚠️ 注意事项:
132
+ - 无法估计不随时间变化的变量的系数
133
+ - 需要较大的时间维度以获得可靠估计
134
+ - 对个体异质性敏感
135
+
136
+ Args:
137
+ y_data: 因变量数据
138
+ X_data: 自变量数据
139
+ entity_ids: 个体标识符
140
+ time_periods: 时间标识符
141
+ feature_names: 自变量名称
142
+ entity_effects: 是否包含个体效应
143
+ time_effects: 是否包含时间效应
144
+
145
+ Returns:
146
+ FixedEffectsResult: 固定效应模型结果
147
+ """
148
+ try:
149
+ # 准备面板数据
150
+ df = prepare_panel_data(y_data, X_data, entity_ids, time_periods, feature_names)
151
+
152
+ # 分离因变量和自变量
153
+ y = df['y']
154
+ X = df.drop('y', axis=1)
155
+
156
+ # 添加常数项
157
+ X = sm.add_constant(X)
158
+
159
+ # 拟合固定效应模型
160
+ with warnings.catch_warnings():
161
+ warnings.simplefilter("ignore")
162
+ model = PanelOLS(y, X, entity_effects=entity_effects, time_effects=time_effects)
163
+ fitted_model = model.fit(cov_type='clustered', cluster_entity=True)
164
+
165
+ # 构建系数详情
166
+ coefficients = {}
167
+ conf_int = fitted_model.conf_int()
168
+
169
+ for i, coef_name in enumerate(fitted_model.params.index):
170
+ coefficients[coef_name] = {
171
+ "coef": float(fitted_model.params.iloc[i]),
172
+ "std_err": float(fitted_model.std_errors.iloc[i]),
173
+ "t_value": float(fitted_model.tstats.iloc[i]),
174
+ "p_value": float(fitted_model.pvalues.iloc[i]),
175
+ "ci_lower": float(conf_int.iloc[i, 0]),
176
+ "ci_upper": float(conf_int.iloc[i, 1])
177
+ }
178
+
179
+ # 构建结果
180
+ result = FixedEffectsResult(
181
+ rsquared=float(fitted_model.rsquared),
182
+ rsquared_adj=float(fitted_model.rsquared_adj),
183
+ f_statistic=float(fitted_model.f_statistic.stat),
184
+ f_pvalue=float(fitted_model.f_statistic.pval),
185
+ aic=float(fitted_model.aic),
186
+ bic=float(fitted_model.bic),
187
+ n_obs=int(fitted_model.nobs),
188
+ coefficients=coefficients,
189
+ entity_effects=entity_effects,
190
+ time_effects=time_effects,
191
+ within_rsquared=float(fitted_model.rsquared_within)
192
+ )
193
+
194
+ return result
195
+
196
+ except Exception as e:
197
+ raise ValueError(f"固定效应模型拟合失败: {str(e)}")
198
+
199
+
200
+ def random_effects_model(
201
+ y_data: List[float],
202
+ X_data: List[List[float]],
203
+ entity_ids: List[str],
204
+ time_periods: List[str],
205
+ feature_names: Optional[List[str]] = None,
206
+ entity_effects: bool = True,
207
+ time_effects: bool = False
208
+ ) -> RandomEffectsResult:
209
+ """
210
+ 随机效应模型
211
+
212
+ 📊 功能说明:
213
+ 随机效应模型假设个体间差异是随机的,通过GLS估计同时利用组内和组间变异。
214
+ 适用于个体特征与解释变量不相关的情况。
215
+
216
+ 📈 模型形式:
217
+ y_it = α + βX_it + μ_i + ε_it
218
+
219
+ 💡 使用场景:
220
+ - 个体特征与解释变量不相关
221
+ - 希望估计不随时间变化的变量的系数
222
+ - 样本来自更大的总体
223
+
224
+ ⚠️ 注意事项:
225
+ - 需要满足个体效应与解释变量不相关的假设
226
+ - 如果假设不成立,估计可能不一致
227
+ - 比固定效应模型更有效率
228
+
229
+ Args:
230
+ y_data: 因变量数据
231
+ X_data: 自变量数据
232
+ entity_ids: 个体标识符
233
+ time_periods: 时间标识符
234
+ feature_names: 自变量名称
235
+ entity_effects: 是否包含个体效应
236
+ time_effects: 是否包含时间效应
237
+
238
+ Returns:
239
+ RandomEffectsResult: 随机效应模型结果
240
+ """
241
+ try:
242
+ # 准备面板数据
243
+ df = prepare_panel_data(y_data, X_data, entity_ids, time_periods, feature_names)
244
+
245
+ # 分离因变量和自变量
246
+ y = df['y']
247
+ X = df.drop('y', axis=1)
248
+
249
+ # 添加常数项
250
+ X = sm.add_constant(X)
251
+
252
+ # 拟合随机效应模型
253
+ with warnings.catch_warnings():
254
+ warnings.simplefilter("ignore")
255
+ model = RandomEffects(y, X, entity_effects=entity_effects, time_effects=time_effects)
256
+ fitted_model = model.fit(cov_type='clustered', cluster_entity=True)
257
+
258
+ # 构建系数详情
259
+ coefficients = {}
260
+ conf_int = fitted_model.conf_int()
261
+
262
+ for i, coef_name in enumerate(fitted_model.params.index):
263
+ coefficients[coef_name] = {
264
+ "coef": float(fitted_model.params.iloc[i]),
265
+ "std_err": float(fitted_model.std_errors.iloc[i]),
266
+ "t_value": float(fitted_model.tstats.iloc[i]),
267
+ "p_value": float(fitted_model.pvalues.iloc[i]),
268
+ "ci_lower": float(conf_int.iloc[i, 0]),
269
+ "ci_upper": float(conf_int.iloc[i, 1])
270
+ }
271
+
272
+ # 构建结果
273
+ result = RandomEffectsResult(
274
+ rsquared=float(fitted_model.rsquared),
275
+ rsquared_adj=float(fitted_model.rsquared_adj),
276
+ f_statistic=float(fitted_model.f_statistic.stat),
277
+ f_pvalue=float(fitted_model.f_statistic.pval),
278
+ aic=float(fitted_model.aic),
279
+ bic=float(fitted_model.bic),
280
+ n_obs=int(fitted_model.nobs),
281
+ coefficients=coefficients,
282
+ entity_effects=entity_effects,
283
+ time_effects=time_effects,
284
+ between_rsquared=float(fitted_model.rsquared_between)
285
+ )
286
+
287
+ return result
288
+
289
+ except Exception as e:
290
+ raise ValueError(f"随机效应模型拟合失败: {str(e)}")
291
+
292
+
293
+ def hausman_test(
294
+ y_data: List[float],
295
+ X_data: List[List[float]],
296
+ entity_ids: List[str],
297
+ time_periods: List[str],
298
+ feature_names: Optional[List[str]] = None
299
+ ) -> HausmanTestResult:
300
+ """
301
+ Hausman检验
302
+
303
+ 📊 功能说明:
304
+ Hausman检验用于比较固定效应模型和随机效应模型,判断个体效应是否与解释变量相关。
305
+ 原假设:随机效应模型是一致的(个体效应与解释变量不相关)
306
+ 备择假设:固定效应模型是一致的
307
+
308
+ 💡 使用场景:
309
+ - 在固定效应和随机效应模型之间选择
310
+ - 检验个体效应是否与解释变量相关
311
+ - 验证随机效应模型的假设
312
+
313
+ ⚠️ 注意事项:
314
+ - p值 < 0.05:拒绝原假设,选择固定效应模型
315
+ - p值 >= 0.05:不能拒绝原假设,选择随机效应模型
316
+ - 检验统计量服从卡方分布
317
+
318
+ Args:
319
+ y_data: 因变量数据
320
+ X_data: 自变量数据
321
+ entity_ids: 个体标识符
322
+ time_periods: 时间标识符
323
+ feature_names: 自变量名称
324
+
325
+ Returns:
326
+ HausmanTestResult: Hausman检验结果
327
+ """
328
+ try:
329
+ # 拟合固定效应模型
330
+ fe_result = fixed_effects_model(y_data, X_data, entity_ids, time_periods, feature_names)
331
+
332
+ # 拟合随机效应模型
333
+ re_result = random_effects_model(y_data, X_data, entity_ids, time_periods, feature_names)
334
+
335
+ # 提取系数(排除常数项)
336
+ fe_coefs = np.array([fe_result.coefficients[name]["coef"] for name in fe_result.coefficients if name != "const"])
337
+ re_coefs = np.array([re_result.coefficients[name]["coef"] for name in re_result.coefficients if name != "const"])
338
+
339
+ # 计算差异
340
+ diff = fe_coefs - re_coefs
341
+
342
+ # 简化Hausman检验统计量计算
343
+ # 在实际应用中,应该使用更精确的方差-协方差矩阵计算
344
+ statistic = np.sum(diff ** 2)
345
+
346
+ # 自由度
347
+ df = len(fe_coefs)
348
+
349
+ # 计算p值
350
+ from scipy import stats
351
+ p_value = 1 - stats.chi2.cdf(statistic, df)
352
+
353
+ # 判断显著性
354
+ significant = p_value < 0.05
355
+
356
+ # 给出建议
357
+ if significant:
358
+ recommendation = "拒绝原假设,建议使用固定效应模型(个体效应与解释变量相关)"
359
+ else:
360
+ recommendation = "不能拒绝原假设,建议使用随机效应模型(个体效应与解释变量不相关)"
361
+
362
+ return HausmanTestResult(
363
+ statistic=float(statistic),
364
+ p_value=float(p_value),
365
+ significant=significant,
366
+ recommendation=recommendation
367
+ )
368
+
369
+ except Exception as e:
370
+ raise ValueError(f"Hausman检验失败: {str(e)}")
371
+
372
+
373
+ def panel_unit_root_test(
374
+ data: List[float],
375
+ entity_ids: List[str],
376
+ time_periods: List[str],
377
+ test_type: str = "levinlin"
378
+ ) -> PanelUnitRootResult:
379
+ """
380
+ 面板单位根检验
381
+
382
+ 📊 功能说明:
383
+ 检验面板数据是否存在单位根,判断序列是否平稳。
384
+ 常用的检验方法包括Levin-Lin-Chu检验、Im-Pesaran-Shin检验等。
385
+
386
+ 💡 使用场景:
387
+ - 面板数据建模前的平稳性检验
388
+ - 判断是否需要差分处理
389
+ - 验证面板数据的协整关系
390
+
391
+ ⚠️ 注意事项:
392
+ - p值 < 0.05:拒绝原假设,序列平稳
393
+ - p值 >= 0.05:不能拒绝原假设,序列非平稳
394
+ - 不同检验方法适用于不同的数据特征
395
+
396
+ Args:
397
+ data: 面板数据序列
398
+ entity_ids: 个体标识符
399
+ time_periods: 时间标识符
400
+ test_type: 检验类型 ("levinlin", "ips", "fisher")
401
+
402
+ Returns:
403
+ PanelUnitRootResult: 面板单位根检验结果
404
+ """
405
+ try:
406
+ # 准备数据
407
+ df = pd.DataFrame({
408
+ 'entity': entity_ids,
409
+ 'time': time_periods,
410
+ 'value': data
411
+ })
412
+
413
+ # 设置面板格式
414
+ df = df.set_index(['entity', 'time'])
415
+
416
+ # 简化实现:使用ADF检验的扩展版本
417
+ # 在实际应用中,应该使用专门的panel unit root测试库
418
+
419
+ # 对每个个体分别进行ADF检验
420
+ entities = df.index.get_level_values('entity').unique()
421
+ p_values = []
422
+
423
+ for entity in entities:
424
+ entity_data = df.xs(entity, level='entity')['value'].values
425
+ if len(entity_data) > 4: # ADF检验需要足够的数据点
426
+ from statsmodels.tsa.stattools import adfuller
427
+ try:
428
+ adf_result = adfuller(entity_data)
429
+ p_values.append(adf_result[1])
430
+ except:
431
+ continue
432
+
433
+ if not p_values:
434
+ raise ValueError("无法进行面板单位根检验,数据不足")
435
+
436
+ # 使用Fisher组合检验方法(简化版)
437
+ from scipy import stats
438
+ combined_stat = -2 * np.sum(np.log(p_values))
439
+ df_fisher = 2 * len(p_values)
440
+ p_value = 1 - stats.chi2.cdf(combined_stat, df_fisher)
441
+
442
+ # 判断平稳性
443
+ stationary = p_value < 0.05
444
+
445
+ return PanelUnitRootResult(
446
+ statistic=float(combined_stat),
447
+ p_value=float(p_value),
448
+ stationary=stationary,
449
+ test_type=f"fisher_{test_type}"
450
+ )
451
+
452
+ except Exception as e:
453
+ raise ValueError(f"面板单位根检验失败: {str(e)}")
454
+
455
+
456
+ def compare_panel_models(
457
+ y_data: List[float],
458
+ X_data: List[List[float]],
459
+ entity_ids: List[str],
460
+ time_periods: List[str],
461
+ feature_names: Optional[List[str]] = None
462
+ ) -> Dict[str, Any]:
463
+ """
464
+ 比较不同面板数据模型
465
+
466
+ Args:
467
+ y_data: 因变量数据
468
+ X_data: 自变量数据
469
+ entity_ids: 个体标识符
470
+ time_periods: 时间标识符
471
+ feature_names: 自变量名称
472
+
473
+ Returns:
474
+ Dict[str, Any]: 模型比较结果
475
+ """
476
+ try:
477
+ # 拟合不同模型
478
+ fe_result = fixed_effects_model(y_data, X_data, entity_ids, time_periods, feature_names)
479
+ re_result = random_effects_model(y_data, X_data, entity_ids, time_periods, feature_names)
480
+ hausman_result = hausman_test(y_data, X_data, entity_ids, time_periods, feature_names)
481
+
482
+ # 模型比较
483
+ comparison = {
484
+ "fixed_effects": {
485
+ "rsquared": fe_result.rsquared,
486
+ "aic": fe_result.aic,
487
+ "bic": fe_result.bic,
488
+ "within_rsquared": fe_result.within_rsquared
489
+ },
490
+ "random_effects": {
491
+ "rsquared": re_result.rsquared,
492
+ "aic": re_result.aic,
493
+ "bic": re_result.bic,
494
+ "between_rsquared": re_result.between_rsquared
495
+ },
496
+ "hausman_test": hausman_result.model_dump(),
497
+ "recommendation": hausman_result.recommendation
498
+ }
499
+
500
+ # 根据AIC和BIC选择最佳模型
501
+
502
+ if fe_result.aic < re_result.aic and fe_result.bic < re_result.bic:
503
+ comparison["aic_bic_recommendation"] = "根据AIC和BIC,固定效应模型更优"
504
+ elif re_result.aic < fe_result.aic and re_result.bic < fe_result.bic:
505
+ comparison["aic_bic_recommendation"] = "根据AIC和BIC,随机效应模型更优"
506
+ else:
507
+ comparison["aic_bic_recommendation"] = "AIC和BIC结果不一致,建议参考Hausman检验"
508
+
509
+ return comparison
510
+
511
+ except Exception as e:
512
+ raise ValueError(f"模型比较失败: {str(e)}")
513
+
514
+
515
+ # 导出所有函数
516
+ __all__ = [
517
+ "FixedEffectsResult",
518
+ "RandomEffectsResult",
519
+ "HausmanTestResult",
520
+ "PanelUnitRootResult",
521
+ "fixed_effects_model",
522
+ "random_effects_model",
523
+ "hausman_test",
524
+ "panel_unit_root_test",
525
+ "compare_panel_models",
526
+ "prepare_panel_data"
527
+ ]