BRAGA

02 Writing02-02 Notes02-02-199

Normal Equation for general subspace U with 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
BTBλ=BTxB^T B \lambda = B^T x
(1)

Deep Breakdown

What it tells me (Situation Awareness): The normal equation BTBλ=BTxB^TB\lambda = B^Tx is the linear system whose solution gives the coordinates λ\lambda of the orthogonal projection of xx onto the subspace spanned by the columns of BB. Solving it for λ^\hat{\lambda} and then computing Bλ^B\hat{\lambda} gives πU(x)\pi_U(x), the closest point in the subspace to xx. It arises directly from the optimality condition: the residual x−Bλx - B\lambda must be orthogonal to every column of BB, which gives BT(x−Bλ)=0B^T(x - B\lambda) = 0, i.e., BTBλ=BTxB^TB\lambda = B^Tx. In DA/DS: this is the OLS normal equation XTXβ^=XTyX^TX\hat{\beta} = X^Ty, which every linear regression solves (either directly or via numerical methods).

Flowchart

Yes (B full column rank)No (rank deficient)Minimize||B*lambda -x||^2 overlambdaTake gradientand set to zerod/d_lambda||B*lambda -x||^2 = 2 B^T(B*lambda - x) =0Normalequation: B^T B* lambda = B^TxIs B^T Binvertible?Unique solution:lambda = (B^TB)^{-1} B^T xInfinitely manysolutions; usepseudoinverseor regularizationProjection:pi_U(x) = B *lambda_hat

Real-world use

Where it is used

OLS linear regression: XTXβ^=XTyX^TX\hat{\beta} = X^Ty gives the least squares coefficients. Ridge regression: (XTX+λI)β^=XTy(X^TX + \lambda I)\hat{\beta} = X^Ty (regularized normal equation). Any least-squares problem: fitting a polynomial, exponential, or any linear model. Solving projection problems numerically (preferred over explicitly computing (BTB)−1(B^TB)^{-1}).

Problem it solves

Given basis BB and target xx, find the coordinates λ\lambda such that BλB\lambda is closest to xx. This requires minimizing ∥Bλ−x∥2\|B\lambda - x\|^2, which leads to the normal equation. It translates a geometric problem (closest point in a subspace) into a solvable linear system.

How it solves the problem

Expand: ∥Bλ−x∥2=λTBTBλ−2λTBTx+xTx\|B\lambda - x\|^2 = \lambda^T B^TB\lambda - 2\lambda^T B^Tx + x^Tx. This is quadratic in λ\lambda. Setting the gradient to zero: 2BTBλ−2BTx=0⇒BTBλ=BTx2B^TB\lambda - 2B^Tx = 0 \Rightarrow B^TB\lambda = B^Tx. If BB has full column rank, BTBB^TB is positive definite and the system has a unique solution λ^=(BTB)−1BTx\hat{\lambda} = (B^TB)^{-1}B^Tx.

Example in a business

In OLS regression with design matrix XX (n×k) and response yy (n×1): the normal equation XTXβ^=XTyX^TX\hat{\beta} = X^Ty gives β^\hat{\beta} directly. Numerically, this is solved via np.linalg.solve(X.T @ X, X.T @ y) (more stable than explicitly inverting XTXX^TX) or via np.linalg.lstsq (which uses SVD and handles rank-deficient cases).

The formula, piece by piece

  • BTB^T: k×nk \times n transpose of the basis matrix; "aggregates" information from all nn dimensions into kk equations
  • BTBB^T B: k×kk \times k Gram matrix; measures pairwise inner products between basis vectors; invertible iff BB has full column rank
  • λ\lambda: k×1k \times 1 vector of unknown coordinates (coefficients in the basis)
  • BTxB^T x: k×1k \times 1 vector; the right-hand side; projects xx onto each basis direction
  • BTBλ=BTxB^T B \lambda = B^T x: a k×kk \times k linear system; the solution minimizes ∥Bλ−x∥2\|B\lambda - x\|^2
  • Solution: λ^=(BTB)−1BTx\hat{\lambda} = (B^TB)^{-1}B^Tx (when BTBB^TB is invertible)
  • OLS form: XTXβ^=XTyX^TX\hat{\beta} = X^Ty

The formula in words

"Take each basis vector and require that the residual is orthogonal to it. This gives one equation per basis vector. Solving the resulting k×kk \times k system gives the coordinates of the closest point." Tiny example: B=(100111)B = \begin{pmatrix}1 & 0\\0 & 1\\1 & 1\end{pmatrix}, x=(2,3,4)Tx = (2, 3, 4)^T. BTB=(2112)B^T B = \begin{pmatrix}2 & 1\\1 & 2\end{pmatrix}. BTx=(2+4,3+4)T=(6,7)TB^T x = (2+4, 3+4)^T = (6, 7)^T. Normal equation: (2112)λ=(67)\begin{pmatrix}2&1\\1&2\end{pmatrix}\lambda = \begin{pmatrix}6\\7\end{pmatrix}. Solution: λ^=13(2⋅6−7,2⋅7−6)T=(5/3,8/3)T\hat{\lambda} = \frac{1}{3}(2\cdot6-7, 2\cdot7-6)^T = (5/3, 8/3)^T. πU(x)=Bλ^=(5/3,8/3,13/3)T\pi_U(x) = B\hat{\lambda} = (5/3, 8/3, 13/3)^T.

