The Complete Guide to Forecasting: How to Predict What Your Business Actually Needs

Forecasting is one of those things that sounds simple until you try to do it properly. Everyone wants to know what's coming. How many units will we sell next quarter? How much inventory do we need? What will demand look like during the holiday season? The good news is that forecasting doesn't have to be guesswork. There are proven methods that can give you genuinely useful predictions. The bad news? There's no magic formula that works for everything. Different situations need different approaches.

The Two Big Categories: Quantitative vs Qualitative

Before we get into the weeds, you need to understand the fundamental split in forecasting approaches.

Quantitative forecasting uses historical numerical data and mathematical models to predict future values. It's data driven, objective, and works brilliantly when you have reliable historical data and the future is likely to behave somewhat like the past.

Qualitative forecasting relies on expert opinion, intuition, market research, and human judgement. It's subjective by nature, but absolutely essential when you're dealing with new products, uncertain markets, or situations where the past simply won't predict the future.

Neither is inherently better than the other. The trick is knowing when to use which, and sometimes combining both.

Quantitative Forecasting Methods

Let's start with the numbers based approaches.

Time Series Analysis

Time series forecasting is probably the most common quantitative method you'll encounter. It analyses patterns in historical data over time to predict future values.

The core assumption here is that the future will follow patterns similar to the past. If umbrella sales spike every November, they'll probably spike next November too.

Time series data typically contains several components:

Trend: The long term direction of the data. Are sales generally increasing, decreasing, or staying flat over years?

Seasonality: Regular, predictable patterns that repeat over fixed periods. Christmas shopping spikes, summer holiday dips, that sort of thing.

Cyclical patterns: Longer term fluctuations tied to economic or business cycles. These aren't as predictable as seasonal patterns.

Irregular/Random variation: The unpredictable noise in your data that no model can capture.

Common time series methods include:

Moving Averages: Smooths out short term fluctuations by averaging data points over a rolling window. Simple but effective for stable data.

Exponential Smoothing: Gives more weight to recent observations. The idea is that what happened last month is probably more relevant than what happened three years ago.

ARIMA (AutoRegressive Integrated Moving Average): A more sophisticated approach that can handle trends and seasonality. It's the workhorse of time series forecasting.

Prophet: Facebook's forecasting tool that handles seasonality, holidays, and missing data really well. Brilliant for business forecasting.

SARIMA: ARIMA's seasonal cousin. When you've got strong seasonal patterns, this is often your best bet.

Regression Analysis

While time series looks at how a variable changes over time, regression analysis examines relationships between variables.

The question shifts from "what will sales be next month based on previous months?" to "what drives sales, and how can we predict them based on those drivers?"

Simple Linear Regression: One predictor variable. "Sales increase by £50,000 for every degree the temperature rises above 20°C."

Multiple Linear Regression: Several predictor variables. "Sales depend on temperature, day of week, whether there's a tube strike, and proximity to payday."

Polynomial Regression: For relationships that aren't straight lines.

Regression is particularly powerful when you can identify causal factors that drive your outcomes. If you know that footfall on Oxford Street correlates strongly with sales, and you have footfall forecasts, you can use that relationship.

Machine Learning Approaches

For complex patterns with lots of variables, machine learning models often outperform traditional statistical methods.

Random Forests: Ensemble methods that combine multiple decision trees. Great for handling non linear relationships and interactions between variables.

Gradient Boosting (XGBoost, LightGBM): Often wins forecasting competitions. Handles complex patterns and large datasets efficiently.

Neural Networks (LSTM, GRU): Particularly good for sequential data with long term dependencies. If what happened six months ago still affects today, these can capture it.

The tradeoff with ML methods is interpretability. A linear regression tells you exactly how each factor contributes to the forecast. A neural network might be more accurate but explaining why it made a particular prediction can be nearly impossible.

Qualitative Forecasting Methods

Now let's talk about when the numbers aren't enough.

The Delphi Method

This is structured expert consensus. You gather a panel of experts, ask them to forecast independently, share the aggregated results anonymously, and repeat until the group converges on a consensus.

The anonymity is crucial. It prevents dominant personalities from steamrolling the discussion and reduces anchoring bias.

