← Files Power BI DesktopARCHIVED FILE

scripts/support/forecast_quality.py

2.04 KB · Oct 2, 2026 · 00:32 UTC

↓ Download file

from collections import defaultdict

from forecast_primitives import num


QUALITY_FIELDNAMES = [
    "horizon_months", "segment", "actual_sales", "ai_forecast", "roll_forecast",
    "wape", "roll_wape", "bias", "risk_flag",
]


def build_quality_rows(backtest_rows):
    by_horizon = defaultdict(lambda: defaultdict(float))
    for row in backtest_rows:
        values = by_horizon[int(row["horizon_months"])]
        add_quality_values(values, row)
    if not by_horizon:
        return [insufficient_quality_row()]
    return [quality_row(horizon, values) for horizon, values in sorted(by_horizon.items())]


def add_quality_values(values, row):
    for fieldname in quality_input_fields():
        values[fieldname] += num(row[fieldname])


def quality_input_fields():
    return [
        "actual_sales", "ai_forecast", "roll_forecast", "absolute_error",
        "roll_absolute_error",
    ]


def quality_row(horizon, values):
    actual = values["actual_sales"]
    bias = values["ai_forecast"] - actual
    wape = values["absolute_error"] / actual if actual else 0.0
    roll_wape = values["roll_absolute_error"] / actual if actual else 0.0
    bias_ratio = bias / actual if actual else 0.0
    return {
        "horizon_months": horizon,
        "segment": "Total",
        "actual_sales": round(actual, 2),
        "ai_forecast": round(values["ai_forecast"], 2),
        "roll_forecast": round(values["roll_forecast"], 2),
        "wape": round(wape, 6),
        "roll_wape": round(roll_wape, 6),
        "bias": round(bias_ratio, 6),
        "risk_flag": quality_risk(wape, roll_wape, bias_ratio),
    }


def quality_risk(wape, roll_wape, bias_ratio):
    if wape > roll_wape:
        return "advisory_only"
    return "biased" if abs(bias_ratio) > 0.03 else "normal"


def insufficient_quality_row():
    return {
        "horizon_months": 1,
        "segment": "Total",
        "actual_sales": 0.0,
        "ai_forecast": 0.0,
        "roll_forecast": 0.0,
        "wape": 0.0,
        "roll_wape": 0.0,
        "bias": 0.0,
        "risk_flag": "insufficient_history",
    }

SHA-256: dfc36541a96a7dd3ec0adbca84bd20e2d57be3555013c65c71b25487010b8665