Lesson 08
Dimensionality Reduction
Use PCA to summarise many correlated features into a smaller set of uncorrelated principal components, read the explained variance ratio, and combine PCA with a classifier.
When features are correlated, much of the information is redundant. Principal Component Analysis (PCA) finds new axes — principal components — that capture the most variance. You keep the first few components and discard the rest, reducing noise and memory without losing much signal.
Explained variance
explained_variance_ratio_ tells you what fraction of the total variance each component captures. Components are sorted from highest to lowest, so the first one always explains the most.
import numpy as np
import pandas as pd
from numpy.random import default_rng
rng = default_rng(42)
n = 300
experience = rng.integers(1, 21, n)
certifications = rng.integers(0, 7, n)
overtime = rng.integers(0, 45, n)
education = rng.choice([10, 12, 14, 16, 18], n, p=[0.05, 0.25, 0.35, 0.25, 0.10])
noise = rng.normal(0, 4500, n)
salary = (20000 + experience * 3200 + (education - 10) * 1500 +
certifications * 1200 + overtime * 80 + noise).round(-2)
salary = np.clip(salary, 18000, 160000).astype(int)
senior = (salary >= 65650).astype(int)
top = ((experience >= 15) & (certifications >= 5)).astype(int)
employees = pd.DataFrame(dict(
experience=experience, certifications=certifications,
overtime=overtime, education=education, salary=salary,
senior=senior, top=top,
))
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import numpy as np
X = employees[["experience", "certifications", "overtime", "education"]].values
X_scaled = StandardScaler().fit_transform(X)
pca = PCA(n_components=4)
pca.fit(X_scaled)
ratio = pca.explained_variance_ratio_
for i, r in enumerate(ratio, 1):
print(f"PC{i}: {r:.3f} cumulative: {ratio[:i].sum():.3f}")Output:
PC1: 0.274 cumulative: 0.274
PC2: 0.260 cumulative: 0.534
PC3: 0.242 cumulative: 0.776
PC4: 0.224 cumulative: 1.000The first two components capture most of the variance. Using two instead of four features loses little information.
Reducing dimensions
import numpy as np
import pandas as pd
from numpy.random import default_rng
rng = default_rng(42)
n = 300
experience = rng.integers(1, 21, n)
certifications = rng.integers(0, 7, n)
overtime = rng.integers(0, 45, n)
education = rng.choice([10, 12, 14, 16, 18], n, p=[0.05, 0.25, 0.35, 0.25, 0.10])
noise = rng.normal(0, 4500, n)
salary = (20000 + experience * 3200 + (education - 10) * 1500 +
certifications * 1200 + overtime * 80 + noise).round(-2)
salary = np.clip(salary, 18000, 160000).astype(int)
senior = (salary >= 65650).astype(int)
top = ((experience >= 15) & (certifications >= 5)).astype(int)
employees = pd.DataFrame(dict(
experience=experience, certifications=certifications,
overtime=overtime, education=education, salary=salary,
senior=senior, top=top,
))
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import numpy as np
X = employees[["experience", "certifications", "overtime", "education"]].values
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
pca = PCA(n_components=2)
X_2d = pca.fit_transform(X_scaled)
print(f"original shape: {X.shape}")
print(f"reduced shape: {X_2d.shape}")
print(f"variance kept: {pca.explained_variance_ratio_.sum():.3f}")Output:
original shape: (300, 4)
reduced shape: (300, 2)
variance kept: 0.534PCA in a pipeline
Wrapping PCA in a pipeline prevents leakage: the scaler and PCA fit on training rows only, then transform test rows with the same learned parameters.
import numpy as np
import pandas as pd
from numpy.random import default_rng
rng = default_rng(42)
n = 300
experience = rng.integers(1, 21, n)
certifications = rng.integers(0, 7, n)
overtime = rng.integers(0, 45, n)
education = rng.choice([10, 12, 14, 16, 18], n, p=[0.05, 0.25, 0.35, 0.25, 0.10])
noise = rng.normal(0, 4500, n)
salary = (20000 + experience * 3200 + (education - 10) * 1500 +
certifications * 1200 + overtime * 80 + noise).round(-2)
salary = np.clip(salary, 18000, 160000).astype(int)
senior = (salary >= 65650).astype(int)
top = ((experience >= 15) & (certifications >= 5)).astype(int)
employees = pd.DataFrame(dict(
experience=experience, certifications=certifications,
overtime=overtime, education=education, salary=salary,
senior=senior, top=top,
))
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import train_test_split
X = employees[["experience", "certifications", "overtime", "education"]].values
y = employees["senior"].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
full = make_pipeline(StandardScaler(), KNeighborsClassifier(11))
reduced = make_pipeline(StandardScaler(), PCA(n_components=2), KNeighborsClassifier(11))
full.fit(X_train, y_train)
reduced.fit(X_train, y_train)
print(f"all 4 features: {full.score(X_test, y_test):.3f}")
print(f"2 components: {reduced.score(X_test, y_test):.3f}")Output:
all 4 features: 0.867
2 components: 0.717Key takeaways
- PCA finds uncorrelated components that explain the most variance
- explained_variance_ratio_ shows how much each component contributes
- Always scale features before PCA
- Use PCA inside a pipeline to prevent leakage
- PCA is useful for visualisation, noise reduction, and speeding up slow models
Loading playground…
Check your understanding
Dimensionality Reduction Quiz
What is the goal of PCA?
1 / 10