| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- #!/usr/bin/env python3
- """Compare indicator features between a pre-repair and a post-repair segment.
- For each cylinder/side it reports the segment medians and, importantly, the
- *peer-relative* value (deviation from the same-stage/side median), so a repair
- that only changes one cylinder can be separated from a process-wide shift.
- Read-only.
- """
- from __future__ import annotations
- import argparse
- import sys
- from collections import defaultdict
- from pathlib import Path
- import numpy as np
- BACKEND = Path(__file__).resolve().parents[1]
- sys.path.insert(0, str(BACKEND))
- from indicator_features.extract_from_db import ( # noqa: E402
- CYLINDERS,
- SIDES,
- connect,
- fetch_cycles,
- fetch_file_rows,
- point_name,
- sample_per_day,
- )
- from indicator_features.indicator_features import CYLINDER_STAGE, cycle_features # noqa: E402
- FEATURES = ("p_discharge", "pressure_ratio", "discharge_frac", "indicated_work", "efficiency", "n_comp")
- def extract(connection, unit, cylinders, sides, start, end, per_day):
- rows = []
- for cylinder in cylinders:
- for side in sides:
- point = point_name(unit, cylinder, side)
- files = fetch_file_rows(connection, point, start, end)
- picked = sample_per_day(files, per_day)
- if not picked:
- continue
- id_to_time = {int(r["id"]): r for r in picked}
- for file_id, signal in fetch_cycles(connection, list(id_to_time), 200).items():
- feats = cycle_features(signal, cylinder)
- if feats:
- rows.append({"cylinder": cylinder, "side": side, "stage": CYLINDER_STAGE[cylinder], **feats})
- return rows
- def summary(rows):
- table = {}
- groups = defaultdict(list)
- for row in rows:
- groups[(row["cylinder"], row["side"])].append(row)
- for key, items in groups.items():
- table[key] = {feature: float(np.median([r[feature] for r in items if r[feature] is not None]))
- for feature in FEATURES}
- return table
- def peer_relative(table, feature):
- """Deviation from the same-stage/side median, per cylinder."""
- by_stage = defaultdict(dict)
- for (cylinder, side), values in table.items():
- stage = CYLINDER_STAGE[cylinder]
- by_stage[(stage, side)][cylinder] = values[feature]
- result = {}
- for (stage, side), members in by_stage.items():
- clean = {c: v for c, v in members.items() if v is not None}
- if len(clean) < 2:
- continue
- med = float(np.median(list(clean.values())))
- if abs(med) < 1e-9:
- continue
- for cylinder, value in clean.items():
- result[(cylinder, side)] = (value - med) / med
- return result
- def main():
- parser = argparse.ArgumentParser(description="修前/修后功图特征同级对照")
- parser.add_argument("--unit", required=True, choices=("7", "8", "9"))
- parser.add_argument("--cylinders", nargs="+", default=list(CYLINDERS), choices=CYLINDERS)
- parser.add_argument("--sides", nargs="+", default=list(SIDES), choices=SIDES)
- parser.add_argument("--pre", nargs=2, required=True, metavar=("START", "END"))
- parser.add_argument("--post", nargs=2, required=True, metavar=("START", "END"))
- parser.add_argument("--per-day", type=int, default=5)
- args = parser.parse_args()
- connection = connect()
- try:
- pre_rows = extract(connection, args.unit, args.cylinders, args.sides, args.pre[0], args.pre[1], args.per_day)
- post_rows = extract(connection, args.unit, args.cylinders, args.sides, args.post[0], args.post[1], args.per_day)
- finally:
- connection.close()
- pre, post = summary(pre_rows), summary(post_rows)
- print(f"{args.unit}号机 修前 {args.pre[0]}~{args.pre[1]} ({len(pre_rows)}行) 修后 {args.post[0]}~{args.post[1]} ({len(post_rows)}行)")
- if not pre_rows or not post_rows:
- print("某一段无数据"); return
- print(f"\n{'缸侧':>6} " + ' '.join(f"{f[:11]:>18}" for f in FEATURES))
- for key in sorted(set(pre) & set(post)):
- cells = []
- for feature in FEATURES:
- a, b = pre[key].get(feature), post[key].get(feature)
- if a and b and abs(a) > 1e-9:
- cells.append(f"{a:7.3f}->{b:6.3f}({(b-a)/a*100:+5.1f}%)")
- else:
- cells.append(f"{'':>18}")
- print(f"{key[0]+key[1]:>6} " + ' '.join(f"{c:>18}" for c in cells))
- print("\n同级 peer 相对(修前 -> 修后):")
- for feature in ("pressure_ratio", "p_discharge", "efficiency"):
- rel_pre = peer_relative(pre, feature)
- rel_post = peer_relative(post, feature)
- print(f" {feature}:")
- for key in sorted(set(rel_pre) & set(rel_post)):
- print(f" {key[0]}缸{key[1]}: {rel_pre[key]*100:+.1f}% -> {rel_post[key]*100:+.1f}%")
- if __name__ == "__main__":
- main()
|