Skip to content

Orthogonal Projections

An orthogonal projection finds the point in a subspace that is closest to your vector — its “shadow.” This one operation is the workhorse of applied linear algebra: PCA projects data onto a low-dimensional subspace, least-squares regression projects targets onto the column space of the features, and lossy compression projects signals onto a small basis. Master projections and you understand the geometry of half of machine learning.

A real-life example: the shadow on the floor

Hold a pencil above the floor and shine a light straight down. The shadow is the pencil’s orthogonal projection onto the floor (a 2-D subspace). Two things are always true: the shadow lies in the floor, and the line from the pencil tip to its shadow is perpendicular to the floor. Those two properties define the projection — and they’re what make it the closest point.

Projection onto a line (1-D)

To project x\mathbf{x} onto the line UU spanned by a vector b\mathbf{b}, we seek the multiple of b\mathbf{b} closest to x\mathbf{x}. “Closest” means the error xπU(x)\mathbf{x} - \pi_U(\mathbf{x}) is orthogonal to b\mathbf{b}. Solving that condition gives:

λ=bxbb,πU(x)=λb=bxb2b,Pπ=bbb2.\lambda = \frac{\mathbf{b}^\top\mathbf{x}}{\mathbf{b}^\top\mathbf{b}}, \qquad \pi_U(\mathbf{x}) = \lambda\,\mathbf{b} = \frac{\mathbf{b}^\top\mathbf{x}}{\lVert\mathbf{b}\rVert^2}\,\mathbf{b}, \qquad P_\pi = \frac{\mathbf{b}\,\mathbf{b}^\top}{\lVert\mathbf{b}\rVert^2}.

The last object PπP_\pi is the projection matrix: multiply any vector by it and you get its projection onto the line. Projection matrices are symmetric and idempotent: Pπ2=PπP_\pi^2 = P_\pi (projecting twice does nothing new — the shadow of a shadow is itself).

See the shadow

The white vector x\mathbf{x} orbits. Its projection πU(x)\pi_U(\mathbf{x}) (green) slides along the line UU, always the closest point; the red segment is the error xπU(x)\mathbf{x} - \pi_U(\mathbf{x}), always perpendicular to UU:

sketch Orthogonal projection onto a line p5.js
The white vector x is projected onto the line U (amber). The green vector is the projection π_U(x) — the closest point on the line — and the red segment is the error, always perpendicular to U.

The error is always perpendicular — that’s the defining property, and it’s why the projection is the least-error approximation of x\mathbf{x} inside UU.

Projection onto a general subspace

For a subspace UU with basis vectors as columns of BB, the same “error is orthogonal to the subspace” condition gives the normal equation BBλ=BxB^\top B\,\boldsymbol\lambda = B^\top\mathbf{x}, hence:

λ=(BB)1Bx,πU(x)=B(BB)1Bx,Pπ=B(BB)1B.\boldsymbol\lambda = (B^\top B)^{-1}B^\top\mathbf{x}, \qquad \pi_U(\mathbf{x}) = B(B^\top B)^{-1}B^\top\mathbf{x}, \qquad P_\pi = B(B^\top B)^{-1}B^\top.

The matrix (BB)1B(B^\top B)^{-1}B^\top is the pseudo-inverse of BB — the exact object behind least-squares regression. If the basis is orthonormal (BB=IB^\top B = I), this collapses to the cheap form πU(x)=BBx\pi_U(\mathbf{x}) = B B^\top\mathbf{x} — no inverse needed (the ONB payoff again).

diagram Diagram mermaid

NumPy

projections.py
import numpy as np
 
# ---- 1-D: project onto a line spanned by b ----
b = np.array([1.0, 2.0, 2.0])
x = np.array([1.0, 1.0, 1.0])
P_line = np.outer(b, b) / (b @ b)          # projection matrix bbᵀ / bᵀb
proj = P_line @ x
print("projection onto line:", np.round(proj, 4))
print("idempotent P² = P:", np.allclose(P_line @ P_line, P_line))
print("error ⟂ b:", np.isclose((x - proj) @ b, 0))
 
# ---- general subspace: project onto column space of B ----
B = np.array([[1.0, 0.0],
              [1.0, 1.0],
              [1.0, 2.0]])
P = B @ np.linalg.inv(B.T @ B) @ B.T        # B(BᵀB)⁻¹Bᵀ
y = np.array([6.0, 0.0, 0.0])
print("projection onto plane:", np.round(P @ y, 4))
projections.py
import numpy as np
 
# ---- 1-D: project onto a line spanned by b ----
b = np.array([1.0, 2.0, 2.0])
x = np.array([1.0, 1.0, 1.0])
P_line = np.outer(b, b) / (b @ b)          # projection matrix bbᵀ / bᵀb
proj = P_line @ x
print("projection onto line:", np.round(proj, 4))
print("idempotent P² = P:", np.allclose(P_line @ P_line, P_line))
print("error ⟂ b:", np.isclose((x - proj) @ b, 0))
 
# ---- general subspace: project onto column space of B ----
B = np.array([[1.0, 0.0],
              [1.0, 1.0],
              [1.0, 2.0]])
P = B @ np.linalg.inv(B.T @ B) @ B.T        # B(BᵀB)⁻¹Bᵀ
y = np.array([6.0, 0.0, 0.0])
print("projection onto plane:", np.round(P @ y, 4))
text
projection onto line: [0.5556 1.1111 1.1111]
idempotent P² = P: True
error ⟂ b: True
projection onto plane: [ 5.  2. -1.]
text
projection onto line: [0.5556 1.1111 1.1111]
idempotent P² = P: True
error ⟂ b: True
projection onto plane: [ 5.  2. -1.]

Why this matters for ML

  • Linear regression is a projection: it projects the target vector onto the column space of the design matrix; the residual is the perpendicular error.
  • PCA projects data onto the subspace spanned by the top principal components; the projection error is the reconstruction loss you minimize.
  • Lossy compression keeps a few orthogonal-basis coefficients — a projection onto a small subspace.

🧪 Try It Yourself

Exercise 1 – Project onto a line

Exercise 2 – The error is perpendicular

Exercise 3 – Projection is idempotent

Recap

  • An orthogonal projection is the closest point in a subspace; the error is perpendicular to the subspace.
  • 1-D: Pπ=bb/b2P_\pi = \mathbf{b}\mathbf{b}^\top/\lVert\mathbf{b}\rVert^2. General: Pπ=B(BB)1BP_\pi = B(B^\top B)^{-1}B^\top (the pseudo-inverse); ONB simplifies to BBBB^\top.
  • Projection matrices are symmetric and idempotent (P2=PP^2 = P).
  • Projections power regression, PCA, and compression.

Next: transformations that move data without distorting it — rotations.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did