Contents
Deep Breakdown
What it tells me (Situation Awareness): When the columns of form an orthonormal basis for the subspace (i.e., ), the projection matrix simplifies from to . This is a massive computational shortcut: no matrix inversion needed. The condition means every column has unit length and columns are mutually orthogonal. In DA/DS: PCA components are orthonormal by construction, so projecting onto the top- components uses where is the matrix of eigenvectors. Orthonormal bases enable fast, numerically stable projections.
Flowchart
Real-world use
Where it is used
Projecting data onto PCA components (orthonormal by construction), computing projections onto eigenvectors or singular vectors (which are orthonormal from SVD), fast reconstruction in autoencoders (encoder maps , decoder maps back via ), and QR decomposition: after np.linalg.qr(X), the matrix has orthonormal columns and .
Problem it solves
The general projection formula requires computing and inverting the Gram matrix, which can be numerically unstable when columns are near-parallel. When is orthonormal, exactly, so the inversion is trivial and the formula collapses to .
How it solves the problem
Substituting into : . Also: (sum of rank-1 outer products, one per basis vector). The projection decomposes as: project onto each basis vector independently and sum.
Example in a business
A DS computes PCA on a customer dataset and keeps the top 3 components (matrix with 3 orthonormal columns). To reconstruct the approximate data in the original space: . Since , the projection matrix is — no inversion required.
The formula, piece by piece
- : matrix with orthonormal columns: ; each column is a unit vector, all pairs orthogonal
- : transpose; computes the coordinate of in the basis
- : projection matrix; symmetric, idempotent, rank
- Special case of the general formula: with
- : first project onto each basis vector (via ), then map back to (via )
- Outer product decomposition:
The formula in words
"With an orthonormal basis, projection is just: find the coordinates (how much of lies along each basis direction), then reconstruct using those coordinates and the basis vectors. No need to invert anything." Tiny example: , . . (orthonormal) . .
Critical Concepts
- Requirement: must hold exactly. If it does not, use the general formula .
- Idempotent: .
- Symmetric: .
- PCA reconstruction: . The approximation error is (sum of discarded singular values squared).
- QR decomposition:
Q, R = np.linalg.qr(X, mode='reduced'). has orthonormal columns, . - Complement: projects onto ; also symmetric and idempotent.
- Gram-Schmidt: converts any linearly independent set to an orthonormal basis, enabling the simplified formula.
Step-by-step calculation (tiny example)
Step 1: Verify orthonormality of columns (2D rotation by 45°). . Orthonormal.
Step 2: Compute the projection matrix Since , . . This makes sense: spans all of , so projection onto the full space is the identity.
Step 3: Projection onto first column only Use only first column: . . : . Residual ; .
Code examples
Beginner example (Python)
import numpy as np # -- Orthonormal projection matrix P = B B^T ---
# Use two orthonormal vectors as basis
b1 = np.array([1.0, 0.0, 0.0]) # e1
b2 = np.array([0.0, 1.0, 0.0]) # e2
B = np.column_stack([b1, b2]) # span the xy-plane # Verify orthonormality
print(f"B^T B = \n{B.T @ B}") # should be I_2 # Projection matrix
P = B @ B.T
print(f"\nP = B B^T =\n{P}")
print(f"Idempotent: {np.allclose(P @ P, P)}")
print(f"Symmetric: {np.allclose(P, P.T)}") # Project a 3D vector onto xy-plane
x = np.array([3.0, 2.0, 5.0])
proj = P @ x
resid = x - proj
print(f"\nx = {x}")
print(f"pi_U = {proj} (xy-plane shadow)")
print(f"resid = {resid} (z component only)")
print(f"B^T resid = {B.T @ resid}")Beginner example (R)
# -- Orthonormal projection
P = B B^T ---
B <- cbind(c(1,0,0), c(0,1,0)) # xy-plane basis
cat("B^T B:\n"); print(t(B) %*% B)
P <- B %*% t(B)
x <- c(3, 2, 5)
proj <- P %*% x
cat(sprintf("pi_U(x) = (%g, %g, %g)\n", proj[1], proj[2], proj[3]))
cat(sprintf("Idempotent: %s\n", isTRUE(all.equal(P %*% P, P))))Professional example (Python)
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
np.random.seed(42) # -- Business scenario: PCA reconstruction using P = V_k V_k^T ---
# Project data onto top-k PCA directions and measure reconstruction quality.
N, D = 300, 8
raw_data = np.random.randn(N, D)
raw_data[:, 1] += 0.9 * raw_data[:, 0]
raw_data[:, 2] += 0.7 * raw_data[:, 0]
scaler = StandardScaler()
X = scaler.fit_transform(raw_data) # SVD to get orthonormal principal components
U, S, Vt = np.linalg.svd(X, full_matrices=False)
results = []
for k in [1, 2, 3, 4]:
V_k = Vt[:k].T # shape (D, k): orthonormal columns # Projection matrix: P = V_k V_k^T (since V_k^T V_k = I_k)
P = V_k @ V_k.T
X_hat = X @ P # reconstruction in original space
error = np.linalg.norm(X - X_hat, 'fro')**2
total_ss = np.linalg.norm(X, 'fro')**2
results.append({ "k_components": k, "reconstruction_error": round(error, 3), "pct_variance_kept": round(1 - error / total_ss, 4), "P_idempotent": np.allclose(P @ P, P), })
df = pd.DataFrame(results)
print("PCA reconstruction via P = V_k V_k^T (orthonormal basis):")
print(df.to_string(index=False))
print("\nNote: P = V_k V_k^T works only because V_k^T V_k = I (orthonormal).")Source: MML1