78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
"""异常检测模块: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
|