Andrew Hoang
← All posts
Linear AlgebraJune 3, 20253 min readLinear Algebra Notes · Ch. 2

Chapter 2: Matrices as Linear Transformations

Matrices aren't just grids of numbers — they represent linear maps. Composition, rotation, scaling, and why matrix multiplication is defined the way it is.

#linear-algebra#matrices#transformations

A matrix is a function#

The most important reframe in linear algebra: a matrix ARm×nA \in \mathbb{R}^{m \times n} is not just a table of numbers — it's a linear transformation T:RnRmT: \mathbb{R}^n \to \mathbb{R}^m, defined by T(x)=AxT(\mathbf{x}) = A\mathbf{x}.

"Linear" means it preserves vector addition and scalar multiplication:

T(u+v)=T(u)+T(v),T(cv)=cT(v)T(\mathbf{u} + \mathbf{v}) = T(\mathbf{u}) + T(\mathbf{v}), \qquad T(c\mathbf{v}) = cT(\mathbf{v})

Matrices act on the basis#

Because a linear map is fully determined by where it sends the basis vectors, the columns of a matrix are exactly the images of the standard basis vectors.

import numpy as np
import matplotlib.pyplot as plt

def plot_transform(A, title):
    theta = np.linspace(0, 2 * np.pi, 100)
    circle = np.array([np.cos(theta), np.sin(theta)])
    transformed = A @ circle

    fig, ax = plt.subplots(figsize=(5, 5))
    ax.plot(*circle, '--', color='gray', alpha=0.5, label='unit circle')
    ax.plot(*transformed, color='steelblue', label='transformed')

    e1, e2 = A @ np.array([1, 0]), A @ np.array([0, 1])
    ax.arrow(0, 0, *e1, head_width=0.08, color='red', length_includes_head=True)
    ax.arrow(0, 0, *e2, head_width=0.08, color='green', length_includes_head=True)

    ax.set_xlim(-3, 3); ax.set_ylim(-3, 3)
    ax.axhline(0, color='gray', lw=0.5); ax.axvline(0, color='gray', lw=0.5)
    ax.set_aspect('equal'); ax.set_title(title); ax.legend(loc='upper left', fontsize=8)
    plt.tight_layout()
    plt.show()

theta = np.pi / 4
R = np.array([[np.cos(theta), -np.sin(theta)],
              [np.sin(theta),  np.cos(theta)]])
S = np.array([[2, 0], [0, 0.5]])
A = R @ S

plot_transform(A, "Scale then rotate: A = R · S")

Why matrix multiplication is defined that way#

Composing two linear transformations T1T_1 (matrix AA) then T2T_2 (matrix BB) should itself be linear, representable by a single matrix. That matrix is exactly the matrix product BABA:

(T2T1)(x)=T2(T1(x))=B(Ax)=(BA)x(T_2 \circ T_1)(\mathbf{x}) = T_2(T_1(\mathbf{x})) = B(A\mathbf{x}) = (BA)\mathbf{x}

This is why matrix multiplication has the (initially odd-looking) row-times-column rule — it's forced by function composition.

import numpy as np

np.random.seed(0)
A = np.random.randn(3, 3)
B = np.random.randn(3, 3)
x = np.random.randn(3)

lhs = B @ (A @ x)     # apply A, then B
rhs = (B @ A) @ x     # apply combined matrix BA

print("B(Ax)  =", np.round(lhs, 4))
print("(BA)x  =", np.round(rhs, 4))
print("Equal:", np.allclose(lhs, rhs))

The determinant as area/volume scaling#

The determinant of a 2×2 matrix tells you how much the transformation scales area (and whether it flips orientation).

import numpy as np

matrices = {
    "Identity": np.eye(2),
    "Scale by 2": np.array([[2, 0], [0, 2]]),
    "Shear": np.array([[1, 1], [0, 1]]),
    "Reflection": np.array([[1, 0], [0, -1]]),
    "Singular (rank 1)": np.array([[1, 2], [2, 4]]),
}

for name, M in matrices.items():
    print(f"{name:20s} det = {np.linalg.det(M): .3f}")

Notice the singular matrix has determinant 0 — it collapses 2D space onto a line, destroying area entirely. This is the same condition as linear dependence of its columns from Chapter 1.


This series continues into systems of equations and null spaces — see "Systems of Linear Equations and the Null Space" for the interactive solver.