The Delphi method works brilliantly for: New product launches where there's no historical data; Long term strategic forecasting; Technology adoption predictions; Market disruption scenarios.

Market Research and Surveys

Sometimes the best way to predict what customers will buy is to ask them.

Customer surveys: "Would you buy this product at this price?"

Focus groups: Deeper qualitative insights into preferences and behaviours

Purchase intent studies: Structured questionnaires that measure likelihood of purchase

The challenge with surveys is that what people say they'll do and what they actually do often differ. Someone might say they'd definitely buy sustainable fashion, then choose the cheaper non sustainable option at checkout.

Sales Force Composite

Your sales team talks to customers every day. They know which products are generating buzz, which ones customers are asking about, and which ones are gathering dust.

Aggregating their estimates into a collective forecast leverages their ground level knowledge. The downsides are that salespeople might be optimistic (or pessimistic depending on incentive structures), and their views might be biased by recent experiences.

Executive Opinion (Jury of Executive Opinion)

Senior leaders pool their judgement to create forecasts. Quick, cheap, and leverages strategic insight.

The risk is groupthink and the HiPPO problem (Highest Paid Person's Opinion wins). Structure the process carefully to get genuine independent input.

Scenario Planning

Rather than predicting a single future, you develop multiple plausible scenarios and forecast for each.

"If the economy grows 3%, we expect X. If we hit recession, we expect Y. If a new competitor enters the market, we expect Z."

This is particularly valuable for strategic planning and risk management.

Seasonal vs Non Seasonal Forecasting

This distinction cuts across the quantitative/qualitative divide.

Seasonal Forecasting

Seasonal patterns are regular, predictable fluctuations that repeat at fixed intervals.

Annual seasonality: Christmas shopping peaks, summer holiday patterns, back to school rushes

Monthly seasonality: Pay day effects, end of month patterns

Weekly seasonality: Weekend vs weekday differences

Daily seasonality: Lunch rush, evening shopping patterns

When you have clear seasonal patterns, your forecasting method must account for them. Using a simple moving average on highly seasonal data will give you forecasts that are always wrong, always late.

Seasonal decomposition separates your data into trend, seasonal, and residual components. You forecast each separately and recombine them.

Methods designed for seasonality include: Seasonal ARIMA (SARIMA), Holt Winters exponential smoothing, Prophet (handles multiple seasonalities beautifully), Fourier terms in regression models.

Non Seasonal Forecasting

Some data genuinely doesn't have seasonal patterns. B2B industrial equipment sales might be driven more by economic cycles and capital expenditure budgets than by time of year.

For non seasonal data, simpler methods often work well: Simple exponential smoothing, ARIMA without seasonal components, Regression models without seasonal terms, Trend extrapolation.

The danger is assuming data is non seasonal when it actually has patterns you haven't detected. Always check for seasonality before assuming it's absent.

When to Use What: A Decision Framework

Here's how to think about choosing your approach:

Use quantitative methods when: You have sufficient historical data (at least two to three years for annual seasonality); The underlying patterns are relatively stable; The future is likely to resemble the past; You need frequent, automated forecasts at scale.

Use qualitative methods when: You're forecasting for new products with no history; Major market disruptions are occurring or expected; You're looking at long time horizons where uncertainty is high; Expert knowledge captures factors that data doesn't.

Combine both when: You want to adjust quantitative forecasts with expert judgement; You're validating model outputs against intuition; Different product categories need different approaches; You're building ensemble forecasts that blend multiple methods.

Practical Example: Forecasting for a Luxury London Department Store

Let me show you how this works in practice. I've been working with forecasting for a famous department store on Oxford Street, the kind of place that's been a London landmark for over a hundred years. They sell everything from designer fashion to fine foods, luxury watches to beauty products.

Each product category needs a different forecasting approach. Here's how we think about it:

Example 1: Designer Sunglasses (Highly Seasonal, Quantitative)

Sunglasses have one of the clearest seasonal patterns in retail. Sales explode in late spring, peak in summer, and crater in winter.

For this category, we use Seasonal ARIMA (SARIMA) because: We have years of historical sales data; The seasonal pattern is strong and consistent; The relationship between season and sales is mechanical (people buy sunglasses when it's sunny).

We also incorporate regression elements using weather forecasts. An unusually warm April can pull summer demand forward; a rainy June suppresses it.

The model handles: Annual seasonality (summer peaks), Trend (gradual growth in premium sunglasses), External regressors (temperature forecasts, UV index).

Example 2: Luxury Watches (Low Seasonality, Mixed Methods)

High end watches from brands in the Wonder Room don't follow typical seasonal patterns the way fashion does. A £10,000 Rolex purchase isn't driven by whether it's sunny outside.

Instead, these purchases correlate with: Bonus season (January, July for financial services); Stock market performance; Tourist arrivals (particularly from certain markets); New model releases; Exchange rates affecting international shoppers.

We use multiple regression with economic indicators, combined with qualitative input from the watch department buyers who know when new models are launching and which pieces are generating waiting lists.

The blend looks like: 60% quantitative model (regression on economic factors), 40% qualitative adjustment (buyer expertise, brand relationship intelligence).

Example 3: New Designer Collection Launch (Pure Qualitative)

When a hot new designer launches an exclusive collection, there's no historical data to work with. Last year's sales of different products from different designers tell you almost nothing.

Here we rely entirely on qualitative methods:

Delphi style expert panels: Buyers, merchandisers, and marketing collaborate on estimates

Analogous forecasting: "This launch is similar to the X collection two years ago, which did Y"

Market research: Social media sentiment, fashion press coverage, influencer interest

Pre order data: Early signals from VIP customers and fashion editors

We typically create three scenarios (conservative, expected, optimistic) and plan inventory and staffing accordingly.

Example 4: Food Hall Confectionery (Seasonal with Events)

The food hall sells premium chocolates, biscuits, and confectionery. This has strong seasonality but also event driven spikes.

Seasonal patterns: Christmas (massive), Easter (significant), Valentine's Day (moderate), Mother's Day (moderate).

Event driven variation: Royal occasions, Store events and collaborations, New product launches.

We use Prophet for this because it handles: Multiple seasonalities (weekly, annual); Holiday effects (with custom holiday calendars for UK specific events); Trend changes; Missing data (helpful when COVID disrupted patterns).

We add qualitative overlays for major events. When the store does a Disney Christmas collaboration, historical patterns don't capture the uplift from that specific partnership.

Example 5: Beauty Products (Mixed Seasonality, Promotional Complexity)

Beauty is complicated. Some products are seasonal (SPF in summer, rich moisturisers in winter), others aren't. Promotional activity drives huge swings.

We use a hybrid approach:

Baseline forecast: Prophet or SARIMA for underlying demand patterns

Promotional uplift models: Regression models that estimate the sales lift from different promotion types

New product forecasting: Qualitative methods for launches

The promotional piece is crucial. A "gift with purchase" promotion might triple sales for a week. The model needs to know promotions are planned to avoid thinking demand has permanently tripled.

Example 6: Luxury Handbags (Supply Constrained, Qualitative Heavy)

For certain heritage luxury brands, demand exceeds supply. There are waiting lists. The forecast challenge isn't "how much will customers want to buy?" but "how much will the brand allocate to us?"

This flips the forecasting problem. We need:

Supply forecasting: Qualitative intelligence from brand relationships about allocation expectations

Demand sensing: Understanding true demand even when sales are constrained by supply

Waiting list management: Predicting conversion rates from waiting list to purchase

Mostly qualitative, heavily relationship driven.

Python Libraries for Forecasting

Right, let's get practical. Here are the Python libraries you'll need and example code for common forecasting tasks.

Essential Libraries

# Core data handling
import pandas as pd
import numpy as np

# Visualisation
import matplotlib.pyplot as plt
import seaborn as sns

# Statistical forecasting
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.seasonal import seasonal_decompose

# Facebook Prophet
from prophet import Prophet

# Machine learning
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.model_selection import train_test_split, TimeSeriesSplit
from sklearn.metrics import mean_absolute_error, mean_squared_error

# Advanced ML
import xgboost as xgb
import lightgbm as lgb

# Deep learning for time series
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout

Example 1: Seasonal Decomposition (Understanding Your Data)

Before forecasting, understand what's in your data.

import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose

# Load sunglasses sales data
df = pd.read_csv('sunglasses_sales.csv', parse_dates=['date'], index_col='date')

# Decompose the time series
decomposition = seasonal_decompose(df['sales'], model='multiplicative', period=12)

# Plot the components
fig, axes = plt.subplots(4, 1, figsize=(12, 10))

decomposition.observed.plot(ax=axes[0], title='Observed')
decomposition.trend.plot(ax=axes[1], title='Trend')
decomposition.seasonal.plot(ax=axes[2], title='Seasonal')
decomposition.resid.plot(ax=axes[3], title='Residual')

plt.tight_layout()
plt.show()

# Now you can see:
# Is there a trend? (growing, declining, flat)
# How strong is seasonality? (big swings or small)
# How much unexplained variation remains? (residual noise)

Example 2: SARIMA for Seasonal Products (Sunglasses)

import pandas as pd
import numpy as np
from statsmodels.tsa.statespace.sarimax import SARIMAX
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error
import matplotlib.pyplot as plt

# Load and prepare data
df = pd.read_csv('sunglasses_sales.csv', parse_dates=['date'], index_col='date')
df = df.asfreq('MS')  # Monthly start frequency

# Split into train and test
train = df[:'2024']
test = df['2025':]

# Fit SARIMA model
# Order: (p, d, q) for non seasonal part
# Seasonal order: (P, D, Q, s) where s is the seasonal period (12 for monthly)
model = SARIMAX(
    train['sales'],
    order=(1, 1, 1),
    seasonal_order=(1, 1, 1, 12),
    enforce_stationarity=False,
    enforce_invertibility=False
)

results = model.fit(disp=False)

# Print model summary
print(results.summary())

# Forecast
forecast = results.get_forecast(steps=len(test))
forecast_mean = forecast.predicted_mean
forecast_ci = forecast.conf_int()

# Evaluate
mae = mean_absolute_error(test['sales'], forecast_mean)
mape = mean_absolute_percentage_error(test['sales'], forecast_mean)

print(f'Mean Absolute Error: {mae:.2f}')
print(f'Mean Absolute Percentage Error: {mape:.2%}')

# Plot results
plt.figure(figsize=(12, 6))
plt.plot(train.index, train['sales'], label='Training Data')
plt.plot(test.index, test['sales'], label='Actual', color='green')
plt.plot(test.index, forecast_mean, label='Forecast', color='red')
plt.fill_between(
    test.index,
    forecast_ci.iloc[:, 0],
    forecast_ci.iloc[:, 1],
    color='red',
    alpha=0.2,
    label='95% Confidence Interval'
)
plt.legend()
plt.title('Sunglasses Sales Forecast (SARIMA)')
plt.xlabel('Date')
plt.ylabel('Sales (£)')
plt.show()

Example 3: Prophet for Food Hall Confectionery

Prophet is brilliant for business forecasting with holidays and events.

import pandas as pd
from prophet import Prophet
import matplotlib.pyplot as plt

# Load data (Prophet requires columns named 'ds' and 'y')
df = pd.read_csv('confectionery_sales.csv')
df.columns = ['ds', 'y']  # date and sales
df['ds'] = pd.to_datetime(df['ds'])

# Define UK holidays and store specific events
uk_holidays = pd.DataFrame({
    'holiday': 'christmas',
    'ds': pd.to_datetime(['2022-12-25', '2023-12-25', '2024-12-25', '2025-12-25']),
    'lower_window': -30,  # Effect starts 30 days before
    'upper_window': 5     # Effect ends 5 days after
})

easter = pd.DataFrame({
    'holiday': 'easter',
    'ds': pd.to_datetime(['2022-04-17', '2023-04-09', '2024-03-31', '2025-04-20']),
    'lower_window': -14,
    'upper_window': 1
})

valentines = pd.DataFrame({
    'holiday': 'valentines',
    'ds': pd.to_datetime(['2022-02-14', '2023-02-14', '2024-02-14', '2025-02-14']),
    'lower_window': -7,
    'upper_window': 0
})

mothers_day_uk = pd.DataFrame({
    'holiday': 'mothers_day',
    'ds': pd.to_datetime(['2022-03-27', '2023-03-19', '2024-03-10', '2025-03-30']),
    'lower_window': -7,
    'upper_window': 0
})

holidays = pd.concat([uk_holidays, easter, valentines, mothers_day_uk])

# Initialise and fit Prophet
model = Prophet(
    holidays=holidays,
    yearly_seasonality=True,
    weekly_seasonality=True,
    daily_seasonality=False,
    changepoint_prior_scale=0.05  # Flexibility of trend changes
)

model.fit(df)

# Create future dataframe for forecasting
future = model.make_future_dataframe(periods=365)  # Forecast one year ahead

# Generate forecast
forecast = model.predict(future)

# Plot the forecast
fig1 = model.plot(forecast)
plt.title('Confectionery Sales Forecast')
plt.xlabel('Date')
plt.ylabel('Sales (£)')
plt.show()

# Plot the components (trend, weekly, yearly, holidays)
fig2 = model.plot_components(forecast)
plt.show()

# Extract the holiday effects
print("Holiday Effects:")
print(forecast[['ds', 'christmas', 'easter', 'valentines', 'mothers_day']].dropna().head(20))

Example 4: Multiple Regression for Luxury Watches

When external factors drive demand, regression captures those relationships.

import pandas as pd
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.model_selection import TimeSeriesSplit
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error, r2_score
import matplotlib.pyplot as plt

# Load data with multiple features
df = pd.read_csv('watch_sales_with_features.csv', parse_dates=['date'])

# Features that might drive luxury watch sales
# ftse_100: Stock market performance
# tourist_arrivals: International visitors to London
# gbp_usd: Exchange rate
# bonus_season: Binary indicator for bonus months (Jan, Jul)
# new_model_release: Binary indicator for new watch releases

feature_columns = [
    'ftse_100_change',
    'tourist_arrivals',
    'gbp_usd',
    'bonus_season',
    'new_model_release',
    'month_sin',  # Cyclical encoding of month
    'month_cos'
]

# Create cyclical month features
df['month'] = df['date'].dt.month
df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)

# Prepare features and target
X = df[feature_columns]
y = df['sales']

# Scale features
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Time series cross validation
tscv = TimeSeriesSplit(n_splits=5)

mae_scores = []
r2_scores = []

for train_idx, test_idx in tscv.split(X_scaled):
    X_train, X_test = X_scaled[train_idx], X_scaled[test_idx]
    y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
    
    model = Ridge(alpha=1.0)
    model.fit(X_train, y_train)
    
    predictions = model.predict(X_test)
    
    mae_scores.append(mean_absolute_error(y_test, predictions))
    r2_scores.append(r2_score(y_test, predictions))

print(f'Average MAE: {np.mean(mae_scores):.2f}')
print(f'Average R2: {np.mean(r2_scores):.3f}')

# Fit final model on all data
final_model = Ridge(alpha=1.0)
final_model.fit(X_scaled, y)

# Feature importance
feature_importance = pd.DataFrame({
    'feature': feature_columns,
    'coefficient': final_model.coef_
}).sort_values('coefficient', key=abs, ascending=False)

print("\nFeature Importance:")
print(feature_importance)

# Plot feature importance
plt.figure(figsize=(10, 6))
plt.barh(feature_importance['feature'], feature_importance['coefficient'])
plt.xlabel('Coefficient (Standardised)')
plt.title('Factors Driving Luxury Watch Sales')
plt.tight_layout()
plt.show()

Example 5: XGBoost for Complex Patterns (Beauty Products)

When you have lots of features and complex interactions, gradient boosting shines.

import pandas as pd
import numpy as np
import xgboost as xgb
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# Load beauty product sales data
df = pd.read_csv('beauty_sales.csv', parse_dates=['date'])

# Feature engineering
df['day_of_week'] = df['date'].dt.dayofweek
df['month'] = df['date'].dt.month
df['week_of_year'] = df['date'].dt.isocalendar().week
df['is_weekend'] = df['day_of_week'].isin([5, 6]).astype(int)
df['is_payday_week'] = df['date'].dt.day.isin(range(25, 32)).astype(int)

# Lag features (previous sales)
df['sales_lag_7'] = df['sales'].shift(7)
df['sales_lag_14'] = df['sales'].shift(14)
df['sales_lag_28'] = df['sales'].shift(28)

# Rolling statistics
df['sales_rolling_mean_7'] = df['sales'].rolling(window=7).mean()
df['sales_rolling_std_7'] = df['sales'].rolling(window=7).std()
df['sales_rolling_mean_28'] = df['sales'].rolling(window=28).mean()

# Drop rows with NaN from lag features
df = df.dropna()

# Define features
feature_columns = [
    'day_of_week', 'month', 'week_of_year', 'is_weekend', 'is_payday_week',
    'sales_lag_7', 'sales_lag_14', 'sales_lag_28',
    'sales_rolling_mean_7', 'sales_rolling_std_7', 'sales_rolling_mean_28'
]

X = df[feature_columns]
y = df['sales']

# Time series split
tscv = TimeSeriesSplit(n_splits=5)

mae_scores = []

for train_idx, test_idx in tscv.split(X):
    X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
    y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
    
    model = xgb.XGBRegressor(
        n_estimators=100,
        max_depth=6,
        learning_rate=0.1,
        subsample=0.8,
        colsample_bytree=0.8,
        random_state=42
    )
    
    model.fit(
        X_train, y_train,
        eval_set=[(X_test, y_test)],
        verbose=False
    )
    
    predictions = model.predict(X_test)
    mae_scores.append(mean_absolute_error(y_test, predictions))

print(f'Average MAE: {np.mean(mae_scores):.2f}')

# Fit final model and get feature importance
final_model = xgb.XGBRegressor(n_estimators=100, max_depth=6, learning_rate=0.1)
final_model.fit(X, y)

# Plot feature importance
xgb.plot_importance(final_model, max_num_features=15)
plt.title('Feature Importance for Beauty Product Sales')
plt.tight_layout()
plt.show()

Example 6: Combining Quantitative and Qualitative (Ensemble Approach)

For new collection launches, blend model outputs with expert judgement.

import pandas as pd
import numpy as np

def create_ensemble_forecast(
    quantitative_forecast,
    expert_estimates,
    quantitative_weight=0.6,
    expert_weight=0.4
):
    """
    Blend quantitative model forecast with expert judgement.
    
    Parameters:
    quantitative_forecast: Model predicted values
    expert_estimates: Dictionary with 'conservative', 'expected', 'optimistic' estimates
    quantitative_weight: Weight for model forecast (default 0.6)
    expert_weight: Weight for expert estimate (default 0.4)
    
    Returns:
    Dictionary with blended forecasts for each scenario
    """
    
    results = {}
    
    for scenario, expert_value in expert_estimates.items():
        blended = (
            quantitative_forecast * quantitative_weight +
            expert_value * expert_weight
        )
        results[scenario] = blended
    
    return results


def calculate_forecast_with_uncertainty(
    base_forecast,
    historical_mape,
    confidence_level=0.95
):
    """
    Add uncertainty bounds to forecasts based on historical accuracy.
    
    Parameters:
    base_forecast: Point forecast value
    historical_mape: Historical Mean Absolute Percentage Error
    confidence_level: Confidence level for bounds (default 0.95)
    
    Returns:
    Dictionary with forecast and bounds
    """
    
    z_score = 1.96 if confidence_level == 0.95 else 1.645
    
    margin = base_forecast * historical_mape * z_score
    
    return {
        'forecast': base_forecast,
        'lower_bound': base_forecast - margin,
        'upper_bound': base_forecast + margin,
        'confidence_level': confidence_level
    }


# Example: New designer collection launch
# Model forecast based on analogous products
model_forecast = 125000  # £125k predicted from similar past launches

# Expert estimates from buyer panel (Delphi style)
expert_estimates = {
    'conservative': 95000,   # Cautious estimate
    'expected': 140000,      # Most likely estimate  
    'optimistic': 200000     # Best case scenario
}

# Create ensemble forecasts
ensemble = create_ensemble_forecast(
    model_forecast,
    expert_estimates,
    quantitative_weight=0.5,  # Equal weight for new product
    expert_weight=0.5
)

print("Ensemble Forecasts for New Collection:")
for scenario, value in ensemble.items():
    print(f"  {scenario.capitalize()}: £{value:,.0f}")

# Add uncertainty to expected scenario
historical_mape = 0.25  # 25% historical error on new product forecasts

forecast_with_bounds = calculate_forecast_with_uncertainty(
    ensemble['expected'],
    historical_mape
)

print(f"\nExpected Forecast with 95% Confidence:")
print(f"  Point Estimate: £{forecast_with_bounds['forecast']:,.0f}")
print(f"  Lower Bound: £{forecast_with_bounds['lower_bound']:,.0f}")
print(f"  Upper Bound: £{forecast_with_bounds['upper_bound']:,.0f}")

Example 7: Forecast Accuracy Monitoring

Forecasting isn't a one time thing. You need to track how well you're doing and adjust.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def calculate_forecast_metrics(actual, forecast):
    """
    Calculate common forecast accuracy metrics.
    
    Returns dictionary with MAE, MAPE, RMSE, and bias.
    """
    
    errors = actual - forecast
    abs_errors = np.abs(errors)
    pct_errors = abs_errors / actual
    
    metrics = {
        'mae': np.mean(abs_errors),
        'mape': np.mean(pct_errors) * 100,
        'rmse': np.sqrt(np.mean(errors ** 2)),
        'bias': np.mean(errors),  # Positive = under forecasting
        'bias_pct': (np.mean(errors) / np.mean(actual)) * 100
    }
    
    return metrics


def forecast_accuracy_report(df, actual_col, forecast_col, group_col=None):
    """
    Generate forecast accuracy report, optionally grouped by category.
    
    Parameters:
    df: DataFrame with actual and forecast columns
    actual_col: Name of actual values column
    forecast_col: Name of forecast values column
    group_col: Optional column to group by (e.g., product category)
    
    Returns:
    DataFrame with accuracy metrics
    """
    
    if group_col:
        results = []
        for group in df[group_col].unique():
            group_df = df[df[group_col] == group]
            metrics = calculate_forecast_metrics(
                group_df[actual_col],
                group_df[forecast_col]
            )
            metrics['category'] = group
            results.append(metrics)
        return pd.DataFrame(results).set_index('category')
    else:
        return pd.DataFrame([calculate_forecast_metrics(
            df[actual_col],
            df[forecast_col]
        )])


# Example usage
results_df = pd.DataFrame({
    'category': ['Sunglasses', 'Sunglasses', 'Watches', 'Watches', 
                 'Confectionery', 'Confectionery', 'Beauty', 'Beauty'],
    'month': ['Jan', 'Feb', 'Jan', 'Feb', 'Jan', 'Feb', 'Jan', 'Feb'],
    'actual': [45000, 52000, 180000, 195000, 120000, 85000, 95000, 98000],
    'forecast': [48000, 49000, 175000, 190000, 115000, 88000, 92000, 95000]
})

# Generate accuracy report by category
accuracy_report = forecast_accuracy_report(
    results_df,
    actual_col='actual',
    forecast_col='forecast',
    group_col='category'
)

print("Forecast Accuracy by Category:")
print(accuracy_report.round(2))

Key Takeaways

Forecasting isn't about finding the perfect method. It's about matching the right approach to your specific situation.

Start with the data you have. If you've got years of clean historical data with clear patterns, quantitative methods will serve you well. If you're flying blind with a new product, embrace qualitative approaches.

Understand your patterns. Seasonal decomposition should be your first step. Know what's driving your data before you try to predict it.

Don't over engineer. A simple method that you understand and can explain often beats a complex black box. Start simple, add complexity only when it demonstrably improves accuracy.

Combine approaches. The best forecasters blend quantitative rigour with expert judgement. Models capture patterns; humans capture context.

Track your accuracy. Forecasting without measuring accuracy is just guessing with extra steps. Build feedback loops and continuously improve.

Plan for uncertainty. Point forecasts are always wrong. The question is how wrong. Provide ranges and scenarios, not just single numbers.

Forecasting well is genuinely hard. But with the right tools and frameworks, you can get much better at predicting what your business needs, whether that's designer sunglasses or artisan chocolates.

Need help building forecasting systems that actually work for your business? I've built prediction models for inventory, demand, and resource planning. Let's figure out what approach makes sense for your situation.

The Complete Guide to Forecasting: How to Predict What Your Business Actually Needs - Georg Keferböck