Forecast error (WAPE)¶
Of all the units we sold, how many did the forecast get wrong?
Current value (inventory case data): 77.0% - ✕ Act. Open the case.
| Field | Value |
|---|---|
| Definition | Sum of absolute errors divided by sum of actuals. |
| Formula | sum(/A - F/) / sum(A) |
| Unit | % |
| Granularity | SKU x week; rolled up by SKU class. |
| Direction | Lower is better ↓ |
| Target and thresholds | ≤ 30.0% (amber ≤ 45.0%) |
| Parameters | – |
| Owner | Demand Planner |
| Source | Demand (UCI Online Retail II) + baseline forecast computed in this repo |
| Input | table forecast, columns actual_units, forecast_units |
| Recommended chart | line - Weekly trend; seasonal peaks annotated. |
| Related KPIs | Forecast error (MAPE), Forecast bias |
How to read it¶
Volume-weighted: big SKUs dominate, which is what inventory and service feel. The headline planning-accuracy KPI.
Common misreadings¶
- Computed at a high aggregation level, errors cancel out (SKU A over, SKU B under); compute at SKU level, then sum.
Worked example (from the test suite)¶
| actual_units | forecast_units |
|---|---|
| 100 | 90 |
| 10 | 20 |
| 0 | 5 |
Expected result: 22.73%. Sum |A-F| = 10 + 10 + 5 = 25. Sum A = 110. 25 / 110 = 0.22727. Same data as MAPE: 22.7% vs 55%.
This example is asserted in tests/test_formulas.py against the Python function and in tests/test_sql.py against the SQL below.
Implementations¶
Python function (the single implementation used by the site):
@kpi("wape")
def wape(df: pd.DataFrame) -> float:
return safe_ratio(
(df["actual_units"] - df["forecast_units"]).abs().sum(), df["actual_units"].sum()
)
Reference SQL (tested on SQLite):
SELECT SUM(ABS(actual_units - forecast_units)) * 1.0 / SUM(actual_units) FROM forecast