aigroup-econ-mcp 0.1.2__py3-none-any.whl → 0.1.6__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.
Potentially problematic release.
This version of aigroup-econ-mcp might be problematic. Click here for more details.
- aigroup_econ_mcp/__init__.py +18 -18
- aigroup_econ_mcp/cli.py +86 -81
- aigroup_econ_mcp/server.py +123 -57
- aigroup_econ_mcp/tools/__init__.py +6 -6
- aigroup_econ_mcp/tools/regression.py +213 -213
- aigroup_econ_mcp/tools/statistics.py +133 -133
- aigroup_econ_mcp/tools/time_series.py +259 -259
- aigroup_econ_mcp-0.1.6.dist-info/METADATA +448 -0
- aigroup_econ_mcp-0.1.6.dist-info/RECORD +12 -0
- {aigroup_econ_mcp-0.1.2.dist-info → aigroup_econ_mcp-0.1.6.dist-info}/licenses/LICENSE +20 -20
- aigroup_econ_mcp-0.1.2.dist-info/METADATA +0 -226
- aigroup_econ_mcp-0.1.2.dist-info/RECORD +0 -12
- {aigroup_econ_mcp-0.1.2.dist-info → aigroup_econ_mcp-0.1.6.dist-info}/WHEEL +0 -0
- {aigroup_econ_mcp-0.1.2.dist-info → aigroup_econ_mcp-0.1.6.dist-info}/entry_points.txt +0 -0
|
@@ -1,260 +1,260 @@
|
|
|
1
|
-
"""
|
|
2
|
-
时间序列分析工具
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import numpy as np
|
|
6
|
-
import pandas as pd
|
|
7
|
-
from typing import List, Dict, Any, Optional, Tuple
|
|
8
|
-
from pydantic import BaseModel
|
|
9
|
-
import statsmodels.api as sm
|
|
10
|
-
from statsmodels.tsa.stattools import adfuller, kpss, acf, pacf
|
|
11
|
-
from statsmodels.tsa.arima.model import ARIMA
|
|
12
|
-
from statsmodels.tsa.statespace.sarimax import SARIMAX
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class StationarityTest(BaseModel):
|
|
16
|
-
"""平稳性检验结果"""
|
|
17
|
-
adf_statistic: float
|
|
18
|
-
adf_pvalue: float
|
|
19
|
-
adf_critical_values: Dict[str, float]
|
|
20
|
-
kpss_statistic: float
|
|
21
|
-
kpss_pvalue: float
|
|
22
|
-
is_stationary: bool
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class ACFPACFResult(BaseModel):
|
|
26
|
-
"""自相关分析结果"""
|
|
27
|
-
acf_values: List[float]
|
|
28
|
-
pacf_values: List[float]
|
|
29
|
-
acf_confidence: List[Tuple[float, float]]
|
|
30
|
-
pacf_confidence: List[Tuple[float, float]]
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class ARIMAResult(BaseModel):
|
|
34
|
-
"""ARIMA模型结果"""
|
|
35
|
-
order: Tuple[int, int, int]
|
|
36
|
-
aic: float
|
|
37
|
-
bic: float
|
|
38
|
-
coefficients: Dict[str, float]
|
|
39
|
-
fitted_values: List[float]
|
|
40
|
-
residuals: List[float]
|
|
41
|
-
forecast: Optional[List[float]] = None
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def check_stationarity(data: List[float], max_lags: int = None) -> StationarityTest:
|
|
45
|
-
"""平稳性检验(ADF和KPSS)"""
|
|
46
|
-
series = pd.Series(data)
|
|
47
|
-
|
|
48
|
-
# ADF检验
|
|
49
|
-
adf_result = adfuller(series, maxlag=max_lags, autolag='AIC')
|
|
50
|
-
adf_stat, adf_pvalue = adf_result[0], adf_result[1]
|
|
51
|
-
adf_critical = adf_result[4]
|
|
52
|
-
|
|
53
|
-
# KPSS检验
|
|
54
|
-
kpss_result = kpss(series, regression='c', nlags='auto')
|
|
55
|
-
kpss_stat, kpss_pvalue = kpss_result[0], kpss_result[1]
|
|
56
|
-
|
|
57
|
-
# 综合判断平稳性
|
|
58
|
-
is_stationary = (adf_pvalue < 0.05) and (kpss_pvalue > 0.05)
|
|
59
|
-
|
|
60
|
-
return StationarityTest(
|
|
61
|
-
adf_statistic=adf_stat,
|
|
62
|
-
adf_pvalue=adf_pvalue,
|
|
63
|
-
adf_critical_values=adf_critical,
|
|
64
|
-
kpss_statistic=kpss_stat,
|
|
65
|
-
kpss_pvalue=kpss_pvalue,
|
|
66
|
-
is_stationary=is_stationary
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def calculate_acf_pacf(
|
|
71
|
-
data: List[float],
|
|
72
|
-
nlags: int = 20,
|
|
73
|
-
alpha: float = 0.05
|
|
74
|
-
) -> ACFPACFResult:
|
|
75
|
-
"""计算自相关和偏自相关函数"""
|
|
76
|
-
series = pd.Series(data)
|
|
77
|
-
|
|
78
|
-
# 计算ACF和PACF
|
|
79
|
-
acf_values = acf(series, nlags=nlags, alpha=alpha)
|
|
80
|
-
pacf_values = pacf(series, nlags=nlags, alpha=alpha)
|
|
81
|
-
|
|
82
|
-
# 构建置信区间
|
|
83
|
-
acf_conf = []
|
|
84
|
-
pacf_conf = []
|
|
85
|
-
|
|
86
|
-
for i in range(len(acf_values[1])):
|
|
87
|
-
acf_conf.append((acf_values[1][i][0], acf_values[1][i][1]))
|
|
88
|
-
pacf_conf.append((pacf_values[1][i][0], pacf_values[1][i][1]))
|
|
89
|
-
|
|
90
|
-
return ACFPACFResult(
|
|
91
|
-
acf_values=acf_values[0].tolist(),
|
|
92
|
-
pacf_values=pacf_values[0].tolist(),
|
|
93
|
-
acf_confidence=acf_conf,
|
|
94
|
-
pacf_confidence=pacf_conf
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def fit_arima_model(
|
|
99
|
-
data: List[float],
|
|
100
|
-
order: Tuple[int, int, int] = (1, 1, 1),
|
|
101
|
-
seasonal_order: Tuple[int, int, int, int] = (0, 0, 0, 0)
|
|
102
|
-
) -> ARIMAResult:
|
|
103
|
-
"""拟合ARIMA模型"""
|
|
104
|
-
series = pd.Series(data)
|
|
105
|
-
|
|
106
|
-
try:
|
|
107
|
-
if seasonal_order != (0, 0, 0, 0):
|
|
108
|
-
# 季节性ARIMA
|
|
109
|
-
model = SARIMAX(series, order=order, seasonal_order=seasonal_order)
|
|
110
|
-
else:
|
|
111
|
-
# 普通ARIMA
|
|
112
|
-
model = ARIMA(series, order=order)
|
|
113
|
-
|
|
114
|
-
fitted_model = model.fit()
|
|
115
|
-
|
|
116
|
-
return ARIMAResult(
|
|
117
|
-
order=order,
|
|
118
|
-
aic=fitted_model.aic,
|
|
119
|
-
bic=fitted_model.bic,
|
|
120
|
-
coefficients=fitted_model.params.to_dict(),
|
|
121
|
-
fitted_values=fitted_model.fittedvalues.tolist(),
|
|
122
|
-
residuals=fitted_model.resid.tolist()
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
except Exception as e:
|
|
126
|
-
raise ValueError(f"ARIMA模型拟合失败: {str(e)}")
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def find_best_arima_order(
|
|
130
|
-
data: List[float],
|
|
131
|
-
max_p: int = 3,
|
|
132
|
-
max_d: int = 2,
|
|
133
|
-
max_q: int = 3,
|
|
134
|
-
seasonal: bool = False,
|
|
135
|
-
max_P: int = 1,
|
|
136
|
-
max_D: int = 1,
|
|
137
|
-
max_Q: int = 1,
|
|
138
|
-
m: int = 12
|
|
139
|
-
) -> Dict[str, Any]:
|
|
140
|
-
"""自动寻找最佳ARIMA模型阶数"""
|
|
141
|
-
series = pd.Series(data)
|
|
142
|
-
best_aic = float('inf')
|
|
143
|
-
best_order = (0, 0, 0)
|
|
144
|
-
best_seasonal_order = (0, 0, 0, 0)
|
|
145
|
-
best_model = None
|
|
146
|
-
|
|
147
|
-
# 非季节性ARIMA
|
|
148
|
-
if not seasonal:
|
|
149
|
-
for p in range(max_p + 1):
|
|
150
|
-
for d in range(max_d + 1):
|
|
151
|
-
for q in range(max_q + 1):
|
|
152
|
-
try:
|
|
153
|
-
model = ARIMA(series, order=(p, d, q))
|
|
154
|
-
fitted_model = model.fit()
|
|
155
|
-
if fitted_model.aic < best_aic:
|
|
156
|
-
best_aic = fitted_model.aic
|
|
157
|
-
best_order = (p, d, q)
|
|
158
|
-
best_model = fitted_model
|
|
159
|
-
except:
|
|
160
|
-
continue
|
|
161
|
-
|
|
162
|
-
# 季节性ARIMA
|
|
163
|
-
else:
|
|
164
|
-
for p in range(max_p + 1):
|
|
165
|
-
for d in range(max_d + 1):
|
|
166
|
-
for q in range(max_q + 1):
|
|
167
|
-
for P in range(max_P + 1):
|
|
168
|
-
for D in range(max_D + 1):
|
|
169
|
-
for Q in range(max_Q + 1):
|
|
170
|
-
try:
|
|
171
|
-
seasonal_order = (P, D, Q, m)
|
|
172
|
-
model = SARIMAX(series, order=(p, d, q), seasonal_order=seasonal_order)
|
|
173
|
-
fitted_model = model.fit()
|
|
174
|
-
if fitted_model.aic < best_aic:
|
|
175
|
-
best_aic = fitted_model.aic
|
|
176
|
-
best_order = (p, d, q)
|
|
177
|
-
best_seasonal_order = seasonal_order
|
|
178
|
-
best_model = fitted_model
|
|
179
|
-
except:
|
|
180
|
-
continue
|
|
181
|
-
|
|
182
|
-
if best_model is None:
|
|
183
|
-
raise ValueError("无法找到合适的ARIMA模型")
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
"best_order": best_order,
|
|
187
|
-
"best_seasonal_order": best_seasonal_order if seasonal else None,
|
|
188
|
-
"best_aic": best_aic,
|
|
189
|
-
"best_bic": best_model.bic,
|
|
190
|
-
"coefficients": best_model.params.to_dict(),
|
|
191
|
-
"model_summary": str(best_model.summary())
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def decompose_time_series(
|
|
196
|
-
data: List[float],
|
|
197
|
-
model: str = "additive",
|
|
198
|
-
period: Optional[int] = None
|
|
199
|
-
) -> Dict[str, List[float]]:
|
|
200
|
-
"""时间序列分解"""
|
|
201
|
-
series = pd.Series(data)
|
|
202
|
-
|
|
203
|
-
if period is None:
|
|
204
|
-
# 自动检测周期(简单方法)
|
|
205
|
-
from statsmodels.tsa.seasonal import seasonal_decompose
|
|
206
|
-
decomposition = seasonal_decompose(series, model=model, extrapolate_trend='freq')
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
"trend": decomposition.trend.fillna(0).tolist(),
|
|
210
|
-
"seasonal": decomposition.seasonal.fillna(0).tolist(),
|
|
211
|
-
"residual": decomposition.resid.fillna(0).tolist(),
|
|
212
|
-
"observed": decomposition.observed.tolist()
|
|
213
|
-
}
|
|
214
|
-
else:
|
|
215
|
-
# 指定周期的分解
|
|
216
|
-
decomposition = seasonal_decompose(series, model=model, period=period)
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
"trend": decomposition.trend.fillna(0).tolist(),
|
|
220
|
-
"seasonal": decomposition.seasonal.fillna(0).tolist(),
|
|
221
|
-
"residual": decomposition.resid.fillna(0).tolist(),
|
|
222
|
-
"observed": decomposition.observed.tolist()
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
def forecast_arima(
|
|
227
|
-
data: List[float],
|
|
228
|
-
order: Tuple[int, int, int] = (1, 1, 1),
|
|
229
|
-
steps: int = 10,
|
|
230
|
-
seasonal_order: Tuple[int, int, int, int] = (0, 0, 0, 0)
|
|
231
|
-
) -> Dict[str, Any]:
|
|
232
|
-
"""ARIMA模型预测"""
|
|
233
|
-
series = pd.Series(data)
|
|
234
|
-
|
|
235
|
-
try:
|
|
236
|
-
if seasonal_order != (0, 0, 0, 0):
|
|
237
|
-
model = SARIMAX(series, order=order, seasonal_order=seasonal_order)
|
|
238
|
-
else:
|
|
239
|
-
model = ARIMA(series, order=order)
|
|
240
|
-
|
|
241
|
-
fitted_model = model.fit()
|
|
242
|
-
|
|
243
|
-
# 生成预测
|
|
244
|
-
forecast_result = fitted_model.forecast(steps=steps)
|
|
245
|
-
forecast_values = forecast_result.tolist()
|
|
246
|
-
|
|
247
|
-
# 预测置信区间
|
|
248
|
-
pred_conf = fitted_model.get_forecast(steps=steps)
|
|
249
|
-
conf_int = pred_conf.conf_int()
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
"forecast": forecast_values,
|
|
253
|
-
"conf_int_lower": conf_int.iloc[:, 0].tolist(),
|
|
254
|
-
"conf_int_upper": conf_int.iloc[:, 1].tolist(),
|
|
255
|
-
"model_aic": fitted_model.aic,
|
|
256
|
-
"model_bic": fitted_model.bic
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
except Exception as e:
|
|
1
|
+
"""
|
|
2
|
+
时间序列分析工具
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from typing import List, Dict, Any, Optional, Tuple
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
import statsmodels.api as sm
|
|
10
|
+
from statsmodels.tsa.stattools import adfuller, kpss, acf, pacf
|
|
11
|
+
from statsmodels.tsa.arima.model import ARIMA
|
|
12
|
+
from statsmodels.tsa.statespace.sarimax import SARIMAX
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StationarityTest(BaseModel):
|
|
16
|
+
"""平稳性检验结果"""
|
|
17
|
+
adf_statistic: float
|
|
18
|
+
adf_pvalue: float
|
|
19
|
+
adf_critical_values: Dict[str, float]
|
|
20
|
+
kpss_statistic: float
|
|
21
|
+
kpss_pvalue: float
|
|
22
|
+
is_stationary: bool
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ACFPACFResult(BaseModel):
|
|
26
|
+
"""自相关分析结果"""
|
|
27
|
+
acf_values: List[float]
|
|
28
|
+
pacf_values: List[float]
|
|
29
|
+
acf_confidence: List[Tuple[float, float]]
|
|
30
|
+
pacf_confidence: List[Tuple[float, float]]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ARIMAResult(BaseModel):
|
|
34
|
+
"""ARIMA模型结果"""
|
|
35
|
+
order: Tuple[int, int, int]
|
|
36
|
+
aic: float
|
|
37
|
+
bic: float
|
|
38
|
+
coefficients: Dict[str, float]
|
|
39
|
+
fitted_values: List[float]
|
|
40
|
+
residuals: List[float]
|
|
41
|
+
forecast: Optional[List[float]] = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def check_stationarity(data: List[float], max_lags: int = None) -> StationarityTest:
|
|
45
|
+
"""平稳性检验(ADF和KPSS)"""
|
|
46
|
+
series = pd.Series(data)
|
|
47
|
+
|
|
48
|
+
# ADF检验
|
|
49
|
+
adf_result = adfuller(series, maxlag=max_lags, autolag='AIC')
|
|
50
|
+
adf_stat, adf_pvalue = adf_result[0], adf_result[1]
|
|
51
|
+
adf_critical = adf_result[4]
|
|
52
|
+
|
|
53
|
+
# KPSS检验
|
|
54
|
+
kpss_result = kpss(series, regression='c', nlags='auto')
|
|
55
|
+
kpss_stat, kpss_pvalue = kpss_result[0], kpss_result[1]
|
|
56
|
+
|
|
57
|
+
# 综合判断平稳性
|
|
58
|
+
is_stationary = (adf_pvalue < 0.05) and (kpss_pvalue > 0.05)
|
|
59
|
+
|
|
60
|
+
return StationarityTest(
|
|
61
|
+
adf_statistic=adf_stat,
|
|
62
|
+
adf_pvalue=adf_pvalue,
|
|
63
|
+
adf_critical_values=adf_critical,
|
|
64
|
+
kpss_statistic=kpss_stat,
|
|
65
|
+
kpss_pvalue=kpss_pvalue,
|
|
66
|
+
is_stationary=is_stationary
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def calculate_acf_pacf(
|
|
71
|
+
data: List[float],
|
|
72
|
+
nlags: int = 20,
|
|
73
|
+
alpha: float = 0.05
|
|
74
|
+
) -> ACFPACFResult:
|
|
75
|
+
"""计算自相关和偏自相关函数"""
|
|
76
|
+
series = pd.Series(data)
|
|
77
|
+
|
|
78
|
+
# 计算ACF和PACF
|
|
79
|
+
acf_values = acf(series, nlags=nlags, alpha=alpha)
|
|
80
|
+
pacf_values = pacf(series, nlags=nlags, alpha=alpha)
|
|
81
|
+
|
|
82
|
+
# 构建置信区间
|
|
83
|
+
acf_conf = []
|
|
84
|
+
pacf_conf = []
|
|
85
|
+
|
|
86
|
+
for i in range(len(acf_values[1])):
|
|
87
|
+
acf_conf.append((acf_values[1][i][0], acf_values[1][i][1]))
|
|
88
|
+
pacf_conf.append((pacf_values[1][i][0], pacf_values[1][i][1]))
|
|
89
|
+
|
|
90
|
+
return ACFPACFResult(
|
|
91
|
+
acf_values=acf_values[0].tolist(),
|
|
92
|
+
pacf_values=pacf_values[0].tolist(),
|
|
93
|
+
acf_confidence=acf_conf,
|
|
94
|
+
pacf_confidence=pacf_conf
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def fit_arima_model(
|
|
99
|
+
data: List[float],
|
|
100
|
+
order: Tuple[int, int, int] = (1, 1, 1),
|
|
101
|
+
seasonal_order: Tuple[int, int, int, int] = (0, 0, 0, 0)
|
|
102
|
+
) -> ARIMAResult:
|
|
103
|
+
"""拟合ARIMA模型"""
|
|
104
|
+
series = pd.Series(data)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
if seasonal_order != (0, 0, 0, 0):
|
|
108
|
+
# 季节性ARIMA
|
|
109
|
+
model = SARIMAX(series, order=order, seasonal_order=seasonal_order)
|
|
110
|
+
else:
|
|
111
|
+
# 普通ARIMA
|
|
112
|
+
model = ARIMA(series, order=order)
|
|
113
|
+
|
|
114
|
+
fitted_model = model.fit()
|
|
115
|
+
|
|
116
|
+
return ARIMAResult(
|
|
117
|
+
order=order,
|
|
118
|
+
aic=fitted_model.aic,
|
|
119
|
+
bic=fitted_model.bic,
|
|
120
|
+
coefficients=fitted_model.params.to_dict(),
|
|
121
|
+
fitted_values=fitted_model.fittedvalues.tolist(),
|
|
122
|
+
residuals=fitted_model.resid.tolist()
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
except Exception as e:
|
|
126
|
+
raise ValueError(f"ARIMA模型拟合失败: {str(e)}")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def find_best_arima_order(
|
|
130
|
+
data: List[float],
|
|
131
|
+
max_p: int = 3,
|
|
132
|
+
max_d: int = 2,
|
|
133
|
+
max_q: int = 3,
|
|
134
|
+
seasonal: bool = False,
|
|
135
|
+
max_P: int = 1,
|
|
136
|
+
max_D: int = 1,
|
|
137
|
+
max_Q: int = 1,
|
|
138
|
+
m: int = 12
|
|
139
|
+
) -> Dict[str, Any]:
|
|
140
|
+
"""自动寻找最佳ARIMA模型阶数"""
|
|
141
|
+
series = pd.Series(data)
|
|
142
|
+
best_aic = float('inf')
|
|
143
|
+
best_order = (0, 0, 0)
|
|
144
|
+
best_seasonal_order = (0, 0, 0, 0)
|
|
145
|
+
best_model = None
|
|
146
|
+
|
|
147
|
+
# 非季节性ARIMA
|
|
148
|
+
if not seasonal:
|
|
149
|
+
for p in range(max_p + 1):
|
|
150
|
+
for d in range(max_d + 1):
|
|
151
|
+
for q in range(max_q + 1):
|
|
152
|
+
try:
|
|
153
|
+
model = ARIMA(series, order=(p, d, q))
|
|
154
|
+
fitted_model = model.fit()
|
|
155
|
+
if fitted_model.aic < best_aic:
|
|
156
|
+
best_aic = fitted_model.aic
|
|
157
|
+
best_order = (p, d, q)
|
|
158
|
+
best_model = fitted_model
|
|
159
|
+
except:
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
# 季节性ARIMA
|
|
163
|
+
else:
|
|
164
|
+
for p in range(max_p + 1):
|
|
165
|
+
for d in range(max_d + 1):
|
|
166
|
+
for q in range(max_q + 1):
|
|
167
|
+
for P in range(max_P + 1):
|
|
168
|
+
for D in range(max_D + 1):
|
|
169
|
+
for Q in range(max_Q + 1):
|
|
170
|
+
try:
|
|
171
|
+
seasonal_order = (P, D, Q, m)
|
|
172
|
+
model = SARIMAX(series, order=(p, d, q), seasonal_order=seasonal_order)
|
|
173
|
+
fitted_model = model.fit()
|
|
174
|
+
if fitted_model.aic < best_aic:
|
|
175
|
+
best_aic = fitted_model.aic
|
|
176
|
+
best_order = (p, d, q)
|
|
177
|
+
best_seasonal_order = seasonal_order
|
|
178
|
+
best_model = fitted_model
|
|
179
|
+
except:
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
if best_model is None:
|
|
183
|
+
raise ValueError("无法找到合适的ARIMA模型")
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
"best_order": best_order,
|
|
187
|
+
"best_seasonal_order": best_seasonal_order if seasonal else None,
|
|
188
|
+
"best_aic": best_aic,
|
|
189
|
+
"best_bic": best_model.bic,
|
|
190
|
+
"coefficients": best_model.params.to_dict(),
|
|
191
|
+
"model_summary": str(best_model.summary())
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def decompose_time_series(
|
|
196
|
+
data: List[float],
|
|
197
|
+
model: str = "additive",
|
|
198
|
+
period: Optional[int] = None
|
|
199
|
+
) -> Dict[str, List[float]]:
|
|
200
|
+
"""时间序列分解"""
|
|
201
|
+
series = pd.Series(data)
|
|
202
|
+
|
|
203
|
+
if period is None:
|
|
204
|
+
# 自动检测周期(简单方法)
|
|
205
|
+
from statsmodels.tsa.seasonal import seasonal_decompose
|
|
206
|
+
decomposition = seasonal_decompose(series, model=model, extrapolate_trend='freq')
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
"trend": decomposition.trend.fillna(0).tolist(),
|
|
210
|
+
"seasonal": decomposition.seasonal.fillna(0).tolist(),
|
|
211
|
+
"residual": decomposition.resid.fillna(0).tolist(),
|
|
212
|
+
"observed": decomposition.observed.tolist()
|
|
213
|
+
}
|
|
214
|
+
else:
|
|
215
|
+
# 指定周期的分解
|
|
216
|
+
decomposition = seasonal_decompose(series, model=model, period=period)
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
"trend": decomposition.trend.fillna(0).tolist(),
|
|
220
|
+
"seasonal": decomposition.seasonal.fillna(0).tolist(),
|
|
221
|
+
"residual": decomposition.resid.fillna(0).tolist(),
|
|
222
|
+
"observed": decomposition.observed.tolist()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def forecast_arima(
|
|
227
|
+
data: List[float],
|
|
228
|
+
order: Tuple[int, int, int] = (1, 1, 1),
|
|
229
|
+
steps: int = 10,
|
|
230
|
+
seasonal_order: Tuple[int, int, int, int] = (0, 0, 0, 0)
|
|
231
|
+
) -> Dict[str, Any]:
|
|
232
|
+
"""ARIMA模型预测"""
|
|
233
|
+
series = pd.Series(data)
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
if seasonal_order != (0, 0, 0, 0):
|
|
237
|
+
model = SARIMAX(series, order=order, seasonal_order=seasonal_order)
|
|
238
|
+
else:
|
|
239
|
+
model = ARIMA(series, order=order)
|
|
240
|
+
|
|
241
|
+
fitted_model = model.fit()
|
|
242
|
+
|
|
243
|
+
# 生成预测
|
|
244
|
+
forecast_result = fitted_model.forecast(steps=steps)
|
|
245
|
+
forecast_values = forecast_result.tolist()
|
|
246
|
+
|
|
247
|
+
# 预测置信区间
|
|
248
|
+
pred_conf = fitted_model.get_forecast(steps=steps)
|
|
249
|
+
conf_int = pred_conf.conf_int()
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
"forecast": forecast_values,
|
|
253
|
+
"conf_int_lower": conf_int.iloc[:, 0].tolist(),
|
|
254
|
+
"conf_int_upper": conf_int.iloc[:, 1].tolist(),
|
|
255
|
+
"model_aic": fitted_model.aic,
|
|
256
|
+
"model_bic": fitted_model.bic
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
except Exception as e:
|
|
260
260
|
raise ValueError(f"ARIMA预测失败: {str(e)}")
|