Principal Component Analysis is usually introduced via the covariance matrix, but the more numerically stable — and more revealing — route is through the Singular Value Decomposition of the data matrix directly.
The covariance route
Given a centered data matrix (rows are observations, columns are features, each column mean-zero), PCA diagonalizes the covariance matrix
Since is symmetric positive semi-definite, it admits an eigendecomposition , where ’s columns are the principal directions and holds the variances explained by each component.
The SVD route
Instead, decompose directly:
where and are orthogonal, and is diagonal with singular values .
Substituting into :
using . Comparing with , this means:
The right singular vectors are the principal directions — no need to ever form .
Why this matters in practice
Forming explicitly squares the condition number of the underlying problem: if has condition number , then has condition number . For features with very different scales, this can meaningfully degrade numerical precision. Computing the SVD of directly avoids that squaring entirely, which is why sklearn.decomposition.PCA uses SVD under the hood rather than eigendecomposing the covariance matrix.
import numpy as np
def pca_via_svd(X, n_components):
X_centered = X - X.mean(axis=0)
U, S, Vt = np.linalg.svd(X_centered, full_matrices=False)
components = Vt[:n_components]
explained_variance = (S[:n_components] ** 2) / (X.shape[0] - 1)
scores = X_centered @ components.T
return scores, components, explained_variance
Explained variance ratio
The proportion of total variance captured by the first components is
which is exactly what explained_variance_ratio_ reports in scikit-learn — computed from singular values, never from an explicit covariance matrix.