| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- #!/usr/bin/env python3
- """Re-run the temperature rules from the cached hourly long table (no DB scan).
- Usage::
- python _tmp_temperature/rescan.py --out _tmp_temperature/out_t1 \
- --mild-peer 4 --mild-spread 4 --abnormal-peer 6 --abnormal-spread 8 \
- --severe-peer 10 --severe-spread 15
- """
- from __future__ import annotations
- import argparse
- import sys
- from pathlib import Path
- import pandas as pd
- ROOT = Path(__file__).resolve().parents[1]
- if str(ROOT) not in sys.path:
- sys.path.insert(0, str(ROOT))
- from _tmp_temperature import hf_validation, unified
- from _tmp_temperature.config import build_unit_config
- LONG = Path(__file__).resolve().parent / "out" / "pks_temperature_hourly.csv"
- def load_wide(path: Path) -> dict[str, list[dict]]:
- frame = pd.read_csv(path, low_memory=False)
- frame["hour_start"] = pd.to_datetime(frame["小时"])
- result: dict[str, list[dict]] = {}
- for unit, group in frame.groupby("机组"):
- keys = ["hour_start", "批次"]
- wide = group.pivot_table(index=keys, columns="点位", values="均值", aggfunc="first")
- coverage = group.groupby(keys)["运行覆盖率"].first()
- std = group.pivot_table(index=keys, columns="点位", values="标准差", aggfunc="first")
- minraw = group.pivot_table(index=keys, columns="点位", values="原始最小值", aggfunc="first")
- zeros = group.pivot_table(index=keys, columns="点位", values="零值样本数", aggfunc="first")
- high = group.pivot_table(index=keys, columns="点位", values="高报警样本数", aggfunc="first")
- high_high = group.pivot_table(index=keys, columns="点位", values="高高报警样本数", aggfunc="first")
- rows = []
- for (hour_start, batch), values in wide.iterrows():
- cov = float(coverage.loc[(hour_start, batch)])
- row = {
- "机组": unit,
- "import_batch_id": int(batch),
- "hour_start": hour_start.to_pydatetime(),
- "coverage": cov,
- # The long table is already filtered to valid running hours by
- # scan.py, so every row is valid here (coverage may be <0.8 for
- # the 4# cumulative-counter path).
- "valid": True,
- }
- for column, value in values.items():
- if pd.notna(value):
- row[f"{column}__avg"] = float(value)
- svalue = std.loc[(hour_start, batch), column] if column in std.columns else float("nan")
- if pd.notna(svalue):
- row[f"{column}__std"] = float(svalue)
- mvalue = minraw.loc[(hour_start, batch), column] if column in minraw.columns else float("nan")
- if pd.notna(mvalue):
- row[f"{column}__minraw"] = float(mvalue)
- zvalue = zeros.loc[(hour_start, batch), column] if column in zeros.columns else float("nan")
- if pd.notna(zvalue):
- row[f"{column}__zeros"] = int(zvalue)
- h = high.loc[(hour_start, batch), column] if column in high.columns else float("nan")
- hh = high_high.loc[(hour_start, batch), column] if column in high_high.columns else float("nan")
- row[f"{column}__high"] = 0 if pd.isna(h) else int(h)
- row[f"{column}__high_high"] = 0 if pd.isna(hh) else int(hh)
- rows.append(row)
- rows.sort(key=lambda item: (item["import_batch_id"], item["hour_start"]))
- result[unit] = rows
- return result
- def main() -> None:
- parser = argparse.ArgumentParser()
- parser.add_argument("--long", default=str(LONG))
- parser.add_argument("--out", required=True)
- parser.add_argument("--min-hours", type=int, default=1)
- parser.add_argument("--confirm-hours", type=int, default=3)
- for name in ("mild_peer", "mild_spread", "abnormal_peer", "abnormal_spread",
- "severe_peer", "severe_spread", "mild_peer_low", "abnormal_peer_low",
- "severe_peer_low", "mild_z", "abnormal_z", "severe_z",
- "mild_delta", "abnormal_delta", "severe_delta"):
- parser.add_argument(f"--{name.replace('_', '-')}", type=float)
- args = parser.parse_args()
- overrides = {
- key: getattr(args, key)
- for key in ("mild_peer", "mild_spread", "abnormal_peer", "abnormal_spread",
- "severe_peer", "severe_spread", "mild_peer_low", "abnormal_peer_low",
- "severe_peer_low", "mild_z", "abnormal_z", "severe_z",
- "mild_delta", "abnormal_delta", "severe_delta", "confirm_hours")
- if getattr(args, key) is not None
- }
- by_unit = load_wide(Path(args.long))
- all_qc, all_features, all_segments, all_cycles, all_channels, all_observations, all_hf = [], [], [], [], [], [], []
- for unit, rows in by_unit.items():
- number = int(unit[0])
- config = build_unit_config(number)
- qc, features, segments, cycles, channels, observations, hf_segments = unified.run(rows, config, overrides, args.min_hours)
- print(f"{unit}: 有效小时 {sum(1 for row in rows if row['valid']):,} 段 {len(segments):,}")
- all_qc += qc
- all_features += features
- all_segments += segments
- all_cycles += cycles
- all_channels += channels
- all_observations += observations
- all_hf += hf_segments
- output = Path(args.out)
- confirmed_hf, hf_candidates = hf_validation.filter_hf(all_hf)
- unified.write_results(output, all_qc, all_features, all_segments, all_cycles, all_channels, all_observations,
- confirmed_hf, hf_candidates)
- for level in ("轻微", "异常", "严重异常"):
- print(f" {level}: {sum(1 for segment in all_segments if segment['温度阶段'] == level):,}")
- print(f" 观察: {len(all_observations):,}")
- print(f" 高频候选: {len(hf_candidates):,} 高频确认: {len(confirmed_hf):,}")
- print(f" 合并段: {len(all_segments) + len(confirmed_hf):,}")
- print(f"written -> {output}")
- if __name__ == "__main__":
- main()
|