Medical Procedure Time Prediction using Stepwise Regression in ML
Get Job-ready with hands-on learning & real-time projects - Enroll Now!
Operating rooms (ORs) are high‑cost environments where inaccurate estimates of procedure duration can lead to scheduling inefficiencies, increased patient wait times, and resource under‑ or over-utilisation.
In this medical procedure time prediction ML project, we will predict the duration of surgical procedures based on pre‑operative features—such as procedure type, patient demographics (age, BMI), and surgeon characteristics—by fitting a linear regression model with stepwise feature selection. Therefore, the resulting model will identify the key influencers of procedure time, helping hospital administrators reduce operational costs.
Libraries Required
import pandas as pd # Data manipulation import numpy as np # Numerical operations import statsmodels.api as sm # Statistical modeling (OLS) 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
Prediction of Surgery Duration
Step-by-Step Code Implementation
Data Loading & Initial Inspection
We load the CSV from Kaggle. Initial commands (.head(), .info(), .describe()) reveal data types, missing values, and basic statistics.
# Block 1: Load dataset
# Kaggle Competition: Prediction of Surgery Duration :contentReference[oaicite:1]{index=1}
df = pd.read_csv("train.csv")
# Inspect the data
print(df.head())
print(df.info())
Data Preprocessing
Categorical fields—such as ProcedureType and SurgeonID—are transformed via one‑hot encoding. We then drop any rows with missing entries to maintain a clean modelling dataset. The features matrix X excludes the Duration column, which serves as our response variable y. The data is divided into training (80%) and testing (20%) sets.
# Block 2: Encode categoricals and clean
# Assume columns: 'ProcedureType', 'SurgeonID', 'PatientAge', 'PatientBMI', plus other numeric features, target='Duration'
df_enc = pd.get_dummies(df, columns=["ProcedureType", "SurgeonID"], drop_first=True)
# Drop rows with missing values, if any
df_enc = df_enc.dropna()
# Define predictors and target
X = df_enc.drop("Duration", axis=1)
y = df_enc["Duration"]
# Split into training and test sets (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: Adds the excluded predictor with the smallest p‑value below 0.01.
- Backward Elimination: Removes the included predictor with the largest p‑value above 0.05.
This process continues until no variables meet the criteria for addition or removal, yielding a parsimonious set of significant predictors.
# 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: try 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(pd.DataFrame(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: try removing each included variable
model = sm.OLS(y, sm.add_constant(pd.DataFrame(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
Using the selected features, we fit an Ordinary Least Squares regression via statsmodels. The printed .summary() provides coefficient estimates, p‑values, R², adjusted R², and diagnostic statistics (e.g., AIC, F‑statistic), offering insights into predictor significance and overall model fit.
Predictions on the held‑out test set allow us to compute R² (explained variance) and RMSE (root‑mean‑square error), quantifying the model’s predictive performance on unseen data.
# Block 4: Feature selection
selected_features = stepwise_selection(X_train, y_train)
# Fit the 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 performance metrics
print("Test R²:", r2_score(y_test, y_pred))
print("Test RMSE:", np.sqrt(mean_squared_error(y_test, y_pred)))
Residual Diagnostics
A residual plot (predicted vs. residuals) checks for non‑random patterns, heteroscedasticity, or outliers—key assumptions for validating the linear regression model.
# Block 5: Residual plot
residuals = y_test - y_pred
plt.scatter(y_pred, residuals)
plt.axhline(0, linestyle="--")
plt.xlabel("Predicted Duration (minutes)")
plt.ylabel("Residuals")
plt.title("Residuals vs. Predicted Duration")
plt.show()
Summary
By applying stepwise regression to surgical procedure data, we find the most influential factors—such as specific procedure types, surgeon identifiers, and patient characteristics—that drive operation duration. Thus, the resulting linear model is both interpretable and performant, achieving a strong balance between explanatory insight (through p‑values and coefficients) and predictive accuracy (high test‑set R², low RMSE).
As a Result, Hospitals can leverage this model to improve OR scheduling, enhance resource utilisation, and reduce costs associated with under‑ or over‑booking surgical slots.
