segment_compare.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #!/usr/bin/env python3
  2. """按运行段逐段对比:每一段开机 vs 上一段,列出最明显的变化(一页纸/段)。"""
  3. from __future__ import annotations
  4. import argparse
  5. import csv
  6. import sys
  7. from datetime import datetime
  8. from pathlib import Path
  9. import numpy as np
  10. BACKEND = Path(__file__).resolve().parents[1]
  11. sys.path.insert(0, str(BACKEND))
  12. from event_bins import load # noqa: E402
  13. from run_segments import unit_segments # noqa: E402
  14. METRICS = {"极差": "range", "粗糙度": "roughness", "水平": "mean",
  15. "波动次数": "zero_cross", "极值数": "n_extrema"}
  16. UP = {"极差": "波动变大", "粗糙度": "变毛糙", "水平": "抬高",
  17. "波动次数": "起伏次数变多", "极值数": "转折变多"}
  18. DOWN = {"极差": "波动变小", "粗糙度": "变平滑", "水平": "降低",
  19. "波动次数": "起伏次数变少", "极值数": "转折变少"}
  20. BAD = {("7", "四", "盖侧"), ("7", "六", "盖侧"), ("8", "六", "盖侧")}
  21. def med_mad(values):
  22. if not values:
  23. return None, None
  24. arr = np.asarray(values, dtype=float)
  25. med = float(np.median(arr))
  26. mad = float(np.median(np.abs(arr - med))) * 1.4826
  27. if mad <= 1e-9:
  28. mad = float(np.std(arr))
  29. return med, mad
  30. def main() -> None:
  31. parser = argparse.ArgumentParser(description="运行段逐段对比")
  32. parser.add_argument("--csv", nargs="+", required=True)
  33. parser.add_argument("--unit", nargs="+", required=True)
  34. parser.add_argument("--top", type=int, default=5, help="每段保留几条")
  35. parser.add_argument("--z", type=float, default=6.0)
  36. parser.add_argument("--outdir", required=True)
  37. args = parser.parse_args()
  38. outdir = Path(args.outdir)
  39. outdir.mkdir(parents=True, exist_ok=True)
  40. for unit, path in zip(args.unit, args.csv):
  41. rows = load(path)
  42. segments = unit_segments(unit, 2)
  43. print(f"\n===== {unit}号机 运行段({len(segments)} 段)=====")
  44. records = []
  45. for i in range(1, len(segments)):
  46. prev, cur = segments[i - 1], segments[i]
  47. pdate, cdate = f"{prev[0]}~{prev[1]}", f"{cur[0]}~{cur[1]}"
  48. cands = []
  49. for cyl, side in sorted({(r["cylinder"], r["side"]) for r in rows}):
  50. if (unit, cyl, side) in BAD:
  51. continue
  52. p_rows = [r for r in rows if r["cylinder"] == cyl and r["side"] == side
  53. and prev[0] <= r["time"].date() <= prev[1]]
  54. c_rows = [r for r in rows if r["cylinder"] == cyl and r["side"] == side
  55. and cur[0] <= r["time"].date() <= cur[1]]
  56. if len({r["wave_file_id"] for r in p_rows}) < 3 or len({r["wave_file_id"] for r in c_rows}) < 3:
  57. continue
  58. if np.median([r["p_span"] for r in p_rows if r["p_span"]] or [0]) < 0.3:
  59. continue
  60. for b in range(36):
  61. pb = [r for r in p_rows if r["bin"] == b]
  62. cb = [r for r in c_rows if r["bin"] == b]
  63. for cn, en in METRICS.items():
  64. pm, mad = med_mad([r[en] for r in pb if r[en] is not None])
  65. cm, _ = med_mad([r[en] for r in cb if r[en] is not None])
  66. if pm is None or cm is None or mad is None or mad <= 1e-9:
  67. continue
  68. z = (cm - pm) / mad
  69. if abs(z) < args.z:
  70. continue
  71. # 还要有"实际幅度":幅度类≥3%,次数类≥2 次
  72. if cn in ("波动次数", "极值数"):
  73. if abs(cm - pm) < 2:
  74. continue
  75. elif abs(pm) > 1e-9 and abs(cm - pm) < 0.03 * abs(pm):
  76. continue
  77. z = max(-999.0, min(z, 999.0))
  78. cands.append({
  79. "机组": unit, "本段": cdate, "上段": pdate,
  80. "测点": f"{cyl}缸{side}", "角度段": f"{b*10}-{b*10+10}°",
  81. "指标": cn, "上段值": round(pm, 3), "本段值": round(cm, 3),
  82. "强度": round(abs(z), 1),
  83. "结论": (UP if z > 0 else DOWN)[cn],
  84. })
  85. cands.sort(key=lambda r: -r["强度"])
  86. seen = set()
  87. picked = []
  88. for row in cands:
  89. key = (row["测点"], row["角度段"])
  90. if key in seen:
  91. continue
  92. seen.add(key)
  93. picked.append(row)
  94. if len(picked) >= args.top:
  95. break
  96. records.extend(picked)
  97. print(f" {cdate} 相对于 上段 {pdate}:")
  98. if not picked:
  99. print(" 无明显变化")
  100. for r in picked:
  101. print(f" {r['测点']:<9}{r['角度段']:<10}{r['指标']:<6}"
  102. f"{r['上段值']:>8}->{r['本段值']:<8} 强度{r['强度']:<5}{r['结论']}")
  103. if records:
  104. out = outdir / f"分段对比_{unit}号.csv"
  105. with out.open("w", encoding="utf-8-sig", newline="") as fh:
  106. w = csv.DictWriter(fh, fieldnames=list(records[0].keys()))
  107. w.writeheader()
  108. w.writerows(records)
  109. print(f" -> {out}")
  110. if __name__ == "__main__":
  111. main()