Contents
Deep Breakdown
What it tells me (Situation Awareness): is the left pseudo-inverse (or Moore-Penrose pseudoinverse when has full column rank) of the matrix . It is a matrix that maps any vector to the least-squares solution . When is a tall matrix () with full column rank, the system is overdetermined (more equations than unknowns) and generally has no exact solution. The pseudo-inverse gives the best approximate solution: minimizes . In OLS: is the pseudo-inverse of applied to .
Flowchart
Real-world use
Where it is used
OLS regression (most common use): . Any overdetermined linear system (more observations than parameters). Computing the coordinates of a vector's projection onto a subspace. Implementing least-squares fitting in signal processing, control systems, and statistics.
Problem it solves
A regular inverse only exists for square, invertible matrices. For a tall matrix (), there is no inverse. The pseudo-inverse is the "best possible substitute": it gives the unique minimum-norm least-squares solution to .
How it solves the problem
is called the left pseudo-inverse because . It satisfies "left inverse": . But (it's the projection matrix, not the identity). So it is a one-sided inverse.
Example in a business
In OLS with observations and predictors: is 1000×5 (tall, full column rank). is 5×1000. Applying it to gives : the 5 regression coefficients. The fitted values are the projection of onto .
The formula, piece by piece
- : transpose; maps
- : Gram matrix; invertible iff has full column rank (no redundant basis vectors)
- : inverse of the Gram matrix
- : pseudo-inverse; maps
- Notation: (full column rank case); more generally, (SVD-based)
- Left inverse:
- (projection matrix, NOT the identity)
- Applied to a vector: , the coordinates of in basis
The formula in words
"The pseudo-inverse solves the unsolvable: it finds the best-fit coefficients for an overdetermined system. When you cannot find such that exactly (because there is no such ), the pseudo-inverse gives the that makes as close to as possible." Tiny example: , . . . .
Critical Concepts
- Requires full column rank: exists iff columns of are linearly independent. If not, use SVD-based pseudoinverse via
np.linalg.pinv. - Left inverse: . Not a right inverse: .
- SVD form: for general , where replaces each nonzero singular value with .
- Minimum norm: among all least-squares solutions, has the smallest .
- OLS: . Numerically, never compute explicitly; use
np.linalg.lstsqorscipy.linalg.solve. - Numerical stability: explicit inversion is ill-conditioned for near-singular . SVD-based
np.linalg.pinvis always preferred.
Step-by-step calculation (tiny example)
Step 1: Identify B and x , . Overdetermined: 3 equations, 1 unknown.
Step 2: Compute the pseudo-inverse . . .
Step 3: Apply to get the least-squares solution . . . .
Code examples
Beginner example (Python)
import numpy as np
# -- Pseudo-inverse: (B^T B)^{-1} B^T ---
B = np.array([[1.0], [2.0], [2.0]]) # tall matrix (3x1), overdetermined
x = np.array([3.0, 1.0, 2.0])
# Manual pseudo-inverse
gram = B.T @ B # 1x1
gram_inv = np.linalg.inv(gram) # 1x1
B_plus = gram_inv @ B.T # pseudo-inverse: 1x3
lambda_hat = (B_plus @ x).item() # least-squares scalar
proj = B * lambda_hat # projection vector
residual = x - proj.flatten()
print(f"B^T B = {gram[0,0]:.2f}")
print(f"Pseudo-inverse B^+: {B_plus.round(4)}")
print(f"lambda_hat = {lambda_hat:.4f}")
print(f"Projection pi_U(x) = {proj.flatten().round(4)}")
print(f"Residual = {residual.round(4)}")
print(f"B^T residual = {(B.T @ residual).round(10)}")
# Compare to NumPy pinv
B_plus_np = np.linalg.pinv(B)
print(f"\nnp.linalg.pinv(B): {B_plus_np.round(4)}")
print(f"Match: {np.allclose(B_plus, B_plus_np)}")Beginner example (R)
# -- Pseudo-inverse ---
B <- matrix(c(1, 2, 2), ncol=1)
x <- c(3, 1, 2)
gram <- t(B) %*% B
B_plus <- solve(gram) %*% t(B)
lambda <- B_plus %*% x
proj <- B %*% lambda
resid <- x - proj
cat(sprintf("lambda_hat: %.4f\n", lambda))
cat(sprintf("projection: %s\n", paste(round(proj, 4), collapse=", ")))
cat(sprintf("B^T resid: %.10f\n", t(B) %*% resid))Professional example (Python)
import numpy as np
import pandas as pd
np.random.seed(42)
# -- Business scenario: OLS coefficients via pseudo-inverse ---
# Compare pseudo-inverse methods for numerical stability.
N, K = 300, 5
X = np.column_stack([
np.ones(N),
np.random.uniform(1, 100, N),
np.random.uniform(0, 50, N),
np.random.uniform(0, 20, N),
np.random.uniform(0, 10, N),
])
true_beta = np.array([10.0, 2.5, -1.8, 0.5, 3.0])
y = X @ true_beta + np.random.randn(N) * 2.0
# Method 1: explicit pseudo-inverse (unstable if X^T X ill-conditioned)
gram = X.T @ X
B_plus_exp = np.linalg.inv(gram) @ X.T
beta_exp = B_plus_exp @ y
# Method 2: NumPy pinv (SVD-based, always stable)
B_plus_svd = np.linalg.pinv(X)
beta_svd = B_plus_svd @ y
# Method 3: lstsq (best practice)
beta_lstsq, residuals, rank, sv = np.linalg.lstsq(X, y, rcond=None)
comparison = pd.DataFrame({
"param": [f"beta_{i}" for i in range(K)],
"true": true_beta,
"explicit_inv": beta_exp.round(4),
"pinv_svd": beta_svd.round(4),
"lstsq": beta_lstsq.round(4),
})
print("Pseudo-inverse comparison for OLS:")
print(comparison.to_string(index=False))
print(f"\nCondition number of X^T X: {np.linalg.cond(gram):.2f}")
print(f"Matrix rank: {rank}")
print("\nPrefer lstsq or pinv over explicit inversion for numerical stability.")Source: MML1