Okay, let's put theory into practice. We've discussed how matrices can represent linear transformations. Now, we'll use NumPy to apply transformations like scaling and rotation to a set of data points. This exercise demonstrates the power of matrix multiplication for manipulating geometric data, a common task in areas like computer graphics and data preprocessing for machine learning.
First, ensure you have NumPy installed and import it. We'll define a small dataset of 2D points. Let's use points that form a simple shape, like an "L", to make the transformations easy to visualize.
import numpy as np
import math
# Define our data points as row vectors in a matrix
# Each row is a point (x, y)
data_points = np.array([
[0, 0],
[1, 0],
[1, 1],
[1, 2]
])
print("Original Data Points (each row is a point):")
print(data_points)
Our data_points
matrix has 4 rows (points) and 2 columns (x and y coordinates).
Before transforming, let's visualize our starting points. A scatter plot is suitable for this.
The initial L-shape formed by our data points.
Let's apply a scaling transformation. We'll scale the x-coordinates by 1.5 and the y-coordinates by 0.5. The scaling matrix S for this is:
S=[1.5000.5]When we multiply our data matrix D (where points are rows) by this scaling matrix S, D′=D@S, each point (x,y) becomes (1.5x,0.5y).
# Define the scaling matrix
scaling_matrix = np.array([
[1.5, 0],
[0, 0.5]
])
# Apply the transformation
# Data points are rows (n x 2), Scaling matrix is (2 x 2)
# Result is (n x 2) @ (2 x 2) = (n x 2)
scaled_data = data_points @ scaling_matrix
print("\nScaled Data Points:")
print(scaled_data)
Let's visualize the result alongside the original points.
The L-shape stretched horizontally (x-axis scaled by 1.5) and compressed vertically (y-axis scaled by 0.5).
Now, let's rotate the original points counter-clockwise by 45 degrees (π/4 radians). The rotation matrix R for a counter-clockwise rotation by an angle θ is:
R=[cos(θ)−sin(θ)sin(θ)cos(θ)]We apply this as D′=D@R.
# Define the rotation angle in radians
angle_degrees = 45
angle_radians = math.radians(angle_degrees)
cos_theta = math.cos(angle_radians)
sin_theta = math.sin(angle_radians)
# Define the rotation matrix
rotation_matrix = np.array([
[cos_theta, sin_theta],
[-sin_theta, cos_theta]
])
# Apply the transformation to the original data
rotated_data = data_points @ rotation_matrix
print(f"\nRotation Matrix ({angle_degrees} degrees CCW):")
print(rotation_matrix)
print("\nRotated Data Points:")
print(rotated_data)
Let's visualize the rotation.
The L-shape rotated 45 degrees counter-clockwise around the origin (0,0). Notice the coordinates change according to the trigonometric functions in the rotation matrix.
Linear transformations can be chained together. The effect of applying transformation T1 followed by T2 to data D is equivalent to applying a single combined transformation Tcombined=T1@T2. The order matters! Let's apply the scaling first, then the rotation.
D′′=(D@S)@R=D@(S@R)
# Combined transformation: Scale first, then Rotate
# Option 1: Apply sequentially
temp_data = data_points @ scaling_matrix # Apply scaling first
scaled_then_rotated_data = temp_data @ rotation_matrix # Then apply rotation
# Option 2: Combine matrices first
combined_matrix_SR = scaling_matrix @ rotation_matrix
scaled_then_rotated_data_combined = data_points @ combined_matrix_SR
print("\nCombined Matrix (Scale then Rotate):")
print(combined_matrix_SR)
print("\nScaled then Rotated Data (Sequential):")
print(scaled_then_rotated_data)
print("\nScaled then Rotated Data (Combined Matrix):")
print(scaled_then_rotated_data_combined)
# Verify they are numerically close
assert np.allclose(scaled_then_rotated_data, scaled_then_rotated_data_combined)
Let's visualize the final result of scaling followed by rotation.
The L-shape after being scaled (stretched horizontally, compressed vertically) and then rotated 45 degrees counter-clockwise. Compare this to applying rotation first, then scaling (exercise for the reader!).
This hands-on exercise demonstrated how matrix multiplication provides a concise and computationally efficient way to apply geometric transformations to data. We used NumPy's @
operator for matrix multiplication to perform scaling and rotation on a set of 2D points.
Understanding these operations is significant for:
Here's the complete Python code for reference:
import numpy as np
import math
# 1. Define Original Data
data_points = np.array([
[0, 0],
[1, 0],
[1, 1],
[1, 2]
])
print("Original Data Points:\n", data_points)
# 2. Define Scaling Transformation
scaling_factor_x = 1.5
scaling_factor_y = 0.5
scaling_matrix = np.array([
[scaling_factor_x, 0],
[0, scaling_factor_y]
])
scaled_data = data_points @ scaling_matrix
print("\nScaling Matrix:\n", scaling_matrix)
print("Scaled Data:\n", scaled_data)
# 3. Define Rotation Transformation
angle_degrees = 45
angle_radians = math.radians(angle_degrees)
cos_theta = math.cos(angle_radians)
sin_theta = math.sin(angle_radians)
rotation_matrix = np.array([
[cos_theta, sin_theta],
[-sin_theta, cos_theta]
])
rotated_data = data_points @ rotation_matrix
print(f"\nRotation Matrix ({angle_degrees} degrees CCW):\n", rotation_matrix)
print("Rotated Data:\n", rotated_data)
# 4. Combined Transformation (Scale then Rotate)
# Option 1: Sequential
scaled_then_rotated_data = (data_points @ scaling_matrix) @ rotation_matrix
# Option 2: Combine matrices first
combined_matrix_SR = scaling_matrix @ rotation_matrix
scaled_then_rotated_data_combined = data_points @ combined_matrix_SR
print("\nCombined Matrix (Scale @ Rotate):\n", combined_matrix_SR)
print("Scaled then Rotated Data:\n", scaled_then_rotated_data)
# Verification
assert np.allclose(scaled_then_rotated_data, scaled_then_rotated_data_combined)
print("\nSequential and combined matrix application results are consistent.")
Experiment with different transformation matrices (e.g., shearing, reflections, different scaling factors, or rotation angles) and observe their effects on the data points. Consider applying transformations in a different order to see how the results change.
© 2025 ApX Machine Learning