You have 240 measurements of three features, x, y and z, and want to keep two numbers each. Feature z has the smallest variance, 2.89 against 5.81 and 4.56, so you drop it. It throws away 21.8% of the variance. The cloud is thin, but not along any raw axis.
A cloud of 240 points in 3D, with its principal axes v₁, v₂ and v₃ drawn at two standard deviations (σ = 3, 2, 0.5), the PC1–PC2 plane and the raw axes x, y and z. The slider flattens the cloud onto the PC1–PC2 plane from t = 0 to 1, then onto the PC1 line from 1 to 2. t = 0. The full cloud. PC1, PC2 and PC3 carry 67.9%, 30.2% and 1.9% of the variance. The raw features x, y and z carry 43.8%, 34.4% and 21.8%.
The amber line is PC1, the solid line PC2 and the dashed line PC3, each drawn two standard deviations either side of the centre. Slide from to and every point moves onto the PC1–PC2 plane, keeping 98.1% of the variance. From to the points move onto the PC1 line, which keeps 67.9%.
Principal components from the SVD#
PCA is the statistical reading of Week 13's SVD. Collect observations of features into , rows as observations. Subtract each column's mean, so the cloud sits at the origin, and call the result . Uncentred, the first direction is pulled toward the mean.
Take the SVD . The principal components are the columns of , ordered by decreasing .
Why maximum variance. For a unit vector , the variance of the data along is
is the sample covariance. With , , so it is largest at the top eigenvector , with value .
The scene's numbers#
The scene's cloud is built so that its covariance is exact:
with Week 13's axes , and . The total variance is the trace, and the trace does not change with the basis:
So PC1, PC2 and PC3 carry , and . The diagonal of gives the raw features , and .
Dropping a raw feature is not dropping a component. Each raw variance is a mix of all three eigenvalues. For , , because leans on and alike. No choice of two raw features keeps more than . The best plane keeps , and it is oblique.
Truncation is projection. Keeping components replaces by , which is the slider at for . Each point moves to , and the share of variance lost is . By Eckart–Young this is the best rank- approximation in both the Frobenius and the spectral norm, so no other plane keeps more.
The Fourier matrix and the FFT#
Week 13 ended with unitary matrices. The most used one is the Fourier matrix. With , for , and is the discrete Fourier transform (DFT), . NumPy and most libraries use the conjugate sign, . For , and
Its columns are orthogonal: , so is unitary and .
The product costs . The FFT splits into its even and odd entries, transforms each half at size (with ), and combines them, for a power of 2:
The cost obeys , so : levels of work.
Course recap#
- (Weeks 3–4) turns into two triangular solves.
- (Weeks 7–8) solves least squares without the normal equations.
- and (Weeks 9–12) give powers and quadratic forms.
- (Weeks 13–14) works for every matrix, and for centred data its and are PCA.
- is a unitary change of basis, made cheap by the FFT.
Knowledge Check#
Before you move the slider, predict from the variances 9, 4 and 0.25: what percentage does the cloud keep at t = 1? Give one decimal, then check the caption.
Set the slider to t = 2. Along which line do all the points lie?
Browser lab: PCA of the scene's cloud#
Rebuild the scene's cloud, run PCA, and check the FFT.
import numpy as np
import matplotlib.pyplot as plt
V = np.array([[2, 2, 1], [-2, 1, 2], [1, -2, 2]]).T / 3 # columns v1, v2, v3
sigma = np.array([3.0, 2.0, 0.5])
rng = np.random.default_rng(14)
Z = rng.normal(size=(240, 3))
Z -= Z.mean(axis=0)
L = np.linalg.cholesky(np.cov(Z, rowvar=False))
Z = Z @ np.linalg.inv(L).T # sample covariance exactly I
X = (Z * sigma) @ V.T # sample covariance exactly V diag(9, 4, 0.25) Vᵀ
Xc = X - X.mean(axis=0)
U, S, Vt = np.linalg.svd(Xc, full_matrices=False)
var = S**2 / (len(X) - 1)
total = var.sum()
print("PC variances ", var.round(4), "shares", (var / total).round(3))
print("raw variances ", Xc.var(axis=0, ddof=1).round(4))
print("keep x, y ", round(1 - Xc[:, 2].var(ddof=1) / total, 3))
print("keep PC1, PC2 ", round((var[0] + var[1]) / total, 3))
print("Vᵀ rows ", Vt.round(3).tolist())
scores = Xc @ Vt.T
fig, axes = plt.subplots(1, 2, figsize=(10, 4.2))
axes[0].scatter(Xc[:, 0], Xc[:, 1], s=8)
axes[0].set_title("raw features x, y")
axes[1].scatter(scores[:, 0], scores[:, 1], s=8)
axes[1].set_title("scores on PC1, PC2")
for ax in axes:
ax.set_aspect("equal")
plt.tight_layout()
plt.show()
n = 8
F = np.exp(2j * np.pi * np.outer(np.arange(n), np.arange(n)) / n)
x = rng.normal(size=n)
print("F^H F = nI:", np.allclose(F.conj().T @ F, n * np.eye(n)))
print("fft matches F̄x:", np.allclose(np.fft.fft(x), F.conj() @ x))
The PC variances print as , and . Keeping and keeps , against . The printed rows are , and , possibly with a sign flipped, as in Week 13. The FFT check matches , NumPy's sign. Change sigma to [3, 2, 1] and predict the gap before you run it.
Key takeaways#
PCA is the SVD of the centred data: the components are the , and the variance along is . A thin cloud need not be thin along a raw axis: in the scene, dropping keeps 78.2% and dropping PC3 keeps 98.1%. The Fourier matrix is unitary up to , and the FFT applies it in .
Further reading#
- Gilbert Strang, Introduction to Linear Algebra (6th ed., Wellesley-Cambridge Press, 2023). Covers PCA through the SVD, and the Fourier matrix and the FFT.
- Lloyd N. Trefethen & David Bau, Numerical Linear Algebra (SIAM, 1997). The SVD and the Eckart–Young theorem behind PCA as low-rank approximation.
- Steven L. Brunton & J. Nathan Kutz, Data-Driven Science and Engineering (Cambridge University Press, 2019). PCA for dimensionality reduction and the FFT for signal processing.
Reading is free. PDF and ePub unlock with the newsletter. Join once and every lesson opens in this browser.