PCA (Principal Component Analysis) › Visualizing PCA
Michael BrenndoerferJuly 21, 20253 min read
Machine Learning from ScratchAll chapters Part 6 of 9Explainability
PCA (Principal Component Analysis) › Visualizing PCAOne section · Sections in this chapter4 / 9 ↓
Sections in this chapter9
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
The mathematics is clearer when you see it in action. Let's visualize PCA on a simple two-dimensional dataset where we can see what's happening.
In[3]:
Codeimport sys
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
for parent in [Path.cwd(), *Path.cwd().parents]:
helper_dir = (
parent / "apps/web/content/notebooks/1_machine-learning-from-scratch"
)
if (helper_dir / "mls_plot_style.py").exists():
if str(helper_dir) not in sys.path:
sys.path.insert(0, str(helper_dir))
break
else:
raise FileNotFoundError("Could not locate mls_plot_style.py")
from mls_plot_style import PALETTE, polish_axes, use_mls_style
# Set random seed for reproducibility
np.random.seed(42)
# Create correlated 2D data
n_samples = 200
mean = [0, 0]
cov = [[2, 1.5], [1.5, 2]]
X = np.random.multivariate_normal(mean, cov, n_samples)
# Standardize the data
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Apply PCA
pca = PCA()
X_pca = pca.fit_transform(X_scaled)
use_mls_style(fixed_canvas=True)
# Original data
fig, ax1 = plt.subplots(figsize=(3, 2.8))
ax1.scatter(
X_scaled[:, 0],
X_scaled[:, 1],
alpha=0.66,
c=PALETTE["primary"],
s=18,
edgecolors=PALETTE["paper"],
linewidths=0.3,
)
ax1.set_xlabel("Feature 1")
ax1.set_ylabel("Feature 2")
ax1.set_title("Original Data (Standardized)")
ax1.set_aspect("equal")
# Add principal component directions
pc1 = pca.components_[0] * np.sqrt(pca.explained_variance_[0])
pc2 = pca.components_[1] * np.sqrt(pca.explained_variance_[1])
ax1.arrow(
0,
0,
pc1[0],
pc1[1],
head_width=0.1,
head_length=0.1,
fc=PALETTE["coral"],
ec=PALETTE["coral"],
linewidth=2,
)
ax1.arrow(
0,
0,
pc2[0],
pc2[1],
head_width=0.1,
head_length=0.1,
fc=PALETTE["teal"],
ec=PALETTE["teal"],
linewidth=2,
)
ax1.text(
pc1[0],
pc1[1],
"PC1",
fontsize=8,
color=PALETTE["coral"],
weight="bold",
)
ax1.text(
pc2[0],
pc2[1],
"PC2",
fontsize=8,
color=PALETTE["teal"],
weight="bold",
)
polish_axes(ax1)
fig.subplots_adjust(left=0.18, right=0.97, bottom=0.18, top=0.86)
plt.show()
# PCA transformed data
fig, ax2 = plt.subplots(figsize=(3, 2.8))
ax2.scatter(
X_pca[:, 0],
X_pca[:, 1],
alpha=0.66,
c=PALETTE["purple"],
s=18,
edgecolors=PALETTE["paper"],
linewidths=0.3,
)
ax2.set_xlabel("First Principal Component")
ax2.set_ylabel("Second Principal Component")
ax2.set_title("Data After PCA Transformation")
ax2.set_aspect("equal")
polish_axes(ax2)
fig.subplots_adjust(left=0.18, right=0.97, bottom=0.18, top=0.86)
plt.show()
# Print explained variance ratios
print("Explained variance ratios:")
for i, ratio in enumerate(pca.explained_variance_ratio_):
print(f"PC{i + 1}: {ratio:.3f} ({ratio * 100:.1f}%)")Out[3]:
Visualization

Explained variance ratios: PC1: 0.873 (87.3%) PC2: 0.127 (12.7%)
In[4]:
Codeuse_mls_style(fixed_canvas=True)
# Create scree plot
fig, ax = plt.subplots(figsize=(3, 2.8))
ax.plot(
range(1, len(pca.explained_variance_ratio_) + 1),
pca.explained_variance_ratio_,
color=PALETTE["primary"],
marker="o",
linewidth=2,
markersize=5,
)
ax.set_xlabel("Principal Component")
ax.set_ylabel("Explained Variance Ratio")
ax.set_title("Scree Plot")
ax.set_xticks(range(1, len(pca.explained_variance_ratio_) + 1))
polish_axes(ax)
fig.subplots_adjust(left=0.20, right=0.97, bottom=0.20, top=0.86)
plt.show()
# Cumulative variance explained
cumulative_variance = np.cumsum(pca.explained_variance_ratio_)
fig, ax = plt.subplots(figsize=(3, 2.8))
ax.plot(
range(1, len(cumulative_variance) + 1),
cumulative_variance,
color=PALETTE["rose"],
marker="o",
linewidth=2,
markersize=5,
)
ax.set_xlabel("Number of Components")
ax.set_ylabel("Cumulative Variance Explained")
ax.set_title("Cumulative Variance Explained")
ax.set_xticks(range(1, len(cumulative_variance) + 1))
ax.axhline(
y=0.95,
color=PALETTE["teal"],
linestyle="--",
alpha=0.85,
label="95% threshold",
)
ax.legend(loc="lower right")
polish_axes(ax)
fig.subplots_adjust(left=0.20, right=0.97, bottom=0.20, top=0.86)
plt.show()Out[4]:
Visualization
