3.1 Numerical Linear Algebra
: 20 minutes
NumPy offers functions for matrix operations such as addition, multiplication, dot product, decompositions, etc. These operations also apply to higher-dimensional arrays.
Basic Operations
The dot product of two vectors can be computed using numpy.dot().
Recall that * computes the element-wise product of two arrays. To perform matrix multiplication, one can either use @ or numpy.dot.
numpy.linalg
Matrix decompositions such as LU, SVD, and operations like inverse and determinant are offered through the numpy.linalg module.
| Method | Description |
|---|---|
diag |
Return the diagonal (or off-diagonal) elements of a square matrix as a 1D array, or convert a 1D array into a square matrix with zeros on the off-diagonal |
dot |
Matrix multiplication |
trace |
Compute the sum of the diagonal elements |
det |
Compute the matrix determinant |
eig |
Compute the eigenvalues and eigenvectors of a square matrix |
inv |
Compute the inverse of a square matrix |
pinv |
Compute the Moore-Penrose pseudoinverse of a matrix |
qr |
Compute the QR decomposition |
svd |
Compute the singular value decomposition (SVD) |
solve |
Solve the linear system Ax = b for x, where A is a square matrix |
Solving A\mathbf{x} = \mathbf{b}
It is tempting to solve a linear system by computing an inverse and multiplying. Do not.
The two agree here, but solve is faster and numerically better behaved. It works by factoring A into triangular pieces—an LU decomposition—and then solving two easy triangular systems. NumPy does this internally; if you ever need the factors themselves they live in scipy.linalg.lu.
Forming inv(A) does strictly more work than the problem requires: it solves n systems (one per column of the identity) in order to answer a question about one right-hand side, and every one of those solves contributes its own rounding error. The rule of thumb worth carrying: if you find yourself writing inv(A) @ b, you wanted solve(A, b).
When A is singular, solve refuses rather than guessing.
That refusal is a feature. A singular A means the columns do not span enough of \mathbb{R}^m to reach every \mathbf{b}, so there is either no solution or infinitely many. Section 3.2 shows what to ask for instead.
The QR decomposition
Any matrix A can be written A = QR, where Q has orthonormal columns (Q^\top Q = I) and R is upper triangular.
Orthonormal columns are the point. As we will see in 3.2, they turn projection—and therefore least squares—into a matter of multiplication rather than inversion.
Eigenvalues and eigenvectors
A vector \mathbf{v} \neq \mathbf{0} is an eigenvector of a square matrix A when A merely stretches it:
A\mathbf{v} = \lambda\mathbf{v}
The scalar \lambda is the corresponding eigenvalue. Eigenvectors are the directions the matrix does not rotate.
For symmetric matrices—and covariance matrices are symmetric—the eigenvalues are real and the eigenvectors can be chosen orthogonal. Use eigh rather than eig in that case; it is faster and it will not hand you complex numbers from rounding error.
Those eigenvectors are the principal components, which is where 3.3 picks the story up.
For a symmetric A, eigh returns eigenvectors that are not merely orthogonal but orthonormal, collected as the columns of a matrix V with V^\top V = I. That single fact turns the eigendecomposition into something you can write down without an inverse:
A = V\,\Lambda\,V^{-1} = V\,\Lambda\,V^{\top}, \qquad \Lambda = \operatorname{diag}(\lambda_1,\dots,\lambda_n).
This is the spectral decomposition, and it is the symmetric special case of the SVD you will meet in 3.3.
Condition number
np.linalg.cond measures how much a matrix amplifies error. Formally, if A\mathbf{x} = \mathbf{b} and we perturb the right-hand side to \mathbf{b} + \delta\mathbf{b}, the solution moves by \delta\mathbf{x} with
\frac{\|\delta\mathbf{x}\|}{\|\mathbf{x}\|} \;\le\; \kappa(A)\,\frac{\|\delta\mathbf{b}\|}{\|\mathbf{b}\|}.
The condition number \kappa(A) is the worst-case amplification factor for relative error. A large \kappa means the columns are nearly linearly dependent, and that solving with the matrix will be unreliable no matter how good the algorithm is—it is a property of the problem, not of NumPy.
For a symmetric positive definite matrix there is a clean formula: \kappa(A) = \lambda_{\max}/\lambda_{\min}. In general \kappa(A) = \sigma_{\max}/\sigma_{\min}, a ratio of singular values, which is one more reason 3.3 matters.
Keep that amplification in mind. When we reach regression on Nov 3, a design matrix with near-duplicate predictors produces exactly this: the coefficients become enormous and unstable while the fitted values barely move. That is multicollinearity, and ridge regression is the standard response.