Python code used for model development used in TB diagnosis prediction.
# =========================================================
# 1. IMPORT LIBRARIES
# =========================================================
import pandas as pd
import numpy as np
import [Link] as plt
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from [Link] import StandardScaler, OneHotEncoder
from [Link] import ColumnTransformer
from [Link] import SimpleImputer
from sklearn.linear_model import LogisticRegression
from [Link] import RandomForestClassifier, GradientBoostingClassifier
from sklearn.neural_network import MLPClassifier
from [Link] import (
confusion_matrix, accuracy_score, precision_score,
recall_score, f1_score, roc_auc_score, roc_curve
from [Link] import calibration_curve
from [Link] import Pipeline
from imblearn.over_sampling import SMOTE
from [Link] import loguniform
# =========================================================
# 2. LOAD DATA
# =========================================================
data = pd.read_csv("Exit [Link]")
print("\n=========== DATASET BEFORE SPLIT ===========")
print("Dataset shape:", [Link])
print("\nFirst 5 rows:")
print([Link]())
print("\nOutcome distribution:")
print(data["TB_diagnosis"].value_counts())
print(data["TB_diagnosis"].value_counts(normalize=True) * 100)
y = data["TB_diagnosis"] # 'Negative' / 'Positive'
X = [Link](columns=["TB_diagnosis"])
# =========================================================
# 3. IDENTIFY VARIABLE TYPES
# =========================================================
categorical_features = X.select_dtypes(include=["object", "category"]).columns
numeric_features = X.select_dtypes(include=["int64", "float64"]).columns
print("\nCategorical variables:", list(categorical_features))
print("Numeric variables:", list(numeric_features))
# =========================================================
# 4. PREPROCESSING
# =========================================================
numeric_transformer = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
])
categorical_transformer = Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("encoder", OneHotEncoder(drop="first", handle_unknown="ignore"))
])
preprocessor = ColumnTransformer([
("num", numeric_transformer, numeric_features),
("cat", categorical_transformer, categorical_features)
])
# =========================================================
# 5. TRAIN / VALIDATION / TEST SPLIT (70 / 15 / 15)
# =========================================================
X_train, X_temp, y_train, y_temp = train_test_split(
X, y, test_size=0.30, stratify=y, random_state=42
X_val, X_test, y_val, y_test = train_test_split(
X_temp, y_temp, test_size=0.50, stratify=y_temp, random_state=42
)
print("\n=========== DATASET AFTER SPLIT ===========")
for name, y_part, X_part in [
("Training", y_train, X_train),
("Validation", y_val, X_val),
("Test", y_test, X_test)
]:
print(f"\n{name} set shape:", X_part.shape)
print(y_part.value_counts())
print(y_part.value_counts(normalize=True) * 100)
# =========================================================
# 6. SMOTE
# =========================================================
smote = SMOTE(sampling_strategy=0.8, random_state=42)
# =========================================================
# 7. FAST MLP HYPERPARAMETER TUNING (RandomizedSearchCV)
# =========================================================
mlp = MLPClassifier(
max_iter=1000,
early_stopping=True,
random_state=42
)
mlp_pipeline = Pipeline([
("preprocess", preprocessor),
("smote", smote),
("model", mlp)
])
param_distributions = {
"model__hidden_layer_sizes": [(50,), (100,), (50, 25), (100, 50)],
"model__learning_rate_init": loguniform(1e-4, 1e-2),
"model__alpha": loguniform(1e-5, 1e-2),
"model__activation": ["relu", "tanh"]
random_search = RandomizedSearchCV(
mlp_pipeline,
param_distributions,
n_iter=30,
scoring="roc_auc",
cv=5,
random_state=42,
n_jobs=-1,
verbose=1
random_search.fit(X_train, (y_train == "Positive").astype(int))
best_mlp_pipeline = random_search.best_estimator_
print("\nBest MLP ROC-AUC:", round(random_search.best_score_, 3))
# =========================================================
# 8. FINAL MODELS
# =========================================================
models = {
"Logistic Regression": LogisticRegression(max_iter=1000),
"Random Forest": RandomForestClassifier(n_estimators=200, random_state=42),
"Gradient Boosting": GradientBoostingClassifier(random_state=42)
pipelines = {}
for name, model in [Link]():
pipe = Pipeline([
("preprocess", preprocessor),
("smote", smote),
("model", model)
])
[Link](X_train, y_train)
pipelines[name] = pipe
pipelines["MLP (Tuned)"] = best_mlp_pipeline
# =========================================================
# 9. CONFUSION MATRIX FUNCTION
# =========================================================
def plot_confusion_matrix(ax, cm, classes, title):
[Link](cm, cmap=[Link])
ax.set_title(title)
tick_marks = [Link](len(classes))
ax.set_xticks(tick_marks, classes)
ax.set_yticks(tick_marks, classes)
thresh = [Link]() / 2
for i in range([Link][0]):
for j in range([Link][1]):
[Link](j, i, cm[i, j],
ha="center",
color="white" if cm[i, j] > thresh else "black")
ax.set_ylabel("True label")
ax.set_xlabel("Predicted label")
# =========================================================
# 10. EVALUATION FUNCTION
# =========================================================
def ensure_string_labels(y_pred):
"""
Convert numeric predictions (0/1) to string labels
to match TB_diagnosis format.
"""
if [Link](y_pred.dtype, [Link]):
return [Link](y_pred == 1, "Positive", "Negative")
return y_pred
def evaluate(pipe, X, y, label):
y_pred = [Link](X)
y_pred = ensure_string_labels(y_pred)
y_prob = pipe.predict_proba(X)[:, 1]
cm = confusion_matrix(
y, y_pred, labels=["Negative", "Positive"]
metrics = {
"Accuracy": accuracy_score(y, y_pred),
"Precision": precision_score(y, y_pred, pos_label="Positive"),
"Recall": recall_score(y, y_pred, pos_label="Positive"),
"F1-score": f1_score(y, y_pred, pos_label="Positive"),
"ROC-AUC": roc_auc_score((y == "Positive").astype(int), y_prob),
"CM": cm
return metrics
# =========================================================
# 11. MODEL EVALUATION & CONSOLIDATED CONFUSION MATRICES
# =========================================================
all_results_summary = [] # To store metrics for comparison table
for name, pipe in [Link]():
print(f"\n======================================")
print(f"MODEL: {name}")
# Evaluate on all sets
train_metrics = evaluate(pipe, X_train, y_train, "Training")
val_metrics = evaluate(pipe, X_val, y_val, "Validation")
test_metrics = evaluate(pipe, X_test, y_test, "Test")
# Print metrics
print(f"\nTraining:")
print(f" Accuracy : {train_metrics['Accuracy']:.4f}")
print(f" Precision: {train_metrics['Precision']:.4f}")
print(f" Recall : {train_metrics['Recall']:.4f}")
print(f" F1-score : {train_metrics['F1-score']:.4f}")
print(f" ROC-AUC : {train_metrics['ROC-AUC']:.4f}")
print(f"\nValidation:")
print(f" Accuracy : {val_metrics['Accuracy']:.4f}")
print(f" Precision: {val_metrics['Precision']:.4f}")
print(f" Recall : {val_metrics['Recall']:.4f}")
print(f" F1-score : {val_metrics['F1-score']:.4f}")
print(f" ROC-AUC : {val_metrics['ROC-AUC']:.4f}")
print(f"\nTest:")
print(f" Accuracy : {test_metrics['Accuracy']:.4f}")
print(f" Precision: {test_metrics['Precision']:.4f}")
print(f" Recall : {test_metrics['Recall']:.4f}")
print(f" F1-score : {test_metrics['F1-score']:.4f}")
print(f" ROC-AUC : {test_metrics['ROC-AUC']:.4f}")
# Store test metrics for final comparison table
all_results_summary.append({
"Model": name,
"Accuracy": test_metrics['Accuracy'],
"Precision": test_metrics['Precision'],
"Recall (Sensitivity)": test_metrics['Recall'],
"F1-score": test_metrics['F1-score'],
"ROC-AUC": test_metrics['ROC-AUC']
})
# Consolidated Confusion Matrices
fig, axes = [Link](1, 3, figsize=(18, 6))
plot_confusion_matrix(axes[0], train_metrics['CM'], ["Negative", "Positive"], "Training Set")
plot_confusion_matrix(axes[1], val_metrics['CM'], ["Negative", "Positive"], "Validation Set")
plot_confusion_matrix(axes[2], test_metrics['CM'], ["Negative", "Positive"], "Test Set")
[Link](f'Confusion Matrices for {name}', fontsize=16)
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
[Link]()
# =========================================================
# 12. ROC CURVES (TEST SET)
# =========================================================
[Link]()
for name, pipe in [Link]():
y_prob = pipe.predict_proba(X_test)[:, 1]
fpr, tpr, _ = roc_curve((y_test == "Positive").astype(int), y_prob)
auc = roc_auc_score((y_test == "Positive").astype(int), y_prob)
[Link](fpr, tpr, label=f"{name} (AUC={auc:.2f})")
[Link]([0, 1], [0, 1], linestyle="--")
[Link]("False Positive Rate")
[Link]("True Positive Rate")
[Link]("ROC Curves – Test Set")
[Link]()
[Link]()
# =========================================================
# 13. CALIBRATION PLOTS
# =========================================================
[Link]()
for name, pipe in [Link]():
prob_true, prob_pred = calibration_curve(
(y_test == "Positive").astype(int),
pipe.predict_proba(X_test)[:, 1],
n_bins=10
[Link](prob_pred, prob_true, marker="o", label=name)
[Link]([0, 1], [0, 1], linestyle="--")
[Link]("Predicted Probability")
[Link]("Observed Probability")
[Link]("Calibration Plot – Test Set")
[Link]()
[Link]()
# =========================================================
# 14. MODEL COMPARISON TABLE
# =========================================================
comparison_df = [Link](all_results_summary)
print("\n=========== MODEL COMPARISON (TEST SET) =========---")
print(comparison_df.round(3))