Restaurant Revenue Cost Prediction using Bayesian Regression in ML
FREE Online Courses: Elevate Your Skills, Zero Cost Attached - Enroll Now!
Restaurant operators and financial planners need to forecast annual restaurant revenue—before the next fiscal cycle—using early business indicators such as location demographics, seating capacity, number of employees, marketing spend, and historical revenue trends. Revenue exhibits nonlinear dependencies (e.g., diminishing returns from marketing beyond a threshold, plateauing foot traffic) and uncertainty due to seasonality and economic shifts. A simple point‐estimate regression masks this uncertainty, risking over‑ or under‑investment. By applying Bayesian Regression, we produce both a point forecast of expected revenue and a credible interval that quantifies our uncertainty—enabling risk‑aware budgeting, staffing, and marketing planning.
Libraries Required
import pandas as pd # data I/O import numpy as np # numerical operations import matplotlib.pyplot as plt # plotting import seaborn as sns # visualization import pymc3 as pm # Bayesian modeling import arviz as az # posterior analysis from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_absolute_error
Dataset
Step-by-Step Code Implementation
Data Loading & Preprocessing
- Categorical features (City_Group, Type) are one‐hot encoded to capture location and restaurant format effects.
- Numeric features (P1–P10) are z‑scored so that Bayesian priors apply uniformly across predictors.
import pandas as pd
# Load training data
df = pd.read_csv("data/train.csv")
# Select predictors and target
features = [
'City_Group', # {Big Cities, Other}
'Type', # {FC, IL, DT, MB}
'P1','P2','P3','P4','P5','P6','P7','P8','P9','P10' # numerical features
]
X = df[features].copy()
y = df['revenue'].values # annual revenue
# One‐hot encode categorical features
X = pd.get_dummies(X, columns=['City_Group','Type'], drop_first=True)
# Train/test split (80% train / 20% test)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X.values, y, test_size=0.2, random_state=42
)
# Standardize numeric columns for stable MCMC
from sklearn.preprocessing import StandardScaler
# assume first 10 columns are P1–P10
scaler = StandardScaler().fit(X_train[:, :10])
X_train_s = X_train.copy()
X_train_s[:, :10] = scaler.transform(X_train[:, :10])
X_test_s = X_test.copy()
X_test_s[:, :10] = scaler.transform(X_test[:, :10])
Define & Fit Bayesian Regression Model
Model priors:
- α ∼ Normal(0, 1e6) accommodates revenue scales (~10⁶‑10⁷ USD).
- β ∼ Normal(0, 1e5) reflects moderate uncertainty on each predictor’s effect.
- σ ∼ HalfNormal(1e6) encodes residual variability in the same order.
Likelihood: Observed revenue ∼ Normal(α + X·β, σ).
Inference: We draw 2,000 posterior samples (after 1,000 tuning) with target_accept=0.9 for stable convergence.
import pymc3 as pm
with pm.Model() as revenue_model:
# Priors
α = pm.Normal("α", mu=0, sigma=1e6) # intercept
β = pm.Normal("β", mu=0, sigma=1e5, shape=X_train_s.shape[1]) # slopes
σ = pm.HalfNormal("σ", sigma=1e6) # noise scale
# Linear predictor
μ = α + pm.math.dot(X_train_s, β)
# Likelihood
Y_obs = pm.Normal("Y_obs", mu=μ, sigma=σ, observed=y_train)
# MCMC sampling
trace = pm.sample(
draws=2000,
tune=1000,
target_accept=0.9,
return_inferencedata=True
)
Posterior Analysis & Point Predictions
- Posterior predictive: Sampling Y_obs yields full predictive distributions; we extract point forecasts (posterior means) and 94% Highest Posterior Density intervals.
- Evaluation: Mean Absolute Error (MAE) on held‑out data quantifies the average forecasting error in USD.
import arviz as az
from sklearn.metrics import mean_absolute_error
# Summarize posterior distributions
az.summary(trace, round_to=2)
# Posterior predictive sampling
with revenue_model:
ppc = pm.sample_posterior_predictive(trace, var_names=["Y_obs"])
# Posterior means
α_post = trace.posterior["α"].mean().item()
β_post = trace.posterior["β"].mean(dim=["chain","draw"]).values
# Point‐forecast on test set
y_pred = α_post + X_test_s.dot(β_post)
# Evaluate MAE
mae = mean_absolute_error(y_test, y_pred)
print(f"Test MAE: ${mae:,.2f}")
Visualise Predictions & Credible Intervals
By varying one key feature (P1) and holding the others fixed, we plot both the posterior mean revenue curve and its credible band—illustrating how P1 drives revenue and the associated uncertainty.
import numpy as np
import matplotlib.pyplot as plt
# Example: vary P1 while holding others at median
p1_vals = np.linspace(X_train_s[:,0].min(), X_train_s[:,0].max(), 100)
grid = np.median(X_train_s, axis=0)[None,:].repeat(100, axis=0)
grid[:,0] = p1_vals
with revenue_model:
mu_samples = trace.posterior["α"].values.flatten()[:,None] \
+ np.dot(trace.posterior["β"].values.reshape(-1, X_train_s.shape[1]), grid.T)
# compute mean and 94% HDI
mean_pred = mu_samples.mean(axis=0)
hpd = az.hdi(mu_samples, hdi_prob=0.94)
# Back‐transform P1
p1_orig = scaler.inverse_transform(grid)[:,0]
plt.figure(figsize=(8,5))
plt.plot(p1_orig, mean_pred, label="Posterior mean")
plt.fill_between(p1_orig, hpd[:,0], hpd[:,1], alpha=0.3,
label="94% credible interval")
plt.scatter(
scaler.inverse_transform(X_test_s)[:,0],
y_test, color="k", alpha=0.5, label="Test data"
)
plt.xlabel("P1")
plt.ylabel("Annual Revenue (USD)")
plt.title("Bayesian Regression: Revenue vs. P1")
plt.legend()
plt.tight_layout()
plt.show()
Summary
This Bayesian Regression workflow for Restaurant Revenue Prediction delivers:
1. Point estimates of annual revenue from early business and demographic indicators.
2. Credible intervals that quantify our forecasting uncertainty—vital for risk‑aware budgeting and investment.
3. Actionable insights: stakeholders can plan marketing spend, staffing levels, and expansion strategies with full awareness of both expected revenues and their uncertainty bounds.