BRAGA

02 Writing02-02 Notes02-02-201

Projection matrix onto orthonormal basis B

Published 16 Apr 20267 min read
Contents
  1. 01Deep Breakdown
  2. 02Flowchart
  3. 03Real-world use
  4. 04The formula, piece by piece
  5. 05The formula in words
  6. 06Critical Concepts
  7. 07Step-by-step calculation (tiny example)
  8. 08Code examples
Pπ=BBTP_\pi = B B^T
(1)

Deep Breakdown

What it tells me (Situation Awareness): When the columns of BB form an orthonormal basis for the subspace UU (i.e., BTB=IkB^TB = I_k), the projection matrix simplifies from B(BTB)−1BTB(B^TB)^{-1}B^T to BBTBB^T. This is a massive computational shortcut: no matrix inversion needed. The condition BTB=IB^TB = I 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-kk components uses Pπ=VVTP_\pi = VV^T where VV is the matrix of kk eigenvectors. Orthonormal bases enable fast, numerically stable projections.

Flowchart

No (not orthonormal)Yes (orthonormal)Basis matrix B:does B^T B = I ?Use full formula:P = B(B^TB)^{-1} B^TSimplification:P_pi = B B^TNo matrixinversionneededProjection:pi_U(x) = B B^Tx= B (B^T x) =sum of outerproducts (b_ib_i^T) * xEach term:project x ontoone basisvector, then sum

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 x→BTxx \to B^Tx, decoder maps back via BzBz), and QR decomposition: after np.linalg.qr(X), the QQ matrix has orthonormal columns and P=QQTP = QQ^T.

Problem it solves

The general projection formula B(BTB)−1BTB(B^TB)^{-1}B^T requires computing and inverting the k×kk \times k Gram matrix, which can be numerically unstable when columns are near-parallel. When BB is orthonormal, BTB=IB^TB = I exactly, so the inversion is trivial and the formula collapses to BBTBB^T.

How it solves the problem

Substituting BTB=IkB^TB = I_k into P=B(BTB)−1BTP = B(B^TB)^{-1}B^T: P=B⋅Ik−1⋅BT=B⋅Ik⋅BT=BBTP = B \cdot I_k^{-1} \cdot B^T = B \cdot I_k \cdot B^T = BB^T. Also: BBT=∑i=1kbibiTBB^T = \sum_{i=1}^k b_i b_i^T (sum of kk rank-1 outer products, one per basis vector). The projection decomposes as: project xx 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 V3V_3 (matrix with 3 orthonormal columns). To reconstruct the approximate data in the original space: X^=XV3V3T=XPπ\hat{X} = X V_3 V_3^T = X P_\pi. Since V3TV3=I3V_3^T V_3 = I_3, the projection matrix is V3V3TV_3 V_3^T — no inversion required.

The formula, piece by piece

  • BB: n×kn \times k matrix with orthonormal columns: BTB=IkB^TB = I_k; each column is a unit vector, all pairs orthogonal
  • BTB^T: k×nk \times n transpose; BTxB^T x computes the coordinate of xx in the basis BB
  • BBTBB^T: n×nn \times n projection matrix; symmetric, idempotent, rank kk
  • Special case of the general formula: B(BTB)−1BTB(B^TB)^{-1}B^T with BTB=IkB^TB = I_k
  • BBTx=B(BTx)BB^T x = B(B^Tx): first project xx onto each basis vector (via BTxB^Tx), then map back to Rn\mathbb{R}^n (via BB)
  • Outer product decomposition: BBT=∑i=1kbibiTBB^T = \sum_{i=1}^k b_i b_i^T

The formula in words

