Skip to content

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