Eigenvalues and SVD: Calculate, Reconstruct, and Approximate
Decomposing a matrix into its action along a few directions reveals how it stretches vectors, which information the transformation loses, and how much error remains when fewer directions are kept. The linear algebra overview introduces vectors, rank, orthogonal projection, and least squares. Start here with a 2×2 eigenvalue calculation, then use a 3×2 matrix to work through SVD, reconstruction, and low-rank approximation. All matrices are real, vectors are columns, and denotes transpose.
Eigenvectors stay on an invariant line
For a square matrix , a nonzero vector satisfying is an eigenvector, and is its eigenvalue. MIT 18.06SC's eigenvalue notes begin with this relationship: the line through is invariant under the transformation. A positive preserves orientation, a negative reverses it, and maps the vector to zero. The zero vector is excluded because it would satisfy the equation for every .
Take
The equation has a nonzero solution only if is singular. First solve the characteristic equation:
This gives and . Find the corresponding null spaces:
- For , , so the coordinates satisfy .
- For , , so the coordinates satisfy .
Normalize the vectors to obtain
Direct multiplication checks and . Every therefore satisfies : the first coordinate is stretched by three, while the second keeps its length.
A real matrix admits with real exactly when it has linearly independent real eigenvectors. A real matrix can have complex eigenvalues; the invariant-line description above applies to real eigenvectors. For example, has the repeated eigenvalue 1, but the null space of is only the line spanned by . Its eigenvectors cannot form a two-dimensional basis.
Symmetry gives an orthogonal decomposition
The spectral theorem in MIT's symmetric-matrix notes states that a real symmetric matrix has real eigenvalues and a complete orthonormal eigenbasis. For distinct eigenvalues , orthogonality follows directly:
For a repeated eigenvalue, arbitrary vectors in the same eigenspace need not be orthogonal, but an orthonormal basis can be chosen within it. With , we have and . Our example becomes
Each term on the right projects onto one axis and scales by its eigenvalue. Symmetry does not require positive eigenvalues; both happen to be positive in this example.
Rectangular SVD and factor shapes
A 3×2 matrix maps two-dimensional inputs to three-dimensional outputs, so cannot directly compare its input and output. MIT's SVD notes establish that every real matrix has an SVD, using two sets of orthonormal directions to connect the spaces:
The right singular vector lies in the input space, and the left singular vector lies in the output space. Singular values are ordered , where . Multiplication by extracts input coordinates, stretches them, and maps them into output coordinates.
In the full form, are square orthogonal matrices. In the other two forms, their columns are orthonormal, but the matrices may be rectangular. Economy SVD can still contain zero singular values: is not necessarily the rank .
The SciPy linear algebra tutorial names the returned objects U, s, Vh. Here s is a one-dimensional singular-value array, and Vh is for real matrices, rather than . The scipy.linalg.svd reference specifies the economy shapes obtained with full_matrices=False.
Reconstructing a matrix from two singular components
Take
Because , we can reuse the eigenvectors: and , with eigenvalues 12 and 4. Thus
For each nonzero singular value, compute :
These vectors have unit length and . Setting , , and gives an economy SVD with shapes . For the full form, add and a bottom row of zeros to ; .
Expand the product into outer products. Each nonzero component has rank 1:
Changing the signs of both leaves their outer product unchanged. For a real symmetric matrix, singular values are the absolute values of eigenvalues; negative signs are carried by the relative orientations of the left and right singular vectors. Singular values equal the eigenvalues themselves only in the positive semidefinite case.
Truncation and approximation error
For , keep only the largest components:
Section 5 of Stanford CS168's low-rank approximation notes gives the optimality result: among matrices of rank at most , minimizes Frobenius error. The norm is defined by . Distinct outer products are orthogonal in this inner product because
The squared residual norm is therefore the sum of the discarded squared singular values:
The matrix -norm measures the greatest length amplification of a unit input vector. The residual attains the stated value along . If , reconstruction error is zero.
Keeping the first component of our example gives
Hence , , and relative Frobenius error is . The retained fraction of squared norm is . Retaining 75% of squared norm does not mean 25% relative norm error: the discarded squared error is 25%, and taking its square root gives the relative norm error.
PCA turns squared singular values into variance
The scikit-learn PCA documentation describes orthogonal components that explain the most variance in centered data. Centering does not automatically scale features to the same magnitude.
Let each row of be an observation, with column means subtracted and . Using the sample covariance convention with denominator , as in the PCA API's explained variance, substitute the full SVD :
Thus is a principal direction, with variance . For the first directions, scores are and reconstruction is . Add the mean back to recover original coordinates when the original data were not centered.
Our matrix has zero column sums, so it can serve directly as . With , the covariance is and component variances are 6 and 2. The first component's scores are . Multiplying by reconstructs , with explained variance .
Clustering and dimensionality reduction discusses scaling, fitting data, and downstream evaluation. The 75% measures variance retained in these coordinates; it does not establish that a classification task retains enough information.
Least squares: zero and small singular values
For , MIT's pseudoinverse notes construct the pseudoinverse by reciprocating the nonzero singular values. The least-squares solution of minimum Euclidean norm is
This projects into the column space and undoes the stretching direction by direction. Every other least-squares solution has the form with . These null-space directions are orthogonal to , so has the smallest norm.
For our example with , the coefficients along the two singular directions are and . This yields
The residual lies in the left null space: . The full-column-rank gives unique coefficients. Replacing it with gives , so adding to any solution leaves predictions unchanged. Identifiable coefficients require that the matrix erase no input direction completely.
A small nonzero singular value creates a different problem: division by it amplifies data perturbations. With fixed, produces . For example, with , a perturbation of in the second output coordinate becomes a coefficient perturbation of .
For full column rank, the spectral condition number is . MIT's numerical-methods notes, Lecture 5, relate these norms to the extreme eigenvalues of and show that : the eigenvalues are squared singular values. Our example gives and 3 respectively; has condition number 1000. Numerical analysis separates problem sensitivity from algorithmic stability; ordinary least squares and regularization develops solver choices and coefficient interpretation.
Truncating an SVD for dimensionality reduction controls matrix reconstruction error. Truncating a pseudoinverse for solving discards coefficient directions and changes the estimate. Choose a threshold using data error and the intended use; a small nonzero singular value is not a mathematical zero.
Checking the components with Python
This standard-library code checks the derived eigenvectors and singular components, then computes reconstruction, rank-one error, and least-squares coefficients. The helpers take lists and require matching vector lengths or matrix row lengths; incompatible dimensions raise ValueError.
from math import sqrt, isclose
def dot(x, y):
if len(x) != len(y):
raise ValueError("Vector lengths must match")
return sum(a * b for a, b in zip(x, y))
def mv(matrix, vector):
return [dot(row, vector) for row in matrix]
B = [[2, 1], [1, 2]]
vectors = [[1 / sqrt(2), 1 / sqrt(2)],
[1 / sqrt(2), -1 / sqrt(2)]]
for eigenvalue, v in zip([3, 1], vectors):
assert all(isclose(a, eigenvalue * b, abs_tol=1e-12)
for a, b in zip(mv(B, v), v))
A = [[2, 0], [0, 2], [-2, -2]]
s = [sqrt(12), 2.0]
left = [[a / sigma for a in mv(A, v)]
for sigma, v in zip(s, vectors)]
assert isclose(dot(left[0], left[1]), 0, abs_tol=1e-12)
assert all(isclose(dot(u, u), 1) for u in left)
components = [[[sigma * u[i] * v[j] for j in range(2)]
for i in range(3)]
for sigma, u, v in zip(s, left, vectors)]
reconstructed = [[sum(c[i][j] for c in components) for j in range(2)]
for i in range(3)]
assert all(isclose(reconstructed[i][j], A[i][j], abs_tol=1e-12)
for i in range(3) for j in range(2))
A1 = components[0]
error = sqrt(sum((A[i][j] - A1[i][j]) ** 2
for i in range(3) for j in range(2)))
b = [1, 0, 0]
x = [sum(v[j] * dot(u, b) / sigma
for sigma, u, v in zip(s, left, vectors)) for j in range(2)]
print("eigenvalues:", [3, 1])
print("singular values:", [round(t, 6) for t in s])
print("reconstructed:", [[round(t, 6) for t in row] for row in reconstructed])
print("rank-one:", [[round(t, 6) for t in row] for row in A1])
print(f"Frobenius error: {error:.6f}")
print(f"retained variance: {s[0] ** 2 / sum(t * t for t in s):.6f}")
print("least-squares x:", [round(t, 6) for t in x])
Output:
eigenvalues: [3, 1]
singular values: [3.464102, 2.0]
reconstructed: [[2.0, 0.0], [0.0, 2.0], [-2.0, -2.0]]
rank-one: [[1.0, 1.0], [1.0, 1.0], [-2.0, -2.0]]
Frobenius error: 2.000000
retained variance: 0.750000
least-squares x: [0.333333, -0.166667]