Coverage for src\time_series_analyzer\formatters.py: 35%
223 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-23 11:57 +0800
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-23 11:57 +0800
1"""
2输出格式化模块
4支持多种输出格式:LaTeX数学表达式、纯文本、JSON结构化数据等。
5适用于报告和论文引用。
6"""
8import json
9from typing import Dict, Any, Optional, List, Union
10from datetime import datetime
11import sympy as sp
12from sympy import latex, pretty
14from .models import ARIMAModel, SeasonalARIMAModel
15from .transfer_function import TransferFunction, TransferFunctionDeriver
18class OutputFormatter:
19 """输出格式化器"""
21 def __init__(self, precision: int = 4):
22 """
23 初始化格式化器
25 Args:
26 precision: 数值精度
27 """
28 self.precision = precision
30 def format_latex(self, model: Union[ARIMAModel, SeasonalARIMAModel],
31 include_transfer_function: bool = True,
32 include_analysis: bool = False) -> str:
33 """
34 生成LaTeX格式的输出
36 Args:
37 model: 时间序列模型
38 include_transfer_function: 是否包含传递函数
39 include_analysis: 是否包含分析结果
41 Returns:
42 LaTeX格式的字符串
43 """
44 latex_output = []
46 # 文档头部
47 latex_output.append("\\documentclass{article}")
48 latex_output.append("\\usepackage{amsmath}")
49 latex_output.append("\\usepackage{amssymb}")
50 latex_output.append("\\usepackage{amsfonts}")
51 latex_output.append("\\begin{document}")
52 latex_output.append("")
54 # 模型标题
55 latex_output.append(f"\\section{{{model.name} 模型分析}}")
56 latex_output.append("")
58 # 模型定义
59 latex_output.append("\\subsection{模型定义}")
61 if isinstance(model, SeasonalARIMAModel):
62 latex_output.append(self._format_sarima_latex(model))
63 else:
64 latex_output.append(self._format_arima_latex(model))
66 latex_output.append("")
68 # 传递函数
69 if include_transfer_function:
70 latex_output.append("\\subsection{传递函数}")
71 deriver = TransferFunctionDeriver()
72 transfer_func = deriver.derive_transfer_function(model)
73 latex_output.append(self._format_transfer_function_latex(transfer_func))
74 latex_output.append("")
76 # 分析结果
77 if include_analysis:
78 latex_output.append("\\subsection{稳定性分析}")
79 deriver = TransferFunctionDeriver()
80 stability = deriver.analyze_stability(model)
81 latex_output.append(self._format_stability_latex(stability))
82 latex_output.append("")
84 latex_output.append("\\end{document}")
86 return "\n".join(latex_output)
88 def _format_arima_latex(self, model: ARIMAModel) -> str:
89 """格式化ARIMA模型的LaTeX表示"""
90 lines = []
92 # 模型方程
93 if model.p > 0 and model.q > 0:
94 # 完整的ARIMA方程
95 ar_terms = []
96 for i, param in enumerate(model.ar_params):
97 if isinstance(param, (int, float)):
98 if param >= 0:
99 ar_terms.append(f"{param:.{self.precision}f}X_{{t-{i+1}}}")
100 else:
101 ar_terms.append(f"{param:.{self.precision}f}X_{{t-{i+1}}}")
102 else:
103 ar_terms.append(f"\\phi_{{{i+1}}}X_{{t-{i+1}}}")
105 ma_terms = []
106 for i, param in enumerate(model.ma_params):
107 if isinstance(param, (int, float)):
108 if param >= 0:
109 ma_terms.append(f"+{param:.{self.precision}f}\\varepsilon_{{t-{i+1}}}")
110 else:
111 ma_terms.append(f"{param:.{self.precision}f}\\varepsilon_{{t-{i+1}}}")
112 else:
113 ma_terms.append(f"+\\theta_{{{i+1}}}\\varepsilon_{{t-{i+1}}}")
115 if model.d > 0:
116 lines.append("差分后的序列:")
117 lines.append(f"$\\nabla^{{{model.d}}}X_t = X_t - " + " - ".join(ar_terms) +
118 " = \\varepsilon_t " + "".join(ma_terms) + "$")
119 else:
120 lines.append("模型方程:")
121 lines.append(f"$X_t - " + " - ".join(ar_terms) +
122 " = \\varepsilon_t " + "".join(ma_terms) + "$")
124 # 滞后算子形式
125 lines.append("")
126 lines.append("滞后算子形式:")
128 # AR多项式
129 if model.p > 0:
130 ar_poly_latex = self._polynomial_to_latex(model.get_ar_polynomial())
131 lines.append(f"$\\phi(B) = {ar_poly_latex}$")
133 # MA多项式
134 if model.q > 0:
135 ma_poly_latex = self._polynomial_to_latex(model.get_ma_polynomial())
136 lines.append(f"$\\theta(B) = {ma_poly_latex}$")
138 # 差分算子
139 if model.d > 0:
140 lines.append(f"$(1-B)^{{{model.d}}}X_t = \\theta(B)\\varepsilon_t$")
142 return "\n".join(lines)
144 def _format_sarima_latex(self, model: SeasonalARIMAModel) -> str:
145 """格式化SARIMA模型的LaTeX表示"""
146 lines = []
148 lines.append("季节性ARIMA模型方程:")
149 lines.append("")
151 # 滞后算子形式
152 components = []
154 if model.p > 0:
155 components.append("\\phi(B)")
156 if model.P > 0:
157 components.append(f"\\Phi(B^{{{model.m}}})")
158 if model.d > 0:
159 components.append(f"(1-B)^{{{model.d}}}")
160 if model.D > 0:
161 components.append(f"(1-B^{{{model.m}}})^{{{model.D}}}")
163 left_side = "".join(components) + "X_t"
165 right_components = []
166 if model.q > 0:
167 right_components.append("\\theta(B)")
168 if model.Q > 0:
169 right_components.append(f"\\Theta(B^{{{model.m}}})")
171 right_side = "".join(right_components) + "\\varepsilon_t"
173 lines.append(f"${left_side} = {right_side}$")
174 lines.append("")
176 # 各多项式定义
177 if model.p > 0:
178 ar_poly_latex = self._polynomial_to_latex(model.get_ar_polynomial())
179 lines.append(f"$\\phi(B) = {ar_poly_latex}$")
181 if model.q > 0:
182 ma_poly_latex = self._polynomial_to_latex(model.get_ma_polynomial())
183 lines.append(f"$\\theta(B) = {ma_poly_latex}$")
185 if model.P > 0:
186 seasonal_ar_latex = self._polynomial_to_latex(model.get_seasonal_ar_polynomial())
187 lines.append(f"$\\Phi(B^{{{model.m}}}) = {seasonal_ar_latex}$")
189 if model.Q > 0:
190 seasonal_ma_latex = self._polynomial_to_latex(model.get_seasonal_ma_polynomial())
191 lines.append(f"$\\Theta(B^{{{model.m}}}) = {seasonal_ma_latex}$")
193 return "\n".join(lines)
195 def _polynomial_to_latex(self, poly) -> str:
196 """将多项式转换为LaTeX格式"""
197 try:
198 return latex(poly.as_expr())
199 except:
200 return str(poly)
202 def _format_transfer_function_latex(self, transfer_func: TransferFunction) -> str:
203 """格式化传递函数的LaTeX表示"""
204 lines = []
206 lines.append("传递函数定义:")
207 lines.append("")
209 num_latex = latex(transfer_func.numerator.as_expr())
210 den_latex = latex(transfer_func.denominator.as_expr())
212 lines.append(f"$H(B) = \\frac{{{num_latex}}}{{{den_latex}}}$")
213 lines.append("")
215 # 极点和零点
216 poles = transfer_func.get_poles()
217 zeros = transfer_func.get_zeros()
219 if poles:
220 lines.append("极点:")
221 for i, pole in enumerate(poles):
222 if abs(pole.imag) < 1e-10:
223 lines.append(f"$p_{{{i+1}}} = {pole.real:.{self.precision}f}$")
224 else:
225 lines.append(f"$p_{{{i+1}}} = {pole.real:.{self.precision}f} {'+' if pole.imag >= 0 else ''}{pole.imag:.{self.precision}f}i$")
227 if zeros:
228 lines.append("")
229 lines.append("零点:")
230 for i, zero in enumerate(zeros):
231 if abs(zero.imag) < 1e-10:
232 lines.append(f"$z_{{{i+1}}} = {zero.real:.{self.precision}f}$")
233 else:
234 lines.append(f"$z_{{{i+1}}} = {zero.real:.{self.precision}f} {'+' if zero.imag >= 0 else ''}{zero.imag:.{self.precision}f}i$")
236 return "\n".join(lines)
238 def _format_stability_latex(self, stability: Dict[str, Any]) -> str:
239 """格式化稳定性分析的LaTeX表示"""
240 lines = []
242 is_stable = stability.get("is_stable", False)
243 max_magnitude = stability.get("max_pole_magnitude", 0)
245 lines.append(f"系统稳定性: {'稳定' if is_stable else '不稳定'}")
246 lines.append("")
247 lines.append(f"最大极点模长: ${max_magnitude:.{self.precision}f}$")
249 if not is_stable:
250 lines.append("")
251 lines.append("\\textbf{警告:} 系统不稳定,存在模长大于等于1的极点。")
253 return "\n".join(lines)
255 def format_plain_text(self, model: Union[ARIMAModel, SeasonalARIMAModel],
256 include_transfer_function: bool = True,
257 include_analysis: bool = False) -> str:
258 """
259 生成纯文本格式的输出
261 Args:
262 model: 时间序列模型
263 include_transfer_function: 是否包含传递函数
264 include_analysis: 是否包含分析结果
266 Returns:
267 纯文本格式的字符串
268 """
269 output = []
271 # 标题
272 output.append("=" * 50)
273 output.append(f"{model.name} 模型分析")
274 output.append("=" * 50)
275 output.append("")
277 # 模型参数
278 output.append("模型参数:")
279 output.append(f" p (AR阶数): {model.p}")
280 output.append(f" d (差分阶数): {model.d}")
281 output.append(f" q (MA阶数): {model.q}")
283 if isinstance(model, SeasonalARIMAModel):
284 output.append(f" P (季节性AR阶数): {model.P}")
285 output.append(f" D (季节性差分阶数): {model.D}")
286 output.append(f" Q (季节性MA阶数): {model.Q}")
287 output.append(f" m (季节性周期): {model.m}")
289 output.append("")
291 # 参数值
292 if model.ar_params:
293 output.append("AR参数:")
294 for i, param in enumerate(model.ar_params):
295 output.append(f" φ_{i+1} = {param}")
297 if model.ma_params:
298 output.append("MA参数:")
299 for i, param in enumerate(model.ma_params):
300 output.append(f" θ_{i+1} = {param}")
302 if isinstance(model, SeasonalARIMAModel):
303 if model.seasonal_ar_params:
304 output.append("季节性AR参数:")
305 for i, param in enumerate(model.seasonal_ar_params):
306 output.append(f" Φ_{i+1} = {param}")
308 if model.seasonal_ma_params:
309 output.append("季节性MA参数:")
310 for i, param in enumerate(model.seasonal_ma_params):
311 output.append(f" Θ_{i+1} = {param}")
313 output.append("")
315 # 传递函数
316 if include_transfer_function:
317 output.append("传递函数:")
318 output.append("-" * 30)
319 deriver = TransferFunctionDeriver()
320 transfer_func = deriver.derive_transfer_function(model)
322 output.append(f"H(B) = ({transfer_func.numerator.as_expr()}) / ({transfer_func.denominator.as_expr()})")
323 output.append("")
325 # 极点和零点
326 poles = transfer_func.get_poles()
327 zeros = transfer_func.get_zeros()
329 if poles:
330 output.append("极点:")
331 for i, pole in enumerate(poles):
332 output.append(f" p_{i+1} = {pole:.{self.precision}f}")
334 if zeros:
335 output.append("零点:")
336 for i, zero in enumerate(zeros):
337 output.append(f" z_{i+1} = {zero:.{self.precision}f}")
339 output.append("")
341 # 稳定性分析
342 if include_analysis:
343 output.append("稳定性分析:")
344 output.append("-" * 30)
345 deriver = TransferFunctionDeriver()
346 stability = deriver.analyze_stability(model)
348 is_stable = stability.get("is_stable", False)
349 max_magnitude = stability.get("max_pole_magnitude", 0)
351 output.append(f"系统稳定性: {'稳定' if is_stable else '不稳定'}")
352 output.append(f"最大极点模长: {max_magnitude:.{self.precision}f}")
354 if not is_stable:
355 output.append("警告: 系统不稳定,存在模长大于等于1的极点。")
357 output.append("")
359 return "\n".join(output)
361 def format_json(self, model: Union[ARIMAModel, SeasonalARIMAModel],
362 include_transfer_function: bool = True,
363 include_analysis: bool = False) -> str:
364 """
365 生成JSON格式的输出
367 Args:
368 model: 时间序列模型
369 include_transfer_function: 是否包含传递函数
370 include_analysis: 是否包含分析结果
372 Returns:
373 JSON格式的字符串
374 """
375 result = {
376 "timestamp": datetime.now().isoformat(),
377 "model": model.to_dict()
378 }
380 if include_transfer_function:
381 deriver = TransferFunctionDeriver()
382 transfer_func = deriver.derive_transfer_function(model)
384 result["transfer_function"] = {
385 "numerator": str(transfer_func.numerator.as_expr()),
386 "denominator": str(transfer_func.denominator.as_expr()),
387 "poles": [{"real": pole.real, "imag": pole.imag} for pole in transfer_func.get_poles()],
388 "zeros": [{"real": zero.real, "imag": zero.imag} for zero in transfer_func.get_zeros()]
389 }
391 if include_analysis:
392 deriver = TransferFunctionDeriver()
393 stability = deriver.analyze_stability(model)
395 # 转换复数为字典格式
396 result["stability_analysis"] = {
397 "is_stable": stability["is_stable"],
398 "max_pole_magnitude": stability["max_pole_magnitude"],
399 "stability_margin": stability["stability_margin"],
400 "poles": [{"real": pole.real, "imag": pole.imag} for pole in stability["poles"]],
401 "zeros": [{"real": zero.real, "imag": zero.imag} for zero in stability["zeros"]]
402 }
404 return json.dumps(result, indent=2, ensure_ascii=False)