feat: add detection + forecast modules with tests
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""异常检测模块:ANOMALY_WINDOW × 6 算法多路跑 + 容错。"""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ALL_ALGOS = ["ksigma", "iqr", "grubbs", "shesd", "lof", "sample_ad_model"]
|
||||
|
||||
|
||||
def run_anomaly_window(conn, stable: str, algo: str) -> list[tuple[int, int]]:
|
||||
"""执行单算法 ANOMALY_WINDOW。
|
||||
|
||||
Args:
|
||||
conn: TDConnection 实例
|
||||
stable: supertable 名称(不含 ds_ 前缀)
|
||||
algo: 算法名
|
||||
|
||||
Returns:
|
||||
[(start_ts_ms, end_ts_ms), ...] 异常窗口列表
|
||||
|
||||
Raises:
|
||||
ValueError: 算法不支持
|
||||
Exception: SQL 执行失败(透传)
|
||||
"""
|
||||
if algo not in ALL_ALGOS:
|
||||
raise ValueError(f"不支持的算法: {algo},可选: {ALL_ALGOS}")
|
||||
|
||||
sql = (
|
||||
f"SELECT _WSTART, _WEND "
|
||||
f"FROM (ANOMALY_WINDOW(value, \"algo={algo}\") "
|
||||
f"FROM ds_{stable})"
|
||||
)
|
||||
|
||||
rows = conn.execute(sql)
|
||||
|
||||
windows = []
|
||||
for row in rows:
|
||||
if row[0] is not None and row[1] is not None:
|
||||
windows.append((int(row[0]), int(row[1])))
|
||||
return windows
|
||||
|
||||
|
||||
def detect_all_algos(conn, stable: str, algos: list[str] | None = None) -> dict[str, Any]:
|
||||
"""依次执行所有指定算法,独立容错。
|
||||
|
||||
Args:
|
||||
conn: TDConnection 实例
|
||||
stable: supertable 名称
|
||||
algos: 算法列表,默认 ALL_ALGOS
|
||||
|
||||
Returns:
|
||||
{algo_name: {"windows": [...], "error": None | str}, ...}
|
||||
"""
|
||||
algos = algos or ALL_ALGOS
|
||||
results: dict[str, Any] = {}
|
||||
|
||||
for algo in algos:
|
||||
logger.info("running ANOMALY_WINDOW algo=%s stable=%s", algo, stable)
|
||||
try:
|
||||
if algo == "sample_ad_model":
|
||||
# 检查模型文件
|
||||
import os
|
||||
model_path = os.path.join("data", "models", "sample_ad_model")
|
||||
if not os.path.exists(model_path):
|
||||
results[algo] = {"windows": [], "error": "model file not found"}
|
||||
logger.warning("sample_ad_model skip: model file not found at %s", model_path)
|
||||
continue
|
||||
|
||||
windows = run_anomaly_window(conn, stable, algo)
|
||||
results[algo] = {"windows": windows, "error": None}
|
||||
logger.info("algo=%s: %d windows found", algo, len(windows))
|
||||
except Exception as e:
|
||||
results[algo] = {"windows": [], "error": str(e)}
|
||||
logger.warning("algo=%s failed (non-fatal): %s", algo, e)
|
||||
|
||||
return results
|
||||
Reference in New Issue
Block a user