| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182 |
- """输出小时标签的排查指标,便于人工判断标签是否合理。"""
- from __future__ import annotations
- import numpy as np
- import pandas as pd
- from . import config
- def _slope(values: pd.Series, times: pd.Series) -> float:
- if len(values) < 2:
- return 0.0
- x = (pd.to_datetime(times) - pd.to_datetime(times).iloc[0]).dt.total_seconds() / 3600.0
- x = np.asarray(x, dtype=float)
- y = np.asarray(values, dtype=float)
- if np.ptp(x) <= 0 or not np.isfinite(y).any():
- return 0.0
- return float(np.polyfit(x, y, 1)[0])
- def build_review(hourly_labels: pd.DataFrame, frame: pd.DataFrame) -> pd.DataFrame:
- if hourly_labels.empty:
- return pd.DataFrame()
- frame = frame.copy()
- frame["sample_time"] = pd.to_datetime(frame["sample_time"])
- rows = []
- for item in hourly_labels.itertuples(index=False):
- group = frame[frame["cylinder"] == item.cylinder]
- part = group[(group["sample_time"] >= item.start_hour) & (group["sample_time"] < item.end_hour)]
- pre = group[(group["sample_time"] >= item.start_hour - pd.Timedelta(hours=6)) & (group["sample_time"] < item.start_hour)]
- if part.empty:
- continue
- score_max = float(part["score"].max())
- score_mean = float(part["score"].mean())
- rms_max = float(part["rms_ratio"].max())
- rms_mean = float(part["rms_ratio"].mean())
- kurt_max = float(part["kurt"].max())
- kurt_ratio_max = float(part["kurt_ratio"].max())
- kurt_ratio_mean = float(part["kurt_ratio"].mean())
- active_rms = float((part["rms_ratio"] >= 1.10).mean())
- active_kurt = float((part["kurt_ratio"] >= 1.50).mean())
- transition_ratio = float(part["transition"].mean()) if "transition" in part else 0.0
- baseline_ok_ratio = float(part["baseline_ok"].mean()) if "baseline_ok" in part else 1.0
- flags = []
- if not bool(item.confirmed):
- flags.append("未确认")
- if rms_max < 1.10 and kurt_ratio_max >= 1.50:
- flags.append("低幅高冲击")
- if score_max >= 4.50 and rms_max < 1.10 and kurt_ratio_max < 1.50:
- flags.append("分数驱动不明")
- if active_kurt < 0.30 and active_rms < 0.30:
- flags.append("持续证据弱")
- if transition_ratio >= 0.25:
- flags.append("启动/过渡")
- if baseline_ok_ratio < 0.50:
- flags.append("局部基线不足")
- rows.append(
- {
- "cylinder": item.cylinder,
- "start_hour": item.start_hour,
- "end_hour": item.end_hour,
- "hour_count": item.hour_count,
- "score_mean": round(score_mean, 3),
- "score_max": round(score_max, 3),
- "rms_ratio_mean": round(rms_mean, 3),
- "rms_ratio_max": round(rms_max, 3),
- "kurt_ratio_mean": round(kurt_ratio_mean, 3),
- "kurt_ratio_max": round(kurt_ratio_max, 3),
- "kurt_max": round(kurt_max, 3),
- "active_rms": round(active_rms, 2),
- "active_kurt": round(active_kurt, 2),
- "transition_ratio": round(transition_ratio, 2),
- "baseline_ok_ratio": round(baseline_ok_ratio, 2),
- "pre6h_score_slope": round(_slope(pre["score"], pre["sample_time"]), 4),
- "pre6h_kurt_slope": round(_slope(pre["kurt_ratio"], pre["sample_time"]), 4),
- "direction": item.direction,
- "level": item.level,
- "confirmed": bool(item.confirmed),
- "review_flags": " / ".join(flags),
- }
- )
- return pd.DataFrame(rows)
|