How to Calculate MAPE in Python
Mean Absolute Percentage Error (MAPE) measures the average magnitude of errors in predictions as a percentage of actual values. Unlike metrics such as RMSE (Root Mean Squared Error) or MAE (Mean...
Key Insights
- MAPE expresses prediction accuracy as an intuitive percentage, making it ideal for business stakeholders who need to understand model performance without deep statistical knowledge
- The metric fails catastrophically when actual values contain zeros, requiring careful preprocessing or alternative metrics like symmetric MAPE for datasets with zero values
- A MAPE below 10% is generally excellent, 10-20% is good, but these thresholds vary dramatically by domain—inventory forecasting tolerates higher error than financial predictions
Understanding MAPE as a Regression Metric
Mean Absolute Percentage Error (MAPE) measures the average magnitude of errors in predictions as a percentage of actual values. Unlike metrics such as RMSE (Root Mean Squared Error) or MAE (Mean Absolute Error), MAPE provides scale-independent results that business users can immediately grasp. When you tell a stakeholder your sales forecast has a MAPE of 8%, they understand the model is off by roughly 8% on average—no statistical background required.
The MAPE formula is straightforward:
"""
MAPE = (1/n) * Σ(|actual - predicted| / |actual|) * 100
Where:
- n = number of observations
- actual = true values
- predicted = forecasted values
- Σ = summation across all observations
"""
MAPE shines in forecasting scenarios where relative error matters more than absolute error. If you’re predicting sales of 1,000 units versus 1,000,000 units, a 100-unit error has vastly different implications. MAPE captures this context by expressing error as a percentage of the actual value.
Use MAPE when you need interpretable, scale-independent metrics for stakeholders. Choose RMSE when you want to penalize large errors more heavily, or MAE when you need a metric robust to outliers. MAPE works best for time series forecasting, demand planning, sales predictions, and any domain where percentage-based accuracy resonates with your audience.
Calculating MAPE from Scratch
Building MAPE manually helps you understand its mechanics and customize the calculation for specific needs. Here’s a clean NumPy implementation:
import numpy as np
def calculate_mape(actual, predicted):
"""
Calculate Mean Absolute Percentage Error manually.
Parameters:
actual (array-like): True values
predicted (array-like): Predicted values
Returns:
float: MAPE as a percentage
"""
actual = np.array(actual)
predicted = np.array(predicted)
# Calculate absolute percentage errors
absolute_errors = np.abs(actual - predicted)
percentage_errors = (absolute_errors / np.abs(actual)) * 100
# Return mean
return np.mean(percentage_errors)
# Example: Monthly sales forecast
actual_sales = np.array([1500, 2300, 1800, 2100, 2500])
predicted_sales = np.array([1450, 2400, 1750, 2000, 2600])
mape = calculate_mape(actual_sales, predicted_sales)
print(f"MAPE: {mape:.2f}%") # Output: MAPE: 4.46%
This implementation clearly shows each step: computing absolute errors, converting to percentages relative to actual values, and averaging. The 4.46% MAPE indicates our sales forecast is quite accurate, with predictions typically within 5% of actual values.
Leveraging Scikit-learn for MAPE
Scikit-learn includes mean_absolute_percentage_error since version 0.24. This built-in function handles edge cases and integrates seamlessly with sklearn’s model evaluation ecosystem:
from sklearn.metrics import mean_absolute_percentage_error
import numpy as np
# Same sales data
actual_sales = np.array([1500, 2300, 1800, 2100, 2500])
predicted_sales = np.array([1450, 2400, 1750, 2000, 2600])
# Calculate MAPE (returns decimal, not percentage)
mape = mean_absolute_percentage_error(actual_sales, predicted_sales)
print(f"MAPE: {mape * 100:.2f}%") # Output: MAPE: 4.46%
# Use with cross-validation
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LinearRegression
# Create sample dataset
X = np.array([[1], [2], [3], [4], [5]])
y = np.array([2.1, 4.2, 5.8, 8.1, 10.3])
model = LinearRegression()
# MAPE as scoring metric (note: sklearn minimizes, so we negate)
from sklearn.metrics import make_scorer
mape_scorer = make_scorer(mean_absolute_percentage_error, greater_is_better=False)
scores = cross_val_score(model, X, y, cv=3, scoring=mape_scorer)
print(f"Average MAPE: {-scores.mean() * 100:.2f}%")
Note that sklearn’s function returns MAPE as a decimal (0.0446) rather than a percentage, so multiply by 100 for conventional reporting. The function also works directly with pandas Series and integrates with sklearn’s cross-validation and scoring infrastructure.
Handling Division by Zero
MAPE’s Achilles heel is zero values in actual data. Division by zero produces infinite or undefined results, breaking your calculations. This commonly occurs in sparse datasets like retail inventory (many products with zero sales on given days) or intermittent demand scenarios.
Here’s how to handle this robustly:
import numpy as np
def safe_mape(actual, predicted, epsilon=1e-10):
"""
MAPE calculation with zero-handling strategies.
"""
actual = np.array(actual)
predicted = np.array(predicted)
# Strategy 1: Add small epsilon to avoid division by zero
mape_epsilon = np.mean(np.abs((actual - predicted) / (actual + epsilon))) * 100
# Strategy 2: Filter out zeros
non_zero_mask = actual != 0
if np.sum(non_zero_mask) == 0:
return np.nan # All zeros, MAPE undefined
mape_filtered = np.mean(
np.abs((actual[non_zero_mask] - predicted[non_zero_mask]) / actual[non_zero_mask])
) * 100
return mape_filtered
def symmetric_mape(actual, predicted):
"""
Symmetric MAPE (sMAPE) - alternative that handles zeros better.
Range: 0-200% instead of 0-infinity
"""
actual = np.array(actual)
predicted = np.array(predicted)
numerator = np.abs(actual - predicted)
denominator = (np.abs(actual) + np.abs(predicted)) / 2
# Avoid division by zero when both actual and predicted are zero
mask = denominator != 0
smape = np.mean(numerator[mask] / denominator[mask]) * 100
return smape
# Test with problematic data
actual = np.array([100, 0, 200, 0, 150])
predicted = np.array([110, 5, 190, 2, 145])
print(f"Safe MAPE (filtered): {safe_mape(actual, predicted):.2f}%")
print(f"Symmetric MAPE: {symmetric_mape(actual, predicted):.2f}%")
The epsilon approach adds a tiny value to denominators, preventing division by zero while minimally affecting results. Filtering zeros is cleaner but changes your sample size. Symmetric MAPE (sMAPE) uses the average of actual and predicted in the denominator, making it symmetric and bounded between 0-200%.
Choose your strategy based on context. For sparse retail data, filtering zeros makes sense—you only care about accuracy when products actually sell. For continuous time series with occasional zeros, sMAPE might be more appropriate.
Real-World Application: Stock Price Forecasting
Let’s apply MAPE to evaluate a simple moving average forecast for stock prices:
import pandas as pd
import numpy as np
from sklearn.metrics import mean_absolute_percentage_error
# Simulate stock price data
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=100, freq='D')
actual_prices = 100 + np.cumsum(np.random.randn(100) * 2)
# Create DataFrame
df = pd.DataFrame({'date': dates, 'actual': actual_prices})
# Simple forecasting models
df['sma_5'] = df['actual'].rolling(window=5).mean() # 5-day moving average
df['sma_20'] = df['actual'].rolling(window=20).mean() # 20-day moving average
df['naive'] = df['actual'].shift(1) # Yesterday's price
# Drop NaN values from rolling calculations
df_eval = df.dropna()
# Calculate MAPE for each model
models = {
'5-day SMA': df_eval['sma_5'],
'20-day SMA': df_eval['sma_20'],
'Naive (Previous Day)': df_eval['naive']
}
print("Model Performance Comparison:")
print("-" * 40)
for model_name, predictions in models.items():
mape = mean_absolute_percentage_error(df_eval['actual'], predictions) * 100
print(f"{model_name:20s}: {mape:6.2f}%")
# Best model
best_model = min(models.items(), key=lambda x: mean_absolute_percentage_error(df_eval['actual'], x[1]))
print(f"\nBest Model: {best_model[0]}")
This workflow demonstrates a complete evaluation pipeline: generating predictions from multiple models, calculating MAPE for each, and identifying the best performer. The structure mirrors real forecasting projects where you compare candidate models before deployment.
Interpreting MAPE and Recognizing Limitations
MAPE interpretation depends heavily on your domain. Here are general guidelines:
- < 10%: Excellent forecasting accuracy
- 10-20%: Good performance, acceptable for most business applications
- 20-50%: Reasonable, depending on context (acceptable for long-term forecasts)
- > 50%: Poor accuracy, model needs improvement
However, these thresholds shift dramatically by industry. Retail demand forecasting might accept 20-30% MAPE for slow-moving items, while financial trading algorithms require sub-1% accuracy.
MAPE has critical limitations. It asymmetrically penalizes over-predictions less than under-predictions. If actual = 100 and predicted = 50, the error is 50%. But if predicted = 150, the error is only 50%. This asymmetry can bias model selection toward over-forecasting.
MAPE also struggles with low-volume scenarios. An actual value of 2 with a prediction of 4 yields 100% MAPE, even though the absolute error is just 2 units. This makes MAPE unreliable for datasets with small actual values.
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import mean_absolute_percentage_error
# Generate example predictions
actual = np.array([100, 120, 115, 130, 125, 140, 135, 150])
predicted = np.array([98, 125, 110, 135, 120, 145, 130, 155])
mape = mean_absolute_percentage_error(actual, predicted) * 100
# Visualization
plt.figure(figsize=(10, 6))
plt.plot(actual, 'o-', label='Actual', linewidth=2, markersize=8)
plt.plot(predicted, 's-', label='Predicted', linewidth=2, markersize=8)
plt.xlabel('Observation')
plt.ylabel('Value')
plt.title(f'Actual vs Predicted (MAPE: {mape:.2f}%)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('mape_visualization.png', dpi=300)
plt.show()
This visualization helps communicate model performance to stakeholders. The MAPE score in the title provides quantitative accuracy, while the plot shows qualitative fit—whether the model captures trends, seasonality, or systematically over/under-predicts.
For production systems, consider reporting MAPE alongside complementary metrics like MAE (for absolute error context) and RMSE (for outlier sensitivity). This multi-metric approach provides a complete picture of model performance and helps identify specific failure modes. MAPE tells you the percentage story stakeholders want to hear, while other metrics provide the technical depth you need for model improvement.