0Pricing
Pandas & NumPy Academy · Lesson

Eigenvalues and SVD Overview

Compute eigenvalues and eigenvectors with np.linalg.eig and understand how SVD underpins PCA dimensionality reduction.

Eigenvalues and SVD Overview is a free Pandas & NumPy Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Pandas & NumPy Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Are Eigenvalues and Eigenvectors?

An eigenvector of a square matrix A is a non-zero vector v such that A @ v = lambda * v — multiplying by A only scales v, it does not change its direction. The scalar lambda is called the eigenvalue corresponding to v. Eigenvalues reveal the intrinsic 'stretching factors' of a linear transformation: an eigenvalue of 2 means the matrix doubles lengths in the eigenvector direction; a negative eigenvalue flips direction.

import numpy as np

A = np.array([[3.0, 1.0],
              [0.0, 2.0]])

eigenvalues, eigenvectors = np.linalg.eig(A)
print('Eigenvalues:', eigenvalues)
print('Eigenvectors (columns):')
print(eigenvectors)

Computing Eigenvalues with np.linalg.eig()

np.linalg.eig(A) returns a tuple (eigenvalues, eigenvectors). The eigenvalues are a 1-D array; the eigenvectors are a 2-D array where each column is an eigenvector. For real symmetric matrices (like covariance matrices), the eigenvalues are always real and the eigenvectors are orthogonal — use np.linalg.eigh(A) for symmetric matrices as it is faster and guaranteed to return real results.

import numpy as np

# Symmetric matrix -> use eigh for efficiency and real eigenvalues
A = np.array([[4.0, 2.0],
              [2.0, 3.0]])

vals, vecs = np.linalg.eigh(A)
print('Eigenvalues (real):', vals)
print('Eigenvectors (orthonormal columns):')
print(vecs)

# Verify: A @ v = lambda * v for each eigenvector
for i in range(len(vals)):
    lhs = A @ vecs[:, i]
    rhs = vals[i] * vecs[:, i]
    print(f'v{i} check:', np.allclose(lhs, rhs))

Eigenvalues and Matrix Properties

Eigenvalues encode important matrix properties. The determinant equals the product of all eigenvalues: det(A) = product(eigenvalues). The trace (sum of diagonal elements) equals the sum of eigenvalues: trace(A) = sum(eigenvalues). A matrix is positive definite (all eigenvalues > 0) only if all eigenvalues are positive — a crucial property for valid covariance matrices and convex optimisation problems.

import numpy as np

A = np.array([[4.0, 2.0],
              [2.0, 3.0]])

vals, _ = np.linalg.eigh(A)
print('Eigenvalues:', vals)
print('Product (should = det):', np.prod(vals))
print('np.linalg.det:', np.linalg.det(A))
print('Sum (should = trace):', np.sum(vals))
print('np.trace:', np.trace(A))
print('Positive definite:', np.all(vals > 0))

Singular Value Decomposition (SVD) Overview

Singular Value Decomposition (SVD) decomposes any matrix A (not just square ones) as A = U @ S @ V.T where U and V are orthogonal matrices and S is diagonal with non-negative singular values on the diagonal. SVD is the most general and numerically stable matrix factorisation. It underpins PCA, image compression, recommender systems, and the pseudo-inverse computation. np.linalg.svd(A) returns U, s (1-D singular values), and Vh (V transposed).

import numpy as np

A = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])

U, s, Vh = np.linalg.svd(A, full_matrices=False)
print('U shape:', U.shape)
print('s (singular values):', s)
print('Vh shape:', Vh.shape)

# Reconstruct A
A_reconstructed = U @ np.diag(s) @ Vh
print('Reconstruction correct:', np.allclose(A, A_reconstructed))

Singular Values and Matrix Rank

The singular values (diagonal of S) are always non-negative and conventionally listed in descending order. The number of non-zero singular values equals the rank of the matrix. Near-zero singular values indicate near-linear-dependence among rows or columns. The largest singular value gives the spectral norm of the matrix, and the ratio of largest to smallest non-zero singular value is the condition number used to measure numerical stability.

