Data Plan Cost Prediction using Bayesian Regression in ML
FREE Online Courses: Click for Success, Learn for Free - Start Now!
Telecom product managers need to estimate a subscriber’s monthly data‐plan charge—before recommending an upgrade or crafting promotional bundles—using early‐contract indicators such as monthly data usage (GB), call minutes, number of lines, contract length, and customer tenure. Pricing tiers introduce nonlinear effects (e.g., usage beyond a cap often triggers overage fees, multline discounts apply), and actual charges vary with plan promotions and add‑ons. A single point estimate ignores this variability. By using Bayesian Regression, we produce both a point forecast of each customer’s expected monthly charge and a credible interval that captures our uncertainty—enabling risk‑aware pricing, churn mitigation, and targeted upsells.
Libraries Required
import pandas as pd # data loading & manipulation 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
Data Features:
- TotalCharges and tenure capture usage history.
- Contract, PaymentMethod, InternetService, and MultipleLines reflect plan structure.
Encoding & Scaling:
- We one‐hot encode plan categories.
- We z‑score numeric inputs so that priors on β apply uniformly.
import pandas as pd
# Load dataset
df = pd.read_csv("data/WA_Fn-UseC_-Telco-Customer-Churn.csv")
# Select predictors and target, drop rows with missing tenure
df = df[['MonthlyCharges','TotalCharges','tenure','Contract','PaymentMethod','InternetService','MultipleLines']].dropna()
# Convert TotalCharges to numeric
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'])
# One‐hot encode categorical plan features
df = pd.get_dummies(df, columns=['Contract','PaymentMethod','InternetService','MultipleLines'], drop_first=True)
# Define X and y
X = df.drop(columns='MonthlyCharges').values
y = df['MonthlyCharges'].values
# Train/test split
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Standardize numeric columns (TotalCharges, tenure)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler().fit(X_train[:, :2])
X_train_s = X_train.copy()
X_train_s[:, :2] = scaler.transform(X_train[:, :2])
X_test_s = X_test.copy()
X_test_s[:, :2] = scaler.transform(X_test[:, :2])
Define & Fit Bayesian Regression Model
Model Priors:
- α ∼ Normal(0, 50) for intercept.
- β ∼ Normal(0, 10) for each coefficient.
- σ ∼ HalfNormal(10) for residual noise.
Likelihood: Observed MonthlyCharges ∼ Normal(μ, σ) with μ=α+β·X.
Sampling: 2,000 posterior draws (with 1,000 burn‑in) at target_accept=0.9 ensure stable convergence.
import pymc3 as pm
with pm.Model() as data_plan_model:
# Priors
α = pm.Normal("α", mu=0, sigma=50) # intercept
β = pm.Normal("β", mu=0, sigma=10, shape=X_train_s.shape[1]) # coefficients
σ = pm.HalfNormal("σ", sigma=10) # 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: We generate predictive distributions, extracting both the posterior mean forecast and its 94% Highest Posterior Density interval for new tenure values.
- Evaluation: Mean Absolute Error quantifies the average point‐forecast error on held‑out customers.
import arviz as az
from sklearn.metrics import mean_absolute_error
# Summarize posterior distributions
az.summary(trace, round_to=2)
# Posterior predictive sampling
with data_plan_model:
ppc = pm.sample_posterior_predictive(trace, var_names=["Y_obs"])
# Extract posterior means
α_post = trace.posterior["α"].mean().item()
β_post = trace.posterior["β"].mean(dim=["chain","draw"]).values
# Compute point predictions 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
Sweeping customer tenure highlights that longer‐tenure users command different expected charges, with credible bands indicating uncertainty due to plan variation and data sparsity.
import numpy as np
import matplotlib.pyplot as plt
# Vary tenure; hold other features at median
tenure_grid = np.linspace(X_train_s[:,1].min(), X_train_s[:,1].max(), 100)
grid = np.median(X_train_s, axis=0)[None,:].repeat(100, axis=0)
grid[:,1] = tenure_grid
with data_plan_model:
ppc_grid = pm.sample_posterior_predictive(trace,
var_names=["Y_obs"],
samples=1000)
preds = ppc_grid["Y_obs"]
mean_pred = preds.mean(axis=0)
hpd = az.hdi(preds, hdi_prob=0.94)
# Back-transform tenure
tenure_orig = scaler.inverse_transform(grid)[:,1]
plt.figure(figsize=(8,5))
plt.plot(tenure_orig, mean_pred, label="Posterior mean")
plt.fill_between(tenure_orig, hpd[:,0], hpd[:,1], alpha=0.3,
label="94% credible interval")
plt.scatter(
scaler.inverse_transform(X_test_s)[:,1],
y_test, color="k", alpha=0.5, label="Test data"
)
plt.xlabel("Customer Tenure (months)")
plt.ylabel("Monthly Charge (USD)")
plt.title("Bayesian Regression: Charge vs. Tenure")
plt.legend()
plt.tight_layout()
plt.show()
Summary
This Bayesian Regression workflow for Data Plan Cost Prediction provides:
1. Accurate point estimates of monthly charges from customer usage and plan features.
2. Credible intervals quantifying forecast uncertainty—critical for pricing and churn management.
3. Actionable insights: marketing and finance teams can craft targeted plan offers, set contract renewal prices, and anticipate revenue under uncertainty.