AI Knowledge Tree

Low-rank approximation (Eckart–Young) & PCA

Prove the truncated SVD is the best low-rank approximation, and identify PCA as the SVD of the centered data matrix.

⏱ 34 minPrerequisites:Singular value decomposition

Storing a full m×nm \times n matrix costs mnmn numbers. Often the matrix is approximately low-rank: most of the information lives in a few directions. The Eckart–Young theorem says the truncated SVD is the optimal compression, and PCA applies this directly to data.

Low-rank approximation

The rank-kk truncated SVD of A=UΣV⊤A = U\Sigma V^\top is

Ak=∑i=1kσiuivi⊤,A_k = \sum_{i=1}^k \sigma_i u_i v_i^\top,

using only the kk largest singular values and their associated vectors. AkA_k is a rank-kk matrix (a sum of kk rank-1 outer products).

Eckart–Young theorem. Among all rank-kk matrices BB[1],

∥A−Ak∥F  ≤  ∥A−B∥F,\lVert A - A_k\rVert_F \;\le\; \lVert A - B\rVert_F,

and the same holds with the spectral norm (largest singular value). The truncated SVD is the closest rank-kk matrix to AA in both the Frobenius and spectral norms.

Proof sketch (Frobenius norm). Write the Frobenius norm using singular values: ∥A∥F2=∑iσi2\lVert A\rVert_F^2 = \sum_i \sigma_i^2. The approximation error is

∥A−Ak∥F2=∑i=k+1rσi2.\lVert A - A_k\rVert_F^2 = \sum_{i=k+1}^r \sigma_i^2.

For any rank-kk matrix BB, BB has a null space of dimension ≥n−k\ge n - k, so there exists a nonzero ww in span⁡(v1,…,vk+1)∩ker⁡B\operatorname{span}(v_1, \dots, v_{k+1}) \cap \ker B (the intersection of a (k+1)(k+1)-dimensional space with the (n−k)(n-k)-dimensional null space of BB; by dimension count this is at least 1-dimensional). Then

∥A−B∥F2≥∥(A−B)w/∥w∥∥2=∥Aw∥2≥σk+12,\lVert A - B\rVert_F^2 \ge \lVert (A - B)w/\lVert w\rVert\rVert^2 = \lVert Aw\rVert^2 \ge \sigma_{k+1}^2,

where the last inequality uses the fact that ww is in the span of the first k+1k+1 right singular vectors and ∥Avk+1∥=σk+1\lVert Av_{k+1}\rVert = \sigma_{k+1}. A more careful argument shows the lower bound is ∑i>kσi2\sum_{i>k}\sigma_i^2, matching the error of AkA_k. ■\qquad\blacksquare

Energy captured

The fraction of variance explained by AkA_k is

∑i=1kσi2∑i=1rσi2.\frac{\sum_{i=1}^k \sigma_i^2}{\sum_{i=1}^r \sigma_i^2}.

Plotting this as a function of kk gives the scree plot: the knee where adding more components yields diminishing returns is the natural choice of rank.

PCA as truncated SVD

Given a data matrix X∈Rn×dX \in \mathbb{R}^{n \times d} (rows = samples, columns = features), principal component analysis (PCA) proceeds[2, 3]:

  1. Center: subtract the column means, obtaining X~\tilde{X} with mean zero.
  2. Compute SVD: X~=UΣV⊤\tilde{X} = U\Sigma V^\top.
  3. Project onto kk components: the score matrix is Z=X~VkZ = \tilde{X} V_k, where VkV_k is the first kk columns of VV.

The ii-th column of VV (the ii-th right singular vector) is the ii-th principal direction: the direction of maximum remaining variance after removing the previous i−1i-1 directions. The corresponding singular value σi\sigma_i encodes how much variance lies along it (σi2/n\sigma_i^2/n is the variance in that direction).

The covariance matrix is 1nX~⊤X~=1nVΣ2V⊤\frac{1}{n}\tilde{X}^\top\tilde{X} = \frac{1}{n}V\Sigma^2 V^\top, so the principal directions are the eigenvectors of the sample covariance and the principal variances are its eigenvalues.

Scree plot (left): the first three components capture nearly all the variance, so the rank-k reconstruction error (right) collapses at rank 3, the knee that reveals the true rank.
Scree plot (left): the first three components capture nearly all the variance, so the rank-k reconstruction error (right) collapses at rank 3, the knee that reveals the true rank.

Check it on arrays

python · numpy
import numpy as np

rng = np.random.default_rng(42)

# Synthetic data: 200 samples, 10 features, low-rank signal
d, n, k_true = 10, 200, 3
V_true = np.linalg.qr(rng.normal(size=(d, k_true)))[0]   # 3 orthonormal directions
scores = rng.normal(scale=[5, 3, 1], size=(n, k_true))    # varying variance
X = scores @ V_true.T + 0.2 * rng.normal(size=(n, d))    # signal + noise

# Center
X_c = X - X.mean(axis=0)

# SVD
U, s, Vt = np.linalg.svd(X_c, full_matrices=False)

# Scree: fraction of variance explained
total = (s**2).sum()
explained = np.cumsum(s**2) / total
print("cumulative variance explained (k=1,2,3,4):", np.round(explained[:4], 3))