import numpy as np

# Full-rank matrix
A = np.random.rand(5, 3)
U, s, Vh = np.linalg.svd(A, full_matrices=False)
print('Singular values:', np.round(s, 4))
print('Rank (non-zero sv):', np.linalg.matrix_rank(A))
print('Condition number:', s[0] / s[-1])

# Rank-deficient matrix
B = np.array([[1.0, 2.0], [2.0, 4.0], [3.0, 6.0]])
_, sb, _ = np.linalg.svd(B, full_matrices=False)
print('Rank-deficient sv:', np.round(sb, 8))

SVD and PCA: The Connection

Principal Component Analysis (PCA) can be implemented directly via SVD. After centering your data matrix X (subtracting column means), the right singular vectors in Vh are the principal components, and the singular values squared (divided by n-1) are the variances explained. Sklearn's PCA uses exactly this approach. Understanding this connection lets you implement or customise PCA from scratch for dimensionality reduction.

import numpy as np

np.random.seed(0)
X = np.random.randn(100, 4)

# Center the data
X_centered = X - X.mean(axis=0)

# SVD-based PCA
U, s, Vh = np.linalg.svd(X_centered, full_matrices=False)

# Variance explained by each component
var_explained = (s ** 2) / (X.shape[0] - 1)
total_var = var_explained.sum()
print('Variance explained ratio:', np.round(var_explained / total_var, 3))

# Project onto top 2 principal components
X_pca = X_centered @ Vh[:2].T
print('Reduced shape:', X_pca.shape)

Low-Rank Approximation with SVD

SVD enables low-rank matrix approximation: keep only the top k singular values and vectors, and reconstruct an approximation of the original matrix. This is the basis of image compression and collaborative filtering for recommendations. The truncated SVD U[:, :k] @ np.diag(s[:k]) @ Vh[:k, :] gives the best rank-k approximation in the least-squares sense (Eckart-Young theorem).

import numpy as np

np.random.seed(1)
A = np.random.rand(20, 15)

U, s, Vh = np.linalg.svd(A, full_matrices=False)

# Rank-3 approximation
k = 3
A_approx = U[:, :k] @ np.diag(s[:k]) @ Vh[:k, :]

error = np.linalg.norm(A - A_approx, 'fro')
total = np.linalg.norm(A, 'fro')
print(f'Approximation error: {error/total:.3f} (fraction of total)')
print(f'Top-3 singular values capture {(s[:3]**2).sum()/(s**2).sum():.1%} of variance')

Eigendecomposition vs SVD: When to Use Which

Use eigendecomposition (np.linalg.eig or eigh) when you have a square symmetric matrix and want to understand its principal axes — for example, the covariance matrix in PCA or Markov transition matrices. Use SVD (np.linalg.svd) when your matrix is rectangular or when you need maximum numerical stability. SVD always exists; eigendecomposition can produce complex numbers for non-symmetric matrices.

import numpy as np

# Non-symmetric matrix: eigenvalues may be complex
A = np.array([[0.0, -1.0],
              [1.0,  0.0]])
vals, _ = np.linalg.eig(A)
print('Eigenvalues (complex for rotation):', vals)

# SVD always gives real singular values
_, s, _ = np.linalg.svd(A)
print('Singular values (always real):', s)

Spectral Theorem for Symmetric Matrices

The spectral theorem states that every real symmetric matrix A can be decomposed as A = Q @ diag(eigenvalues) @ Q.T where Q is orthogonal (Q.T = Q⁻¹). This means symmetric matrices are always diagonalisable with real eigenvalues and orthogonal eigenvectors. Covariance matrices, kernel matrices in SVMs, and the Hessian in optimisation are all symmetric, making this theorem universally useful in machine learning theory.

import numpy as np

