Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Prerequisites: ch108 (Transformations), ch067 (Scaling in 1D)

Outcomes: Apply uniform and non-uniform scaling; Understand how scaling affects area and determinant; Connect to PCA and data normalization

Scaling Matrix

Scaling each coordinate independently: x’ = sx · x y’ = sy · y

Matrix form: S(sx, sy) = [[sx, 0], [0, sy]]

Special cases:

  • Uniform scaling: sx = sy = s → S = s·I — enlarges/shrinks uniformly

  • Non-uniform: sx ≠ sy — stretches/compresses differently along each axis

  • Reflection via scaling: sx = -1, sy = 1 → flip along y-axis

Area scaling: det(S) = sx · sy If you scale a region by sx along x and sy along y, the area scales by |sx · sy|.

Scaling along arbitrary axis: combine rotation + scale + rotation back: S_axis = R(-θ) · S(sx, sy) · R(θ) where θ is the angle of the axis.

(Determinant as area scaling factor derived in ch158.)

# --- Scaling visualization ---
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-v0_8-whitegrid')

def scale_matrix(sx, sy): return np.array([[sx, 0],[0, sy]])
def apply_mat(M, pts): return (M @ pts.T).T

# Unit square
square = np.array([[0,0],[1,0],[1,1],[0,1],[0,0]], dtype=float)

fig, axes = plt.subplots(1, 3, figsize=(14, 4))

# Various scalings
ax = axes[0]
for (sx, sy, color, label) in [(1,1,'gray','original'),(2,2,'steelblue','uniform 2x'),
                                 (2,0.5,'crimson','stretch x'),(0.5,2,'darkgreen','stretch y')]:
    scaled = apply_mat(scale_matrix(sx,sy), square)
    ax.plot(scaled[:,0], scaled[:,1], color=color, lw=2, label=label)
    ax.fill(scaled[:,0], scaled[:,1], alpha=0.1, color=color)
ax.set_aspect('equal'); ax.legend(fontsize=8); ax.set_title('Scaling the Unit Square')
ax.axhline(0,'k',lw=0.4); ax.axvline(0,'k',lw=0.4); ax.set_xlim(-0.5,2.5); ax.set_ylim(-0.5,2.5)

# Area = det(S)
scalings = [(i,j) for i in [0.5,1,2] for j in [0.5,1,2]]
areas = [abs(sx*sy) for sx,sy in scalings]
ax = axes[1]
ax.bar(range(len(scalings)), areas, color='steelblue', alpha=0.7, edgecolor='black')
ax.set_xticks(range(len(scalings)))
ax.set_xticklabels([f'({sx},{sy})' for sx,sy in scalings], rotation=45, fontsize=8)
ax.set_title('Area After Scaling: det(S) = sx·sy')
ax.set_ylabel('Area of scaled unit square')

# Scaling along arbitrary axis (shear analogy)
ax = axes[2]
theta = np.radians(30)
R = np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]])
S = scale_matrix(2, 0.5)
M = R.T @ S @ R  # scale along rotated axis
for pts, color, label in [(square,'gray','original'),(apply_mat(M, square),'steelblue','scaled along 30° axis')]:
    ax.plot(pts[:,0], pts[:,1], color=color, lw=2)
    ax.fill(pts[:,0], pts[:,1], alpha=0.15, color=color)
ax.set_aspect('equal'); ax.set_title('Scaling Along Arbitrary Axis')
ax.set_xlim(-1.5,2.5); ax.set_ylim(-1.5,2.5); ax.axhline(0,'k',lw=0.4); ax.axvline(0,'k',lw=0.4)

plt.suptitle('2D Scaling', fontsize=12, fontweight='bold')
plt.tight_layout(); plt.show()

Summary

  • S(sx,sy) = diag(sx,sy); scales x and y independently

  • Area scales by |det(S)| = |sx·sy|; reflections flip sign of determinant

  • Scaling along arbitrary axis: rotate, scale, rotate back

  • In PCA (ch175): data is standardized (scaled to unit variance) before finding principal components

Forward: ch114 (Affine Transformations) combines scaling with rotation and translation.