# Rank-k approximation
k = 3
X_approx = U[:, :k] * s[:k] @ Vt[:k, :]
print(f"rank-{k} Frobenius error / total norm:", np.linalg.norm(X_c - X_approx, 'fro') / np.linalg.norm(X_c, 'fro'))

# PCA scores: project onto first k principal directions
Z = X_c @ Vt[:k].T    # (n, k) scores
print("variance of first component:", np.var(Z[:, 0], ddof=1))
print("expected (s[0]^2 / (n-1)):", s[0]**2 / (n - 1))

The first three components capture most of the variance; the rank-3 approximation has low relative error; and the PCA score variance matches the expected formula.

Exercises

Exercise 1. A matrix has singular values σ=(4,3,2,1)\sigma = (4, 3, 2, 1). Compute the best rank-2 Frobenius approximation error and the fraction of variance explained by the first two components.

Solution

Step 1. By Eckart–Young, the rank-2 error in Frobenius norm uses the discarded singular values σ3,σ4\sigma_3, \sigma_4:

∥A−A2∥F=σ32+σ42=4+1=5≈2.236.\lVert A - A_2\rVert_F = \sqrt{\sigma_3^2 + \sigma_4^2} = \sqrt{4 + 1} = \sqrt{5} \approx 2.236.

Step 2. The fraction of variance explained is the kept energy over the total energy, with energy measured as squared singular values:

σ12+σ22σ12+σ22+σ32+σ42=16+916+9+4+1=2530≈0.833.\frac{\sigma_1^2 + \sigma_2^2}{\sigma_1^2 + \sigma_2^2 + \sigma_3^2 + \sigma_4^2} = \frac{16 + 9}{16 + 9 + 4 + 1} = \frac{25}{30} \approx 0.833.

The first two components hold about 83 percent of the variance, and the leftover error is the root-sum-square of the two smallest singular values.

Exercise 2. Prove that the rank-kk truncated SVD error in Frobenius norm is exactly ∑i>kσi2\sqrt{\sum_{i>k}\sigma_i^2}. Use the unitary invariance of the Frobenius norm.

Solution

Step 1. Write the error matrix using the SVD. With A=∑iσiuivi⊤A = \sum_i \sigma_i u_i v_i^\top and Ak=∑i≤kσiuivi⊤A_k = \sum_{i\le k}\sigma_i u_i v_i^\top, the difference keeps only the tail:

A−Ak=∑i>kσiuivi⊤=UΣ>kV⊤,A - A_k = \sum_{i > k} \sigma_i u_i v_i^\top = U \Sigma_{>k} V^\top,

where Σ>k\Sigma_{>k} zeroes the first kk singular values.

Step 2. The Frobenius norm is unitarily invariant: ∥UMV⊤∥F=∥M∥F\lVert U M V^\top\rVert_F = \lVert M\rVert_F for orthogonal U,VU, V, because ∥M∥F2=tr⁡(M⊤M)\lVert M\rVert_F^2 = \operatorname{tr}(M^\top M) and the trace is unchanged by conjugation with orthogonal matrices. So

∥A−Ak∥F=∥Σ>k∥F=∑i>kσi2.\lVert A - A_k\rVert_F = \lVert \Sigma_{>k}\rVert_F = \sqrt{\sum_{i>k}\sigma_i^2}.

The error is read off directly from the discarded singular values, with no cross terms surviving.

Exercise 3 (code). Generate data along two orthogonal directions with variances 9 and 1 plus small noise. Confirm that PCA recovers the two principal directions (up to sign) and that the first principal variance is near 9.

Solution
python · numpy
import numpy as np

rng = np.random.default_rng(7)
n = 4000
d1 = np.array([1.0, 0.0])           # true principal directions
d2 = np.array([0.0, 1.0])
s1 = rng.normal(scale=3.0, size=n)  # variance 9 along d1
s2 = rng.normal(scale=1.0, size=n)  # variance 1 along d2
X = np.outer(s1, d1) + np.outer(s2, d2) + 0.01 * rng.normal(size=(n, 2))

Xc = X - X.mean(axis=0)
U, sv, Vt = np.linalg.svd(Xc, full_matrices=False)

# First principal variance = sigma_1^2 / (n - 1) should be ~ 9.
var1 = sv[0]**2 / (n - 1)
assert abs(var1 - 9.0) < 0.5

# First principal direction aligns with d1 (sign is arbitrary).
assert abs(abs(Vt[0] @ d1) - 1.0) < 1e-2
print("first principal variance:", round(float(var1), 3))

The largest singular value reproduces the dominant variance of 9, and its right singular vector lines up with the direction that variance lives along, which is what PCA is built to find.

With the best low-rank approximation understood, the next lesson looks at the factorizations that solve linear systems efficiently without forming inverses.


References

  1. [1]Eckart, C., & Young, G. (1936). "The approximation of one matrix by another of lower rank". Psychometrika, 1, 211-218. doi:10.1007/BF02288367. The original optimality proof for the truncated SVD.↩
  2. [2]Jolliffe, I. T. (2002). Principal Component Analysis (2nd ed.). Springer. The standard reference for PCA and the variance-maximization view.↩
  3. [3]Strang, G. (2016). Introduction to Linear Algebra (5th ed.). Wellesley-Cambridge Press. PCA as the SVD of the centered data matrix.↩