Forecast error (MAPE)¶
On average, by what percentage does each item's forecast miss?
Current value (inventory case data): 389.2% - ✕ Act. Open the case.
| Field | Value |
|---|---|
| Definition | Mean of /actual - forecast/ / actual over SKU-periods with actual > 0. |
| Formula | mean(/A - F/ / A) for A > 0 |
| Unit | % |
| Granularity | SKU x week. |
| Direction | Lower is better ↓ |
| Target and thresholds | ≤ 40.0% (amber ≤ 60.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 | bar - MAPE vs WAPE side by side per SKU class makes the volume effect visible. |
| Related KPIs | Forecast error (WAPE), Forecast bias |
How to read it¶
Every SKU-week weighs the same, so slow movers dominate. Use it to judge forecastability of the long tail, not overall planning quality.
Common misreadings¶
- Undefined when actual = 0 (those rows are dropped here - say so) and explodes on tiny actuals.
- Asymmetric - over-forecasts can exceed 100% error while under-forecasts cap at 100%.
- Comparing MAPE across portfolios with different volume mix; use WAPE for that.
Worked example (from the test suite)¶
| actual_units | forecast_units |
|---|---|
| 100 | 90 |
| 10 | 20 |
| 0 | 5 |
Expected result: 55.00%. Row 3 dropped (actual 0). |100-90|/100 = 0.10; |10-20|/10 = 1.00. Mean (0.10 + 1.00) / 2 = 0.55. The small SKU dominates.
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("mape")
def mape(df: pd.DataFrame) -> float:
d = df[df["actual_units"] > 0]
if d.empty:
return float("nan")
return float(((d["actual_units"] - d["forecast_units"]).abs() / d["actual_units"]).mean())
Reference SQL (tested on SQLite):
SELECT AVG(ABS(actual_units - forecast_units) * 1.0 / actual_units) FROM forecast WHERE actual_units > 0