Spaces:
Running
Running
| """Target engineering for the three prediction tasks. | |
| T1 y_road_closure : bool -> barricading / diversion need (imbalanced ~7%) | |
| T2 y_high_priority : bool -> manpower tier (priority == "High") | |
| T3 y_duration_min : float -> impact duration in minutes (regression) | |
| Duration is derived from post-event timestamps (the only place it exists) but | |
| those timestamps are NEVER used as model features. The duration label is built | |
| by coalescing resolved -> closed -> end (most reliable first). Rows that were | |
| auto-closed by the nightly batch job, are still active, or have a non-positive | |
| duration are marked invalid and excluded from regression training only. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| import pandas as pd | |
| from . import config as C | |
| def _coalesced_end(df: pd.DataFrame) -> pd.Series: | |
| """Best available clearance timestamp: resolved > closed > end.""" | |
| cols = ["resolved_datetime", "closed_datetime", "end_datetime"] | |
| present = [c for c in cols if c in df.columns] | |
| return df[present].bfill(axis=1).iloc[:, 0] | |
| def build_targets(df: pd.DataFrame, save: bool = True) -> pd.DataFrame: | |
| df = df.copy() | |
| # ---- T1: road closure (barricading / diversion) --------------------- # | |
| df[C.TARGET_CLOSURE] = df["requires_road_closure"].fillna(False).astype(int) | |
| # ---- T2: high priority (manpower tier) ------------------------------ # | |
| df[C.TARGET_PRIORITY] = ( | |
| df["priority"].astype(str).str.strip().str.lower().eq("high").astype(int) | |
| ) | |
| # ---- T3: impact duration (minutes) ---------------------------------- # | |
| end = _coalesced_end(df) | |
| duration = (end - df["start_datetime"]).dt.total_seconds() / 60.0 | |
| valid = ( | |
| duration.gt(C.DURATION_MIN_MINUTES) | |
| & (~df["auto_resolved_flag"].fillna(False)) | |
| & df["status"].astype(str).str.lower().ne("active") | |
| ) | |
| df[C.TARGET_DURATION] = np.where(valid, duration, np.nan) | |
| df["duration_valid"] = valid.astype(bool) | |
| if save: | |
| df.to_parquet(C.CLEAN_PARQUET, index=False) | |
| return df | |
| def winsorized_log_duration(y_min: pd.Series, upper_cap: float | None = None): | |
| """Return (log1p target, fitted upper cap). Cap is computed ONLY on the | |
| values passed in (the trainer passes the training split to avoid leakage). | |
| """ | |
| y = y_min.dropna() | |
| if upper_cap is None: | |
| upper_cap = float(np.quantile(y, C.DURATION_WINSOR_UPPER_Q)) | |
| capped = np.clip(y_min, a_min=None, a_max=upper_cap) | |
| return np.log1p(capped), upper_cap | |
| if __name__ == "__main__": # pragma: no cover | |
| d = pd.read_parquet(C.CLEAN_PARQUET) | |
| d = build_targets(d) | |
| print("closure positive rate:", d[C.TARGET_CLOSURE].mean().round(4)) | |
| print("high-priority rate :", d[C.TARGET_PRIORITY].mean().round(4)) | |
| print("duration valid rows :", int(d["duration_valid"].sum())) | |
| print(d.loc[d.duration_valid, C.TARGET_DURATION].describe().round(1)) | |