Ad Campaign Effectiveness Prediction using Stepwise Regression in ML
We offer you a brighter future with FREE online courses - Start Now!!
Even though marketing teams invest significant money in online and offline ad campaigns, identifying the factors that most effectively drive conversions remains challenging. In this project, we’ll predict campaign effectiveness (e.g., cost per acquisition or conversion rate) based on budget allocation, channel mix (social, search, display), targeting characteristics (age group, region), and creative attributes (ad length, format).
However, by applying stepwise regression, we’ll find the most impactful predictors and build an interpretable linear model that balances simplicity with predictive power—enabling marketers to optimise spend and maximise return.
Libraries Required
import pandas as pd # Data manipulation import numpy as np # Numerical operations import statsmodels.api as sm # Statistical modeling from sklearn.model_selection import train_test_split # Data splitting from sklearn.metrics import r2_score, mean_squared_error # Evaluation import matplotlib.pyplot as plt # Visualization
Dataset
Step-by-Step Code Implementation
Data Loading & Initial Inspection
We load a comprehensive ad campaign dataset—featuring budget, channel, region, creative format, impressions, clicks, and cost per conversion—and inspect its schema and statistics.
# Block 1: Load dataset
# Advertising Campaign Dataset – Kaggle :contentReference[oaicite:0]{index=0}
url = "https://www.kaggle.com/datasets/ziya07/advertising-campaign-dataset/download"
df = pd.read_csv(url)
# Inspect structure and summary
print(df.head())
print(df.info())
print(df.describe())
Data Preprocessing
Categorical fields (Channel, Region, Creative_Format) are one‑hot encoded; any incomplete records are dropped. We designate Cost_Per_Conversion as the response (y) and the remaining columns as predictors (X), then perform an 80/20 train‑test split.
# Block 2: Encode categorical variables and clean
# Assume columns: Channel (Search, Social, Display), Region, Creative_Format
df_enc = pd.get_dummies(df,
columns=["Channel", "Region", "Creative_Format"],
drop_first=True)
# Drop rows with missing values
df_enc = df_enc.dropna()
# Define predictors and target
X = df_enc.drop("Cost_Per_Conversion", axis=1)
y = df_enc["Cost_Per_Conversion"]
# Split into train/test (80/20)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
Stepwise Regression Function
The stepwise_selection function iteratively performs forward inclusion (adding the excluded predictor with the lowest p‑value below 0.01) and backward elimination (removing the included predictor with the highest p‑value above 0.05) until convergence—yielding a concise set of significant features.
# Block 3: Forward–backward stepwise selection
def stepwise_selection(X, y,
initial_list=[],
threshold_in=0.01,
threshold_out=0.05,
verbose=True):
included = list(initial_list)
while True:
changed = False
# Forward step: consider adding each excluded variable
excluded = list(set(X.columns) - set(included))
new_pvals = pd.Series(index=excluded, dtype=float)
for col in excluded:
model = sm.OLS(y, sm.add_constant(X[included + [col]])).fit()
new_pvals[col] = model.pvalues[col]
best_pval = new_pvals.min()
if best_pval < threshold_in:
best_var = new_pvals.idxmin()
included.append(best_var)
changed = True
if verbose:
print(f"Add {best_var:30} p-value {best_pval:.6f}")
# Backward step: consider removing each included variable
model = sm.OLS(y, sm.add_constant(X[included])).fit()
pvals = model.pvalues.iloc[1:] # exclude intercept
worst_pval = pvals.max()
if worst_pval > threshold_out:
worst_var = pvals.idxmax()
included.remove(worst_var)
changed = True
if verbose:
print(f"Drop {worst_var:30} p-value {worst_pval:.6f}")
if not changed:
break
return included
Model Building & Evaluation
- Model Fitting: Using the selected predictors, we fit an Ordinary Least Squares regression via statsmodels. The .summary() output shows coefficient estimates, standard errors, p‑values, R², and diagnostic statistics, facilitating interpretation of each factor’s impact on cost efficiency.
- Evaluation: We generate predictions on the held‑out test set and compute R² (variance explained) and RMSE (root‑mean‑square error) to quantify model generalisation.
# Block 4: Feature selection
selected_features = stepwise_selection(X_train, y_train)
# Fit final OLS model
X_train_sel = sm.add_constant(X_train[selected_features])
model = sm.OLS(y_train, X_train_sel).fit()
print(model.summary())
# Predict on test set
X_test_sel = sm.add_constant(X_test[selected_features])
y_pred = model.predict(X_test_sel)
# Compute metrics
print("Test R²:", r2_score(y_test, y_pred))
print("Test RMSE:", np.sqrt(mean_squared_error(y_test, y_pred)))
Residual Diagnostics
Plotting residuals versus predicted cost checks for non‑random patterns or heteroscedasticity, validating key OLS assumptions.
# Block 5: Residual plot
residuals = y_test - y_pred
plt.scatter(y_pred, residuals)
plt.axhline(0, linestyle="--")
plt.xlabel("Predicted Cost per Conversion")
plt.ylabel("Residuals")
plt.title("Residuals vs. Predicted Cost")
plt.show()
Summary
By applying stepwise regression to ad campaign data, we distil the most influential drivers of cost per conversion—such as budget allocation across channels, targeting regions, and creative formats—while pruning less informative variables.
The resulting linear model achieves a strong balance between interpretability and predictive accuracy (high R², low RMSE), empowering marketers to allocate spend effectively and maximise ROI through a data‑driven strategy.