Critical Concepts

  • Preferred over explicit inversion: solve np.linalg.solve(B.T @ B, B.T @ x) rather than np.linalg.inv(B.T @ B) @ B.T @ x. Numerically more stable.
  • Best practice: use np.linalg.lstsq(B, x) which uses SVD and handles rank-deficient cases.
  • Ridge modification: (BTB+αI)λ^=BTx(B^TB + \alpha I)\hat{\lambda} = B^Tx adds α\alpha to each diagonal of the Gram matrix, ensuring invertibility and shrinking the solution.
  • Rank deficiency: if BTBB^TB is singular (linearly dependent columns), the normal equation has infinitely many solutions; use the minimum-norm solution via pseudoinverse.
  • Numerical conditioning: BTBB^TB can be ill-conditioned when columns of BB are nearly parallel. QR decomposition or SVD are more numerically stable alternatives.
  • OLS interpretation: XTXX^TX = "feature cross-products," XTyX^Ty = "feature-response cross-products." The normal equation balances these to find β^\hat{\beta}.

Step-by-step calculation (tiny example)

Step 1: Set up the normal equation B=(21)B = \begin{pmatrix}2\\1\end{pmatrix}, x=(3,4)Tx = (3, 4)^T. BTB=4+1=5B^TB = 4+1 = 5. BTx=6+4=10B^Tx = 6+4 = 10. Normal equation: 5λ^=10⇒λ^=25\hat{\lambda} = 10 \Rightarrow \hat{\lambda} = 2.

Step 2: Compute the projection πU(x)=Bλ^=2⋅(2,1)T=(4,2)T\pi_U(x) = B\hat{\lambda} = 2\cdot(2,1)^T = (4, 2)^T.

Step 3: Verify residual orthogonality r=x−πU(x)=(3,4)−(4,2)=(−1,2)r = x - \pi_U(x) = (3,4)-(4,2) = (-1, 2). BTr=2(−1)+1(2)=0B^Tr = 2(-1) + 1(2) = 0. ✓\checkmark The residual is orthogonal to BB.

Code examples

Beginner example (Python)

python
import numpy as np   # -- Solve normal equation B^T B lambda = B^T x ---
B = np.array([[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]])
x = np.array([2.0, 3.0, 4.0])
gram_matrix = B.T @ B   # B^T B (2x2)
rhs = B.T @ x   # B^T x (2,) # Solve (preferred: avoid explicit inversion)
lambda_hat = np.linalg.solve(gram_matrix, rhs)
projection = B @ lambda_hat
residual = x - projection
print(f"B^T B:\n{gram_matrix}")
print(f"B^T x: {rhs}")
print(f"\nlambda_hat (solution): {lambda_hat.round(4)}")
print(f"Projection pi_U(x): {projection.round(4)}")
print(f"Residual: {residual.round(4)}")
print(f"B^T residual (check): {(B.T @ residual).round(10)}")   # Alternative: lstsq handles rank-deficient cases
lambda_lstsq, _, _, _ = np.linalg.lstsq(B, x, rcond=None)
print(f"\nlstsq solution: {lambda_lstsq.round(4)} (matches: {np.allclose(lambda_hat, lambda_lstsq)})")

Beginner example (R)

r
# -- Normal equation B^T B
lambda = B^T x ---
B <- matrix(c(1,0,1, 0,1,1), nrow=3, ncol=2)
x <- c(2, 3, 4)
gram <- t(B) %*% B
rhs <- t(B) %*% x
lambda <- solve(gram, rhs)
proj <- B %*% lambda
resid <- x - proj
cat("lambda_hat:", round(lambda, 4), "\n")
cat("projection:", round(proj, 4), "\n")
cat("B^T resid: ", round(t(B) %*% resid, 10), "\n")

Professional example (Python)

python
import numpy as np
import pandas as pd
np.random.seed(42)   # -- Business scenario: solve OLS normal equations, compare methods ---
# X^T X beta = X^T y (normal equation for linear regression)
N, K = 200, 4
X = np.column_stack([ np.ones(N), np.random.uniform(0, 10, N), np.random.uniform(0, 5, N), np.random.uniform(0, 3, N),
])
true_beta = np.array([1.5, 0.8, -0.5, 0.3])
y = X @ true_beta + np.random.randn(N) * 0.3
gram = X.T @ X   # X^T X (normal equation left side)
rhs = X.T @ y   # X^T y (normal equation right side) # Method 1: solve the normal equation directly
beta_solve = np.linalg.solve(gram, rhs)   # Method 2: via explicit inversion (less stable)
beta_inv = np.linalg.inv(gram) @ rhs   # Method 3: lstsq (uses SVD, most numerically stable)
beta_lstsq, _, _, _ = np.linalg.lstsq(X, y, rcond=None)
comparison = pd.DataFrame({ "parameter": [f"beta_{i}" for i in range(K)], "true_beta": true_beta, "solve": beta_solve.round(4), "inv": beta_inv.round(4), "lstsq": beta_lstsq.round(4),
})
print("OLS via normal equation (B^T B lambda = B^T x with B=X, x=y):")
print(comparison.to_string(index=False))
print(f"\nCondition number of X^T X: {np.linalg.cond(gram):.2f}")
print("Recommendation: use lstsq or solve, NOT explicit inversion.")

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