"With an orthonormal basis, projection is just: find the coordinates (how much of xx lies along each basis direction), then reconstruct using those coordinates and the basis vectors. No need to invert anything." Tiny example: B=12(11)B = \frac{1}{\sqrt{2}}\begin{pmatrix}1\\1\end{pmatrix}, x=(3,1)Tx = (3,1)^T. BTB=12(1+1)=1B^TB = \frac{1}{2}(1+1) = 1. ✓\checkmark (orthonormal) BBT=12(1111)BB^T = \frac{1}{2}\begin{pmatrix}1&1\\1&1\end{pmatrix}. πU(x)=12(1111)(31)=12(44)=(22)\pi_U(x) = \frac{1}{2}\begin{pmatrix}1&1\\1&1\end{pmatrix}\begin{pmatrix}3\\1\end{pmatrix} = \frac{1}{2}\begin{pmatrix}4\\4\end{pmatrix} = \begin{pmatrix}2\\2\end{pmatrix}.

Critical Concepts

  • Requirement: BTB=IkB^TB = I_k must hold exactly. If it does not, use the general formula B(BTB)−1BTB(B^TB)^{-1}B^T.
  • Idempotent: (BBT)2=BBTBBT=B(BTB)BT=BIkBT=BBT(BB^T)^2 = BB^TBB^T = B(B^TB)B^T = BI_kB^T = BB^T. ✓\checkmark
  • Symmetric: (BBT)T=(BT)TBT=BBT(BB^T)^T = (B^T)^T B^T = BB^T. ✓\checkmark
  • PCA reconstruction: X^=XVkVkT\hat{X} = XV_kV_k^T. The approximation error is ∥X−X^∥F2=∑i>kσi2\|X - \hat{X}\|_F^2 = \sum_{i>k}\sigma_i^2 (sum of discarded singular values squared).
  • QR decomposition: Q, R = np.linalg.qr(X, mode='reduced'). QQ has orthonormal columns, P=QQTP = QQ^T.
  • Complement: I−BBTI - BB^T projects onto U⊥U^\perp; 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 B=(1/21/21/2−1/2)B = \begin{pmatrix}1/\sqrt{2} & 1/\sqrt{2}\\ 1/\sqrt{2} & -1/\sqrt{2}\end{pmatrix} (2D rotation by 45°). BTB=(1001)=I2B^T B = \begin{pmatrix}1 & 0\\ 0 & 1\end{pmatrix} = I_2. ✓\checkmark Orthonormal.

Step 2: Compute the projection matrix Since BTB=IB^TB = I, Pπ=BBT=(1/21/2)(1/2,1/2)+(1/2−1/2)(1/2,−1/2)P_\pi = BB^T = \begin{pmatrix}1/\sqrt{2}\\1/\sqrt{2}\end{pmatrix}(1/\sqrt{2}, 1/\sqrt{2}) + \begin{pmatrix}1/\sqrt{2}\\-1/\sqrt{2}\end{pmatrix}(1/\sqrt{2},-1/\sqrt{2}). =12(1111)+12(1−1−11)=(1001)=I= \frac{1}{2}\begin{pmatrix}1&1\\1&1\end{pmatrix} + \frac{1}{2}\begin{pmatrix}1&-1\\-1&1\end{pmatrix} = \begin{pmatrix}1&0\\0&1\end{pmatrix} = I. This makes sense: BB spans all of R2\mathbb{R}^2, so projection onto the full space is the identity.

Step 3: Projection onto first column only Use only first column: b1=(1/2,1/2)Tb_1 = (1/\sqrt{2}, 1/\sqrt{2})^T. P=b1b1T=12(1111)P = b_1 b_1^T = \frac{1}{2}\begin{pmatrix}1&1\\1&1\end{pmatrix}. x=(3,1)Tx = (3, 1)^T: πU(x)=(2,2)T\pi_U(x) = (2, 2)^T. Residual (1,−1)T(1,-1)^T; b1T(1,−1)=0b_1^T(1,-1) = 0. ✓\checkmark

Code examples

Beginner example (Python)

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)

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)

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

#linear-algebra#spatial-distance-norms-and-analytic-geometry#orthogonal-projections

Keyboard shortcuts

Single keys, ignored while typing in a field or with Cmd, Ctrl or Alt held.

0
Home
1 2 3 4
Work, Writing, Tools, About
j / k
Next / previous section on this page
n / p
Next / previous project or essay
u
Up one level
l
Deutsch
t
Light or dark theme
?
This list
Esc
Close