Coverage for src\time_series_analyzer\cli.py: 0%
140 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使用click框架实现用户友好的命令行工具。
5"""
7import click
8from pathlib import Path
9from typing import Optional
10import sys
12from .parsers import ModelParser
13from .formatters import OutputFormatter
14from .transfer_function import TransferFunctionDeriver
15from .models import ARIMAModel, SeasonalARIMAModel
18@click.group()
19@click.version_option(version="0.1.0")
20def main():
21 """
22 时序模型传递函数分析器
24 自动推导ARIMA和SARIMA模型的传递函数表达式。
25 """
26 pass
29@main.command()
30@click.option('--model', '-m',
31 help='模型字符串,如 "ARIMA(2,1,1)" 或 "SARIMA(2,1,1)(1,1,1,12)"')
32@click.option('--file', '-f', type=click.Path(exists=True),
33 help='包含模型参数的配置文件 (JSON/YAML)')
34@click.option('--interactive', '-i', is_flag=True,
35 help='交互式输入模型参数')
36@click.option('--output', '-o', type=click.Path(),
37 help='输出文件路径')
38@click.option('--format', 'output_format',
39 type=click.Choice(['text', 'latex', 'json'], case_sensitive=False),
40 default='text', help='输出格式')
41@click.option('--include-analysis', is_flag=True,
42 help='包含稳定性分析')
43@click.option('--precision', type=int, default=4,
44 help='数值精度 (小数位数)')
45def analyze(model: Optional[str], file: Optional[str], interactive: bool,
46 output: Optional[str], output_format: str, include_analysis: bool,
47 precision: int):
48 """
49 分析时间序列模型并生成传递函数
50 """
51 try:
52 # 解析模型
53 if interactive:
54 model_obj = ModelParser.parse_interactive()
55 elif file:
56 model_obj = ModelParser.parse_from_file(file)
57 elif model:
58 model_obj = ModelParser.parse_from_string(model)
59 else:
60 click.echo("错误: 必须指定模型参数 (使用 --model, --file 或 --interactive)", err=True)
61 sys.exit(1)
63 # 创建格式化器
64 formatter = OutputFormatter(precision=precision)
66 # 生成输出
67 if output_format.lower() == 'latex':
68 result = formatter.format_latex(
69 model_obj,
70 include_transfer_function=True,
71 include_analysis=include_analysis
72 )
73 elif output_format.lower() == 'json':
74 result = formatter.format_json(
75 model_obj,
76 include_transfer_function=True,
77 include_analysis=include_analysis
78 )
79 else: # text
80 result = formatter.format_plain_text(
81 model_obj,
82 include_transfer_function=True,
83 include_analysis=include_analysis
84 )
86 # 输出结果
87 if output:
88 with open(output, 'w', encoding='utf-8') as f:
89 f.write(result)
90 click.echo(f"结果已保存到: {output}")
91 else:
92 click.echo(result)
94 except Exception as e:
95 click.echo(f"错误: {e}", err=True)
96 sys.exit(1)
99@main.command()
100@click.option('--model', '-m', required=True,
101 help='模型字符串,如 "ARIMA(2,1,1)" 或 "SARIMA(2,1,1)(1,1,1,12)"')
102@click.option('--max-lag', type=int, default=20,
103 help='最大滞后阶数')
104@click.option('--output', '-o', type=click.Path(),
105 help='输出文件路径')
106@click.option('--format', 'output_format',
107 type=click.Choice(['text', 'json'], case_sensitive=False),
108 default='text', help='输出格式')
109def impulse(model: str, max_lag: int, output: Optional[str], output_format: str):
110 """
111 计算脉冲响应函数
112 """
113 try:
114 # 解析模型
115 model_obj = ModelParser.parse_from_string(model)
117 # 计算脉冲响应
118 deriver = TransferFunctionDeriver()
119 impulse_response = deriver.derive_impulse_response(model_obj, max_lag)
121 # 格式化输出
122 if output_format.lower() == 'json':
123 import json
124 result = json.dumps({
125 "model": model_obj.to_dict(),
126 "impulse_response": {str(k): str(v) for k, v in impulse_response.items()},
127 "max_lag": max_lag
128 }, indent=2, ensure_ascii=False)
129 else:
130 lines = [f"{model_obj.name} 脉冲响应函数", "=" * 40]
131 for lag, coeff in impulse_response.items():
132 lines.append(f"h[{lag}] = {coeff}")
133 result = "\n".join(lines)
135 # 输出结果
136 if output:
137 with open(output, 'w', encoding='utf-8') as f:
138 f.write(result)
139 click.echo(f"脉冲响应已保存到: {output}")
140 else:
141 click.echo(result)
143 except Exception as e:
144 click.echo(f"错误: {e}", err=True)
145 sys.exit(1)
148@main.command()
149@click.option('--model', '-m', required=True,
150 help='模型字符串,如 "ARIMA(2,1,1)" 或 "SARIMA(2,1,1)(1,1,1,12)"')
151@click.option('--frequencies', type=str, default="0,0.1,0.2,0.3,0.4,0.5",
152 help='频率列表 (逗号分隔)')
153@click.option('--output', '-o', type=click.Path(),
154 help='输出文件路径')
155@click.option('--format', 'output_format',
156 type=click.Choice(['text', 'json'], case_sensitive=False),
157 default='text', help='输出格式')
158def frequency(model: str, frequencies: str, output: Optional[str], output_format: str):
159 """
160 计算频率响应
161 """
162 try:
163 # 解析模型
164 model_obj = ModelParser.parse_from_string(model)
166 # 解析频率
167 freq_list = [float(f.strip()) for f in frequencies.split(',')]
169 # 计算频率响应
170 deriver = TransferFunctionDeriver()
171 freq_response = deriver.get_frequency_response(model_obj, freq_list)
173 # 格式化输出
174 if output_format.lower() == 'json':
175 import json
176 result = json.dumps({
177 "model": model_obj.to_dict(),
178 "frequency_response": {
179 "frequencies": freq_response["frequencies"],
180 "magnitudes": [float(m) for m in freq_response["magnitudes"]],
181 "phases": [float(p) for p in freq_response["phases"]],
182 "magnitude_db": [float(m) for m in freq_response["magnitude_db"]]
183 }
184 }, indent=2, ensure_ascii=False)
185 else:
186 lines = [f"{model_obj.name} 频率响应", "=" * 40]
187 lines.append(f"{'频率':<10} {'幅度':<15} {'相位':<15} {'幅度(dB)':<15}")
188 lines.append("-" * 60)
190 for i, freq in enumerate(freq_response["frequencies"]):
191 mag = freq_response["magnitudes"][i]
192 phase = freq_response["phases"][i]
193 mag_db = freq_response["magnitude_db"][i]
194 lines.append(f"{freq:<10.3f} {mag:<15.6f} {phase:<15.6f} {mag_db:<15.3f}")
196 result = "\n".join(lines)
198 # 输出结果
199 if output:
200 with open(output, 'w', encoding='utf-8') as f:
201 f.write(result)
202 click.echo(f"频率响应已保存到: {output}")
203 else:
204 click.echo(result)
206 except Exception as e:
207 click.echo(f"错误: {e}", err=True)
208 sys.exit(1)
211@main.command()
212@click.option('--model', '-m', required=True,
213 help='模型字符串,如 "ARIMA(2,1,1)" 或 "SARIMA(2,1,1)(1,1,1,12)"')
214@click.option('--output', '-o', type=click.Path(),
215 help='输出文件路径')
216def stability(model: str, output: Optional[str]):
217 """
218 分析模型稳定性
219 """
220 try:
221 # 解析模型
222 model_obj = ModelParser.parse_from_string(model)
224 # 稳定性分析
225 deriver = TransferFunctionDeriver()
226 stability_result = deriver.analyze_stability(model_obj)
228 # 格式化输出
229 lines = [f"{model_obj.name} 稳定性分析", "=" * 40]
230 lines.append(f"系统稳定性: {'稳定' if stability_result['is_stable'] else '不稳定'}")
231 lines.append(f"最大极点模长: {stability_result['max_pole_magnitude']:.6f}")
232 lines.append(f"稳定性裕度: {stability_result['stability_margin']:.6f}")
233 lines.append("")
235 if stability_result['poles']:
236 lines.append("极点:")
237 for i, pole in enumerate(stability_result['poles']):
238 lines.append(f" p_{i+1} = {pole:.6f} (|p_{i+1}| = {abs(pole):.6f})")
240 if stability_result['zeros']:
241 lines.append("")
242 lines.append("零点:")
243 for i, zero in enumerate(stability_result['zeros']):
244 lines.append(f" z_{i+1} = {zero:.6f}")
246 if not stability_result['is_stable']:
247 lines.append("")
248 lines.append("警告: 系统不稳定!存在模长大于等于1的极点。")
250 result = "\n".join(lines)
252 # 输出结果
253 if output:
254 with open(output, 'w', encoding='utf-8') as f:
255 f.write(result)
256 click.echo(f"稳定性分析已保存到: {output}")
257 else:
258 click.echo(result)
260 except Exception as e:
261 click.echo(f"错误: {e}", err=True)
262 sys.exit(1)
265@main.command()
266def examples():
267 """
268 显示使用示例
269 """
270 examples_text = """
271使用示例:
2731. 分析简单ARIMA模型:
274 tsm-analyzer analyze -m "ARIMA(2,1,1)"
2762. 分析带参数的ARIMA模型:
277 tsm-analyzer analyze -m "ARIMA(2,1,1)" --include-analysis
2793. 分析SARIMA模型并输出LaTeX格式:
280 tsm-analyzer analyze -m "SARIMA(2,1,1)(1,1,1,12)" --format latex
2824. 从配置文件分析:
283 tsm-analyzer analyze -f model_config.json --format json -o result.json
2855. 交互式输入:
286 tsm-analyzer analyze --interactive
2886. 计算脉冲响应:
289 tsm-analyzer impulse -m "ARIMA(2,1,1)" --max-lag 10
2917. 计算频率响应:
292 tsm-analyzer frequency -m "ARIMA(2,1,1)" --frequencies "0,0.1,0.2,0.3,0.4,0.5"
2948. 稳定性分析:
295 tsm-analyzer stability -m "ARIMA(2,1,1)"
297配置文件格式 (JSON):
298{
299 "model_type": "ARIMA",
300 "p": 2,
301 "d": 1,
302 "q": 1,
303 "ar_params": [0.5, -0.3],
304 "ma_params": [0.2],
305 "constant": 0
306}
308配置文件格式 (YAML):
309model_type: SARIMA
310p: 2
311d: 1
312q: 1
313P: 1
314D: 1
315Q: 1
316m: 12
317ar_params: [0.5, -0.3]
318ma_params: [0.2]
319seasonal_ar_params: [0.8]
320seasonal_ma_params: [0.4]
321"""
322 click.echo(examples_text)
325if __name__ == '__main__':
326 main()