📐 Array Shape and Dimensions
Understanding array shape and dimensions is fundamental to working with NumPy effectively! Shape tells you how your data is organized, while dimensions indicate how many "levels" of nesting your array has. This knowledge is essential for data manipulation, mathematical operations, and avoiding common errors.
Think of shape as the "floor plan" of your data structure!
import numpy as np
# Examples of different shapes and dimensions
vector = np.array([1, 2, 3, 4]) # 1D
matrix = np.array([[1, 2, 3], [4, 5, 6]]) # 2D
cube = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) # 3D
print(f"Vector: {vector}")
print(f" Shape: {vector.shape}, Dimensions: {vector.ndim}")
print(f"Matrix: \n{matrix}")
print(f" Shape: {matrix.shape}, Dimensions: {matrix.ndim}")
print(f"Cube shape: {cube.shape}, Dimensions: {cube.ndim}")
🎯 Understanding Array Shape
Shape is a tuple that tells you the size along each dimension. It's like reading the dimensions of a box: length × width × height.
import numpy as np
# Different shapes explained
array_1d = np.array([10, 20, 30, 40, 50]) # Shape: (5,)
array_2d = np.array([[1, 2, 3], [4, 5, 6]]) # Shape: (2, 3)
array_3d = np.zeros((2, 3, 4)) # Shape: (2, 3, 4)
print(f"1D array: {array_1d}")
print(f" Shape: {array_1d.shape} → {len(array_1d)} elements")
print(f"2D array: \n{array_2d}")
print(f" Shape: {array_2d.shape} → {array_2d.shape[0]} rows, {array_2d.shape[1]} columns")
print(f"3D array shape: {array_3d.shape}")
print(f" → {array_3d.shape[0]} layers, {array_3d.shape[1]} rows, {array_3d.shape[2]} columns")
Shape interpretation:
- (5,): 1D array with 5 elements
- (2, 3): 2D array with 2 rows and 3 columns
- (2, 3, 4): 3D array with 2 layers, 3 rows, 4 columns
📏 Dimensions Explained
Dimensions (ndim) tell you how many indices you need to access a single element:
import numpy as np
# Understanding dimensions
data_1d = np.array([10, 20, 30])
data_2d = np.array([[1, 2], [3, 4], [5, 6]])
data_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(f"1D array: {data_1d}")
print(f" Dimensions: {data_1d.ndim}")
print(f" Access element: data_1d[0] = {data_1d[0]}")
print(f"2D array: \n{data_2d}")
print(f" Dimensions: {data_2d.ndim}")
print(f" Access element: data_2d[1, 0] = {data_2d[1, 0]}")
print(f"3D array dimensions: {data_3d.ndim}")
print(f" Access element: data_3d[0, 1, 1] = {data_3d[0, 1, 1]}")
🔍 Real-world Shape Examples
Let's see how shape applies to real data structures:
import numpy as np
print("🌍 Real-world Array Shapes")
print("=" * 30)
# Example 1: Image data
image_rgb = np.random.randint(0, 255, (480, 640, 3))
print(f"RGB Image shape: {image_rgb.shape}")
print(f" Height: {image_rgb.shape[0]} pixels")
print(f" Width: {image_rgb.shape[1]} pixels")
print(f" Channels: {image_rgb.shape[2]} (Red, Green, Blue)")
# Example 2: Time series data
time_series = np.random.randn(365, 24) # 365 days, 24 hours each
print(f"Time series shape: {time_series.shape}")
print(f" Days: {time_series.shape[0]}")
print(f" Hours per day: {time_series.shape[1]}")
# Example 3: Student grades
grades = np.array([[85, 92, 78], [91, 87, 95], [76, 89, 82]])
print(f"Grades shape: {grades.shape}")
print(f" Students: {grades.shape[0]}")
print(f" Subjects: {grades.shape[1]}")
📊 Working with Shape Information
You can use shape information to understand and manipulate your data:
import numpy as np
# Using shape for calculations
sales_data = np.array([
[150, 200, 175, 180], # Q1
[160, 210, 185, 190], # Q2
[140, 195, 170, 175], # Q3
[170, 220, 195, 200] # Q4
])
print(f"Sales data shape: {sales_data.shape}")
print(f"Data: \n{sales_data}")
# Extract shape information
num_quarters = sales_data.shape[0]
num_products = sales_data.shape[1]
total_data_points = sales_data.size
print(f"\nData analysis:")
print(f" Quarters tracked: {num_quarters}")
print(f" Products tracked: {num_products}")
print(f" Total data points: {total_data_points}")
# Calculate using shape
quarterly_totals = np.sum(sales_data, axis=1) # Sum across products
product_totals = np.sum(sales_data, axis=0) # Sum across quarters
print(f" Quarterly totals: {quarterly_totals}")
print(f" Product totals: {product_totals}")
🔄 Reshaping Arrays
You can change an array's shape without changing its data using reshape()
:
import numpy as np
# Reshaping examples
original = np.arange(12) # 12 elements: 0, 1, 2, ..., 11
print(f"Original: {original}")
print(f"Original shape: {original.shape}")
# Different reshape options
as_matrix = original.reshape(3, 4) # 3 rows, 4 columns
as_tall = original.reshape(6, 2) # 6 rows, 2 columns
as_cube = original.reshape(2, 2, 3) # 2×2×3 cube
auto_cols = original.reshape(4, -1) # 4 rows, auto columns
print(f"As matrix (3×4): \n{as_matrix}")
print(f"As tall (6×2): \n{as_tall}")
print(f"As cube (2×2×3) shape: {as_cube.shape}")
print(f"Auto columns (4×?): \n{auto_cols}")
📏 Shape Compatibility for Operations
Understanding shape is crucial for mathematical operations and broadcasting:
import numpy as np
# Shape compatibility examples
matrix = np.array([[1, 2, 3], [4, 5, 6]]) # Shape: (2, 3)
vector = np.array([10, 20, 30]) # Shape: (3,)
scalar = 5
print(f"Matrix shape: {matrix.shape}")
print(f"Vector shape: {vector.shape}")
# Compatible operations
result1 = matrix + vector # Broadcasting works!
result2 = matrix * scalar # Scalar multiplication
print(f"Matrix: \n{matrix}")
print(f"Vector: {vector}")
print(f"Matrix + Vector: \n{result1}")
print(f"Matrix × Scalar: \n{result2}")
# Check compatibility
print(f"Compatible for addition? {matrix.shape[-1] == vector.shape[0]}")
🎯 Common Shape Operations
Here are essential operations you'll use frequently:
import numpy as np
# Common shape operations
data = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print(f"Original: \n{data}")
print(f"Shape: {data.shape}")
# Transpose (swap rows and columns)
transposed = data.T
print(f"Transposed: \n{transposed}")
print(f"Transposed shape: {transposed.shape}")
# Flatten to 1D
flattened = data.flatten()
print(f"Flattened: {flattened}")
print(f"Flattened shape: {flattened.shape}")
# Add new dimension
expanded = data[np.newaxis, :] # Add dimension at start
print(f"Expanded shape: {expanded.shape}")
# Squeeze out size-1 dimensions
squeezed = np.squeeze(expanded)
print(f"Squeezed shape: {squeezed.shape}")
🔍 Shape Debugging Tips
When working with arrays, shape mismatches are common. Here's how to debug:
import numpy as np
# Common shape debugging scenarios
def debug_shapes(arr1, arr2, operation="operation"):
print(f"Shape debugging for {operation}:")
print(f" Array 1 shape: {arr1.shape}")
print(f" Array 2 shape: {arr2.shape}")
# Check compatibility
try:
result = arr1 + arr2 # Test operation
print(f" ✅ Compatible! Result shape: {result.shape}")
except ValueError as e:
print(f" ❌ Shape mismatch: {e}")
print(f" 💡 Try reshaping or broadcasting")
# Test different scenarios
a = np.array([[1, 2, 3], [4, 5, 6]]) # (2, 3)
b = np.array([10, 20, 30]) # (3,)
c = np.array([[10], [20]]) # (2, 1)
debug_shapes(a, b, "matrix + vector")
debug_shapes(a, c, "matrix + column vector")
# Fix shape mismatch
b_reshaped = b.reshape(1, 3) # Make it (1, 3)
debug_shapes(a, b_reshaped, "matrix + reshaped vector")
🎯 Key Takeaways
🚀 What's Next?
Great! Now you understand how arrays are structured. Next, let's explore data types and memory usage - understanding how NumPy stores different kinds of numbers efficiently.
Continue to: Data Types and Memory
Ready to master data storage! 🔢✨
Was this helpful?
Track Your Learning Progress
Sign in to bookmark tutorials and keep track of your learning journey.
Your progress is saved automatically as you read.