Spaces:
Sleeping
Sleeping
| """Compute the Congestion Impact Index (CII) per hotspot cell. | |
| CII is a transparent, rank-normalised blend of four ideas: | |
| - severity-weighted VOLUME (how much weighted illegal parking happens here) | |
| - PERSISTENCE (chronic every-day spot vs one-off spike) | |
| - PEAK-hour concentration (bad exactly when the road is busiest) | |
| - junction proximity (blocking near junctions cascades downstream) | |
| It is explicitly a *proxy* for traffic-flow impact, derived only from | |
| enforcement data, with every weight exposed in config.py. | |
| """ | |
| from src import config | |
| def _norm(series): | |
| """Rank-based normalisation to [0, 1] (robust to heavy-tailed counts).""" | |
| return series.rank(method="average", pct=True).fillna(0.0) | |
| def add_cii(stats): | |
| stats = stats.copy() | |
| w = config.CII_WEIGHTS | |
| v = _norm(stats["weighted_volume"]) | |
| p = _norm(stats["persistence"]) | |
| k = _norm(stats["peak_share"]) | |
| base = w["volume"] * v + w["persistence"] * p + w["peak"] * k | |
| junction = stats["junction_share"].clip(0, 1) | |
| cii = base * (1 + config.CII_JUNCTION_ALPHA * junction) | |
| # scale to a friendly 0-100 | |
| cii = (cii - cii.min()) / (cii.max() - cii.min() + 1e-9) * 100 | |
| stats["cii"] = cii.round(1) | |
| stats["cii_rank"] = stats["cii"].rank(ascending=False, method="min").astype(int) | |
| return stats.sort_values("cii", ascending=False).reset_index(drop=True) | |