Contents
, where is the sample covariance of the centred data , is the -th principal axis (a unit vector in feature space) and is the variance of the data projected on that axis.
Deep Breakdown
What it tells me (Situation Awareness):
PCA looks for the directions in feature space along which the data spreads the most. The variance of the data projected on a unit vector is . Maximising subject to with a Lagrange multiplier gives , which is exactly . So the principal axes are the eigenvectors of , and substituting back gives : the eigenvalue is the variance captured along that axis. The largest eigenvalue gives the first principal component; the next axis is the best direction orthogonal to the first, which is the eigenvector with the second-largest eigenvalue, and so on. Because is symmetric and positive semi-definite, the spectral theorem guarantees real, non-negative eigenvalues and an orthonormal set of eigenvectors, so and the axes form a rotated coordinate system in which the features are uncorrelated. The eigenvalues sum to the total variance, , so is the share of variance explained by axis . In DA/DS: this equation is what sklearn.decomposition.PCA solves (through an SVD of ), what the scree plot shows, and what decides how many components to keep before clustering, regression or visualisation.
Flowchart
Real-world use
Where it is used
Every PCA implementation, factor-style summaries of survey and KPI data, image compression and eigenfaces, decorrelating features before regression (principal component regression), yield-curve analysis in finance (level, slope, curvature are the first three axes), genomics population structure plots, anomaly detection on the residual subspace, and as the first step of whitening.
Problem it solves
Data with many correlated columns is redundant and hard to read: ten KPIs that all move with store size carry roughly one signal. The eigenvector equation finds a new set of orthogonal axes, ordered by how much variance each carries, so a few axes can stand in for many columns with a measured loss.
How it solves the problem
It turns "find the direction of maximum spread" into a linear-algebra problem with an exact answer. The eigenvectors give the directions (loadings), the eigenvalues give the importance of each direction, and orthogonality means each new axis carries information the previous ones do not. Keeping the top axes is the best rank- linear summary in squared error; the variance discarded equals (see Residual Error (Truncated Eigenvalues)), and the scores are the Projection (Encoding).
Example in a business
A retail chain tracks five KPIs for 500 stores: revenue, footfall, basket count, margin and shrinkage. After standardising, the eigenvalues of the correlation matrix are . The first axis (loadings about on revenue, footfall and baskets) is a "store volume" factor explaining of the variance; the second ( margin, shrinkage) is an "operational efficiency" factor explaining a further . Two axes cover of the variation, so stores can be benchmarked on a two-dimensional map instead of five separate league tables.
The formula, piece by piece
- : sample covariance matrix, , symmetric and positive semi-definite; with standardised features it is the correlation matrix (see Sample Covariance Matrix)
- : data matrix with each column's mean subtracted; without centring the first axis points at the mean, not along the spread (see Data Centering)
- : -th eigenvector, normalised to length ; its entries are the loadings of each original feature
- : -th eigenvalue; variance of the scores
- : multiplying the axis by only stretches it, never rotates it; the general linear-algebra statement is in Eigenvector Equation
- : orthogonal matrix, , and with
- : the characteristic equation whose roots are the eigenvalues
- : eigenvalues partition the total variance
- Sign ambiguity: if solves the equation, so does ; libraries may return either
The formula in words
"A principal axis is a direction that the covariance matrix only stretches, never turns; the stretch factor is the variance of the data along that direction." Tiny example: with , the direction comes back as , i.e. stretched by ; the direction comes back as , which is turned, so it is not an axis.
Critical Concepts
- Eigenvalue = variance: . A zero eigenvalue means an exact linear dependency among features.
- Orthogonality comes free: eigenvectors of a symmetric matrix with distinct eigenvalues are orthogonal, so principal components are uncorrelated by construction.
- Scale dependence: PCA on raw covariance is dominated by the column with the largest units (revenue in euros swamps margin in %). Standardise when the units differ; then is the correlation matrix and (or with mixed conventions).
- Sign and order are conventions:
np.linalg.eighreturns eigenvalues ascending; reverse them. Fix signs (for example, largest loading positive) before comparing runs or reporting loadings. - Numerics: compute via
eigh(symmetric solver) or the SVD of rather than a generaleig; the SVD avoids forming , which squares the condition number. Singular values relate by (see Singular Value Decomposition and Low-Rank Approximation (SVD)). - Repeated eigenvalues: when , any rotation within that plane is an equally valid pair of axes; the individual axes are not identifiable.
- How many to keep: cumulative explained variance (for example ), the elbow of the scree plot, or the Kaiser rule ( on standardised data).
Step-by-step calculation (tiny example)
Four observations of two features: .
Step 1: Centre Means are . Centred rows: .
Step 2: Covariance , , . Divide by : .
Step 3: Characteristic equation .
Step 4: Eigenvectors For : gives , so . For : , so (or its negative). Check: , and .
Step 5: Interpret Total variance . The first axis, along which both features rise together, explains of the variance; the second explains . Scores on axis 1 are , with sample variance .
Code examples
Beginner example (Python)
import numpy as np
# Four observations of two features (e.g. two correlated KPIs)
X = np.array([[7.0, 4.0],
[4.0, 4.0],
[4.0, 1.0],
[5.0, 3.0]])
Xc = X - X.mean(axis=0) # centre each column
S = Xc.T @ Xc / (len(X) - 1) # sample covariance
print("mean:", X.mean(axis=0))
print("S =\n", S)
# Solve S v = lambda v (eigh: for symmetric matrices, ascending order)
eigvals, eigvecs = np.linalg.eigh(S)
order = np.argsort(eigvals)[::-1] # largest variance first
eigvals, eigvecs = eigvals[order], eigvecs[:, order]
print("eigenvalues (variances):", eigvals)
print("principal axes (columns):\n", eigvecs.round(4))
# Check the defining equation for each axis
for k in range(2):
v, lam = eigvecs[:, k], eigvals[k]
print(f"PC{k + 1}: S v = {np.round(S @ v, 4)}, lambda v = {np.round(lam * v, 4)}")
# Eigenvalue = variance of the data projected on its axis
scores = Xc @ eigvecs
print("variance of scores:", scores.var(axis=0, ddof=1))
print("explained variance ratio:", eigvals / eigvals.sum())Beginner example (R)
X <- matrix(c(7, 4,
4, 4,
4, 1,
5, 3), ncol = 2, byrow = TRUE)
S <- cov(X) # centres and divides by n - 1
print(S) # 2 1 / 1 2
e <- eigen(S, symmetric = TRUE) # eigenvalues already in decreasing order
print(e$values) # 3 1
print(e$vectors) # columns are the principal axes (signs may differ)
# Check S v = lambda v for the first axis
v1 <- e$vectors[, 1]
print(S %*% v1)
print(e$values[1] * v1)
# Same eigenvalues from prcomp (sdev^2 = variances along the axes)
p <- prcomp(X, center = TRUE, scale. = FALSE)
print(p$sdev^2) # 3 1
print(e$values / sum(e$values)) # 0.75 0.25Professional example (Python)
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
rng = np.random.default_rng(42)
n = 500
# Five store KPIs driven by two latent factors (demand, efficiency) plus noise
demand = rng.normal(size=n)
efficiency = rng.normal(size=n)
X = np.column_stack([
demand + 0.3 * rng.normal(size=n), # revenue
0.9 * demand + 0.4 * rng.normal(size=n), # footfall
0.8 * demand + 0.2 * efficiency + 0.4 * rng.normal(size=n), # basket count
efficiency + 0.3 * rng.normal(size=n), # margin
-0.9 * efficiency + 0.4 * rng.normal(size=n), # shrinkage
])
names = ["revenue", "footfall", "baskets", "margin", "shrinkage"]
Z = StandardScaler().fit_transform(X) # PCA on the correlation scale
# 1. Solve S v = lambda v directly
S = np.cov(Z, rowvar=False) # ddof = 1
lam, V = np.linalg.eigh(S)
lam, V = lam[::-1], V[:, ::-1] # descending
# 2. Same answer from scikit-learn (SVD of the centred data)
pca = PCA().fit(Z)
print("eigenvalues (eigh) :", lam.round(4))
print("explained_variance_ :", pca.explained_variance_.round(4))
print("max |eigh - sklearn| :", np.abs(lam - pca.explained_variance_).max())
# Axes agree up to sign: |cosine| between matching axes = 1
cos = np.abs(np.sum(V * pca.components_.T, axis=0))
print("|cos(axis_eigh, axis_sklearn)|:", cos.round(6))
# 3. Axes are orthonormal and diagonalise S
print("V^T V = I ?", np.allclose(V.T @ V, np.eye(5)))
print("V^T S V diagonal ?", np.allclose(V.T @ S @ V, np.diag(lam)))
# 4. How many axes to keep
ratio = lam / lam.sum()
print("explained ratio :", ratio.round(3))
print("cumulative :", np.cumsum(ratio).round(3))
print("Kaiser rule (lambda > 1) keeps", int((lam > 1).sum()), "components")
# 5. Read the first two axes (loadings)
for k in range(2):
v = V[:, k] * np.sign(V[np.argmax(np.abs(V[:, k])), k]) # fix the sign
print(f"PC{k + 1}:", ", ".join(f"{nm} {w:+.2f}" for nm, w in zip(names, v)))
# 6. Power iteration: the leading axis without a full decomposition
v = rng.normal(size=5)
for _ in range(100):
v = S @ v
v /= np.linalg.norm(v)
print("power iteration lambda_1 =", round(float(v @ S @ v), 4))The direct eigen-solution and scikit-learn's SVD route agree to on the eigenvalues , and every pair of axes has , i.e. they match up to sign. The eigenvalues sum to rather than because StandardScaler divides by while np.cov divides by (). Two components explain of the variance and are the only two with ; their loadings recover the two latent factors used to generate the data (volume: revenue, footfall, baskets; efficiency: margin against shrinkage). Power iteration reaches the same using only matrix-vector products, which is how leading axes are found for very large .
Source: domain knowledge