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.