Files
patient_leakage_detection/classes/visualizations.py
T
rpotter6298 8a136c71fe Add scripts for patient classification and visualization
- Created `classification.py` for comparing image-level and patient-level classification results using various CNN models.
- Implemented `create_patient_groups.py` to extract features, generate PCA/t-SNE plots, and identify patient groups via K-means clustering.
- Added `figure6.py` to generate boxplots for test accuracy across multiple seeds.
- Developed `simple_patient_tsne.py` to perform t-SNE visualization of patient groups and save results in a manifest file.
- Introduced `simple_patient_manifest.csv` to store patient IDs, classes, image counts, and associated images.
2026-06-29 14:30:16 +02:00

149 lines
4.1 KiB
Python

"""
Visualization utilities for feature space exploration.
PCA and t-SNE plots to inspect class separability in the
extracted feature representations.
"""
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
def plot_pca_tsne(X, Y, legend_names=None, perplexity=35,
random_state=42, output_path=None, figsize=(12, 5),
dpi=150):
"""Generate side-by-side PCA and t-SNE plots of feature vectors.
Parameters
----------
X : np.ndarray of shape (n_samples, n_features)
Feature matrix (e.g. VGG16 outputs).
Y : np.ndarray of shape (n_samples,)
Class labels.
legend_names : dict, optional
Mapping from raw label -> display label, e.g.
{"Bengin cases": "Benign", ...}.
perplexity : int
t-SNE perplexity (default 35).
random_state : int
Seed for reproducibility.
output_path : str, optional
Path to save the figure. If None, saved as "pca_tsne.png".
figsize : tuple
Figure dimensions.
dpi : int
Output resolution.
Returns
-------
fig : matplotlib Figure
"""
if legend_names is None:
legend_names = {}
n_samples, n_features = X.shape
# --- PCA: 50D → 2D ---
n_pca = min(50, n_samples - 1, n_features)
pca_pre = PCA(n_components=n_pca, random_state=random_state)
X_pca50 = pca_pre.fit_transform(X)
pca_2d = PCA(n_components=2, random_state=random_state)
X_pca2 = pca_2d.fit_transform(X_pca50)
explained_variance = pca_2d.explained_variance_ratio_ * 100
# --- t-SNE: 50D → 2D ---
tsne = TSNE(
n_components=2,
perplexity=perplexity,
learning_rate="auto",
init="pca",
random_state=random_state
)
X_tsne = tsne.fit_transform(X_pca50)
# --- Plot ---
fig, axes = plt.subplots(1, 2, figsize=figsize)
unique_classes = np.unique(Y)
for class_name in unique_classes:
idx = Y == class_name
label = legend_names.get(class_name, class_name)
axes[0].scatter(
X_pca2[idx, 0], X_pca2[idx, 1],
label=label, alpha=0.7, s=8
)
axes[0].set_xlabel(f"PC1 ({explained_variance[0]:.1f}%)")
axes[0].set_ylabel(f"PC2 ({explained_variance[1]:.1f}%)")
axes[0].set_title("PCA")
axes[0].legend(markerscale=2)
for class_name in unique_classes:
idx = Y == class_name
label = legend_names.get(class_name, class_name)
axes[1].scatter(
X_tsne[idx, 0], X_tsne[idx, 1],
label=label, alpha=0.7, s=8
)
axes[1].set_xlabel("t-SNE dimension 1")
axes[1].set_ylabel("t-SNE dimension 2")
axes[1].set_title("t-SNE")
axes[1].legend(markerscale=2)
plt.tight_layout()
if output_path is None:
output_path = "pca_tsne.png"
plt.savefig(output_path, dpi=dpi)
plt.close()
print(f" Saved PCA/t-SNE plot to {output_path}")
return fig
def plot_cv_accuracy(cv_results, split_type="", output_path=None,
figsize=(7, 5), dpi=150):
"""Plot cross-validation accuracy vs number of selected features.
Parameters
----------
cv_results : list of dict
Each dict has keys "nfeatures" and "best_cv_accuracy".
split_type : str
Label for the plot title, e.g. "Image-level split".
output_path : str, optional
Output file path.
figsize : tuple
dpi : int
Returns
-------
fig : matplotlib Figure
"""
fig, ax = plt.subplots(figsize=figsize)
nfeats = [r["nfeatures"] for r in cv_results]
accs = [r["best_cv_accuracy"] for r in cv_results]
ax.plot(nfeats, accs, marker="o")
ax.set_xscale("log")
ax.set_xlabel("Number of selected features")
ax.set_ylabel("Mean 5-fold validation accuracy")
if split_type:
ax.set_title(split_type)
plt.tight_layout()
if output_path is None:
output_path = f"{split_type.replace(' ', '_').replace('-', '_')}_cv.png"
plt.savefig(output_path, dpi=dpi)
plt.close()
print(f" Saved CV accuracy plot to {output_path}")
return fig