Contents
Deep Breakdown
What it tells me (Situation Awareness): This is the spectral decomposition: it expresses a symmetric (or Hermitian) matrix as a weighted sum of rank-1 projection matrices, one per eigenvector. The dagger notation means conjugate transpose (for real vectors, just ). In DA/DS you use this form when you need to understand how much each principal component contributes to a covariance matrix, when you want to build a matrix with a desired spectrum (e.g., regularizing a covariance by zeroing small eigenvalues), or when you need a matrix function like or .
Flowchart
Real-world use
Where it is used
Use this form when you need to manipulate the spectrum of a matrix directly: drop small eigenvalue terms to denoise a covariance matrix, replace eigenvalues with their square roots to compute , or inspect the geometric structure of by seeing which eigenvectors carry the most weight. Also central to PCA reconstruction and covariance shrinkage in portfolio optimization.
Problem it solves
A covariance matrix estimated from limited data often has spuriously small or noisy eigenvalues that make it numerically unstable. By expressing as a sum of spectral components, you can selectively remove or shrink the noisy terms and reconstruct a cleaner, better-conditioned matrix.
How it solves the problem
Each term is a rank-1 matrix that projects onto the direction and scales by . Summing all terms exactly reconstructs . Dropping terms with small or replacing with a shrinkage target gives a modified matrix with the same eigenvectors but a better spectrum.
Example in a business
A quant analyst estimates a 20-asset covariance matrix from 60 weeks of returns. The last 18 eigenvalues are near zero (the matrix is estimated from too little data). Using the spectral expansion, they keep only the top 5 terms (5 dominant market factors), replacing the rest with a small constant . The resulting regularized covariance is positive definite, well-conditioned, and ready for portfolio optimization.
The formula, piece by piece
- : a symmetric () or Hermitian () matrix; this decomposition applies only to these types
- : sum over all eigenvalue-eigenvector pairs
- : the -th eigenvalue, a real scalar (guaranteed real for symmetric/Hermitian matrices)
- : the -th eigenvector, a unit-norm column vector ()
- : conjugate transpose of ; for real vectors this is just
- : outer product, an rank-1 matrix that projects onto the direction of Full formula:
The formula in words
"A symmetric matrix is a weighted sum of projection directions, where each weight is the eigenvalue and each direction is the corresponding unit eigenvector." Tiny numeric example: . Eigenpairs: and . Expansion: .
Critical Concepts
- Symmetric/Hermitian only: this exact form requires an orthonormal eigenbasis; general (asymmetric) matrices need the full eigendecomposition , which is different.
- Truncation gives best approximation: keeping only the top terms gives the best rank- approximation to in Frobenius norm.
- Matrix functions: you can compute for any function : square root, inverse, exponential, log.
- Covariance regularization: replace noisy small eigenvalues with a floor value before reconstruction; this is Ledoit-Wolf / eigenvalue clipping shrinkage.
- Numerical orthogonality: for nearly-repeated eigenvalues, eigenvectors may not be perfectly orthonormal numerically; always check after decomposition.
- Trace check: ; a fast sanity check on the eigenvalue sum.
Step-by-step calculation (tiny example)
Step 1: Compute eigenvalues and normalized eigenvectors . Characteristic polynomial: . Eigenvalues: , . For : . For : (null space of ).
Step 2: Form the two rank-1 outer product terms . .
Step 3: Weight by eigenvalues and sum to verify reconstruction . Since , the second term vanishes: is rank-1.
Code examples
Beginner example (Python)
import numpy as np # -- Spectral decomposition of a 3x3 symmetric matrix ----------------
A = np.array([[4.0, 2.0, 0.0], [2.0, 3.0, 1.0], [0.0, 1.0, 2.0]]) # Eigendecomposition (eigh: symmetric matrix, returns real eigenvalues)
eigenvalues, eigenvectors = np.linalg.eigh(A)
print("Eigenvalues:", eigenvalues.round(4))
print("Eigenvectors (columns):")
print(eigenvectors.round(4)) # Reconstruct A as sum of lambda_k * v_k * v_k^T
n = A.shape[0]
A_reconstructed = np.zeros((n, n))
for k in range(n):
lam_k = eigenvalues[k]
v_k = eigenvectors[:, k].reshape(-1, 1)
A_reconstructed += lam_k * (v_k @ v_k.T)
print("\nReconstructed A:")
print(A_reconstructed.round(4))
print(f"\nMax reconstruction error: {np.max(np.abs(A - A_reconstructed)):.2e}")Beginner example (R)
# -- Spectral decomposition of a 3x3 symmetric matrix ----------------
A <- matrix(c(4, 2, 0, 2, 3, 1, 0, 1, 2), nrow = 3, byrow = TRUE)
result <- eigen(A, symmetric = TRUE)
eigenvalues <- result$values
eigenvectors <- result$vectors
cat("Eigenvalues:\n")
print(round(eigenvalues, 4)) # Reconstruct
A = sum of lambda_k * v_k * v_k^T
n <- nrow(A)
A_hat <- matrix(0, n, n)
for (k in seq_len(n)) { v_k <- eigenvectors[, k, drop = FALSE]
A_hat <- A_hat + eigenvalues[k] * (v_k %*% t(v_k))
}
cat("\nReconstructed A:\n")
print(round(A_hat, 4))
cat(sprintf("\nMax reconstruction error: %.2e\n", max(abs(A - A_hat))))Professional example (Python)
import numpy as np
import pandas as pd
np.random.seed(42) # -- Business scenario: regularize a noisy estimated covariance matrix --
N_ASSETS = 10
N_PERIODS = 40 # short history: covariance will be noisy
N_KEEP = 4 # retain top 4 spectral components (market factors)
FLOOR_VALUE = 0.01 # replace small eigenvalues with this floor
asset_names = [f"asset_{i+1:02d}" for i in range(N_ASSETS)] # Simulate returns from a low-rank covariance (3 true factors)
true_factors = np.random.normal(size=(N_PERIODS, 3))
loadings = np.random.normal(size=(N_ASSETS, 3))
returns_raw = true_factors @ loadings.T + np.random.normal(0, 0.5, (N_PERIODS, N_ASSETS))
df_returns = pd.DataFrame(returns_raw, columns=asset_names) # Estimate covariance (will have many small/noisy eigenvalues)
cov_estimated = df_returns.cov().to_numpy() # Eigendecomposition
eigenvalues, eigenvectors = np.linalg.eigh(cov_estimated)
order = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[order]
eigenvectors = eigenvectors[:, order] # Regularize: keep top N_KEEP eigenvalues, floor the rest
eigenvalues_reg = np.where( np.arange(N_ASSETS) < N_KEEP, eigenvalues, np.maximum(eigenvalues, FLOOR_VALUE) ) # Reconstruct regularized covariance via spectral expansion
cov_regularized = np.zeros((N_ASSETS, N_ASSETS))
for k in range(N_ASSETS):
v_k = eigenvectors[:, k].reshape(-1, 1)
cov_regularized += eigenvalues_reg[k] * (v_k @ v_k.T)
cond_original = np.linalg.cond(cov_estimated)
cond_regularized = np.linalg.cond(cov_regularized)
print(f"Condition number (original): {cond_original:.1f}")
print(f"Condition number (regularized): {cond_regularized:.1f}")
print(f"\nTop-{N_KEEP} eigenvalues: {eigenvalues[:N_KEEP].round(4)}")
print(f"Min eigenvalue (original): {eigenvalues.min():.6f}")
print(f"Min eigenvalue (regularized): {eigenvalues_reg.min():.6f}")
print("\nInterpretation: spectral regularization reduces condition number, making Cholesky and portfolio optimization stable.")Source: MYLA p. 7