A = np.array([[5.0, 2.0, 1.0],
              [2.0, 3.0, 0.0],
              [1.0, 0.0, 4.0]])

vals, Q = np.linalg.eigh(A)
print('Eigenvalues:', np.round(vals, 4))

# Reconstruct A = Q @ diag(vals) @ Q.T
A_reconstructed = Q @ np.diag(vals) @ Q.T
print('Reconstruction correct:', np.allclose(A, A_reconstructed))
print('Q is orthogonal:', np.allclose(Q.T @ Q, np.eye(3)))

Practical: Power Iteration for Dominant Eigenvalue

When you only need the largest eigenvalue and its eigenvector, power iteration is far more efficient than computing all eigenvalues. Start with a random vector, repeatedly multiply by A and normalise, and it converges to the dominant eigenvector. This is how Google's original PageRank algorithm worked. NumPy makes each iteration a single matrix-vector multiply with @.

import numpy as np

A = np.array([[4.0, 1.0, 2.0],
              [1.0, 3.0, 0.0],
              [2.0, 0.0, 2.0]])

v = np.random.rand(3)
for _ in range(50):
    v = A @ v
    v = v / np.linalg.norm(v)

eigenvalue_approx = v @ A @ v
print('Dominant eigenvector:', np.round(v, 4))
print('Approx eigenvalue:', round(eigenvalue_approx, 4))

# Verify with eigh
vals, vecs = np.linalg.eigh(A)
print('True max eigenvalue:', round(vals[-1], 4))

Using np.linalg.svd in Real Pipelines

In a real data pipeline, SVD is used for noise reduction and data compression. After fitting SVD on training data, you keep only the top k components that capture 95% of the variance. This reduces the dimensionality of new data before feeding it to a classifier or regressor, speeding up training and often improving generalisation by removing noisy dimensions. Always fit SVD on training data only and apply the same transform to test data.

import numpy as np

np.random.seed(7)
X_train = np.random.randn(200, 50)
X_test = np.random.randn(40, 50)

# Fit on training data
X_mean = X_train.mean(axis=0)
X_centered = X_train - X_mean
U, s, Vh = np.linalg.svd(X_centered, full_matrices=False)

# Choose k to capture 90% variance
cumvar = np.cumsum(s**2) / (s**2).sum()
k = np.searchsorted(cumvar, 0.9) + 1
print(f'Components to capture 90% variance: {k}')

# Transform test data using the same Vh
X_test_reduced = (X_test - X_mean) @ Vh[:k].T
print('Reduced test shape:', X_test_reduced.shape)

Quick Check

Test your understanding of Data Analysis concepts from this lesson.

Lesson Recap

In this lesson you learned: np.linalg.eig()/eigh() compute eigenvalues and eigenvectors that reveal intrinsic stretching directions of a matrix, np.linalg.svd() decomposes any matrix into U, singular values, and Vh enabling PCA and low-rank approximation, and singular values quantify the variance captured by each component and determine the matrix rank. Next up we tackle handling large datasets by streaming CSV files in chunks.

Frequently asked questions

Is the “Eigenvalues and SVD Overview” lesson free?

Yes — the full text of “Eigenvalues and SVD Overview” is free to read here on the web, and the Pandas & NumPy Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Pandas & NumPy Academy course, upgrade to CoddyKit PRO.

What will I learn in “Eigenvalues and SVD Overview”?

Compute eigenvalues and eigenvectors with np.linalg.eig and understand how SVD underpins PCA dimensionality reduction. You practise Pandas & NumPy Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Pandas & NumPy Academy?

No prior experience is required. Pandas & NumPy Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Eigenvalues and SVD Overview” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Pandas & NumPy Academy lesson?

Yes. Every Pandas & NumPy Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Matrix Multiplication with np.matmul and @
  2. Determinants, Inverses, and Transposes
  3. Solving Linear Systems
  4. Eigenvalues and SVD Overview
← Back to Pandas & NumPy Academy