team-project-gui / src /plots.py
nikos99n's picture
remove random state, matrix labels on specific rows
2607de5
Raw
History Blame Contribute Delete
4.04 kB
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import os
from sklearn.metrics import confusion_matrix, roc_curve, auc, classification_report
from sklearn.preprocessing import label_binarize
from . import config
def plot_confusion_matrix(y_true, y_pred, classes, model_name):
"""Generates and saves both Raw and Normalized confusion matrix heatmaps."""
# 1. Determine Alphabetical Order
# y_true/y_pred are indices (0, 1, 2...) mapping to the original 'classes' list.
# We want to display them in alphabetical order of the class names.
sorted_indices = np.argsort(classes)
sorted_classes = np.array(classes)[sorted_indices]
# 2. Calculate Raw Matrix
# passing 'labels=sorted_indices' forces the matrix rows/cols to follow the alphabetical order
cm = confusion_matrix(y_true, y_pred, labels=sorted_indices)
# Plot Raw Counts
plt.figure(figsize=(10, 8))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=sorted_classes, yticklabels=sorted_classes)
plt.title(f"{model_name} Confusion Matrix (Counts)")
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.xticks(rotation=45)
plt.tight_layout()
filename = f"{model_name.lower()}_confusion_matrix.png"
plt.savefig(os.path.join(config.MODEL_DIR, filename))
plt.close()
# 3. Calculate Normalized Matrix
# Divide each row element by the sum of that row (True Label count)
cm_norm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
# Replace NaN with 0 (safe guard for empty classes)
cm_norm = np.nan_to_num(cm_norm)
# Plot Normalized Percentages
plt.figure(figsize=(10, 8))
# Use fmt='.2f' to show 2 decimal places (e.g., 0.95)
sns.heatmap(cm_norm, annot=True, fmt='.2f', cmap='Greens',
xticklabels=sorted_classes, yticklabels=sorted_classes)
plt.title(f"{model_name} Confusion Matrix (Normalized)")
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.xticks(rotation=45)
plt.tight_layout()
filename_norm = f"{model_name.lower()}_confusion_matrix_normalized.png"
plt.savefig(os.path.join(config.MODEL_DIR, filename_norm))
plt.close()
def plot_multiclass_roc(model, X_test, y_test, classes, model_name):
"""Generates and saves a Multi-class ROC Curve."""
# 1. Binarize labels (One-vs-Rest)
y_test_bin = label_binarize(y_test, classes=range(len(classes)))
n_classes = y_test_bin.shape[1]
# 2. Get probabilities
if hasattr(model, "predict_proba"):
y_score = model.predict_proba(X_test)
else:
print(f"{model_name} does not support probability prediction. Skipping ROC.")
return
# 3. Compute ROC curve and ROC area for each class
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
fpr[i], tpr[i], _ = roc_curve(y_test_bin[:, i], y_score[:, i])
roc_auc[i] = auc(fpr[i], tpr[i])
# 4. Plot
plt.figure(figsize=(10, 8))
colors = plt.cm.rainbow(np.linspace(0, 1, n_classes))
for i, color in zip(range(n_classes), colors):
plt.plot(fpr[i], tpr[i], color=color, lw=2,
label=f'{classes[i]} (AUC = {roc_auc[i]:.2f})')
plt.plot([0, 1], [0, 1], 'k--', lw=2) # Diagonal line
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title(f'{model_name} Multi-class ROC Curve')
plt.legend(loc="lower right")
plt.tight_layout()
filename = f"{model_name.lower()}_roc_curve.png"
plt.savefig(os.path.join(config.MODEL_DIR, filename))
plt.close()
def save_classification_report(y_true, y_pred, classes, model_name):
"""Saves the text classification report."""
report = classification_report(y_true, y_pred, target_names=classes)
filename = f"{model_name.lower()}_report.txt"
with open(os.path.join(config.MODEL_DIR, filename), "w") as f:
f.write(report)
print(f"Report saved to {filename}")