Reshaping Arrays¶
Reshaping changes an array's dimensions while preserving its data.
Mental Model
Reshape reinterprets a flat sequence of numbers by placing new row and column boundaries. The total number of elements stays constant -- you are folding the same ribbon of data into a different grid. When possible, NumPy returns a view (no copy), so the reshaped array shares memory with the original.
Method reshape¶
The reshape method returns a new view with the specified shape.
1. Basic Usage¶
```python import numpy as np
def main(): x = np.arange(6) print(f"{x.shape = }") print(x, end="\n\n")
y = x.reshape((3, 2))
print(f"{y.shape = }")
print(y)
if name == "main": main() ```
Output:
``` x.shape = (6,) [0 1 2 3 4 5]
y.shape = (3, 2) [[0 1] [2 3] [4 5]] ```
2. Total Size Rule¶
The product of new dimensions must equal the original total size.
Function np.reshape¶
The np.reshape function provides the same functionality.
1. Function Syntax¶
```python import numpy as np
def main(): x = np.arange(6) print(f"{x.shape = }") print(x, end="\n\n")
y = np.reshape(x, (3, 2))
print(f"{y.shape = }")
print(y)
if name == "main": main() ```
2. Method vs Function¶
Both are equivalent; the method syntax is more common.
The -1 Wildcard¶
Using -1 lets NumPy infer one dimension automatically.
1. Flatten to 1D¶
```python import numpy as np
def main(): x = np.zeros((2, 3, 4)) y = x.reshape((-1,)) print(f"{x.shape = }") print(f"{y.shape = }")
if name == "main": main() ```
Output:
x.shape = (2, 3, 4)
y.shape = (24,)
2. Keep First Axis¶
```python import numpy as np
def main(): x = np.zeros((2, 3, 4)) z = x.reshape((2, -1)) print(f"{x.shape = }") print(f"{z.shape = }")
if name == "main": main() ```
Output:
x.shape = (2, 3, 4)
z.shape = (2, 12)
3. Keep Last Axis¶
```python import numpy as np
def main(): x = np.zeros((2, 3, 4)) w = x.reshape((-1, 4)) print(f"{x.shape = }") print(f"{w.shape = }")
if name == "main": main() ```
Output:
x.shape = (2, 3, 4)
w.shape = (6, 4)
Multiple Examples¶
Combining all -1 examples in one block.
1. All Together¶
```python import numpy as np
def main(): x = np.zeros((2, 3, 4)) y = x.reshape((-1,)) z = x.reshape((2, -1)) w = x.reshape((-1, 4)) print(f"{x.shape = }") print(f"{y.shape = }") print(f"{z.shape = }") print(f"{w.shape = }")
if name == "main": main() ```
2. Only One -1¶
You can only use -1 for a single dimension per reshape call.
Runnable Example: shape_manipulation_tutorial.py¶
```python """ 02_shape_manipulation.py - Reshaping and Transforming
🔗 Topic #24: Most operations return VIEWS (no memory copy)! """
import numpy as np
if name == "main":
print("="*80)
print("SHAPE MANIPULATION")
print("="*80)
print("\n🔗 Remember: Most operations return VIEWS (Topic #24)!")
# ============================================================================
# Reshape
# ============================================================================
print("\n" + "="*80)
print("Reshape - Change Dimensions")
print("="*80)
arr = np.arange(12)
print(f"Original: {arr}")
print(f"Shape: {arr.shape}")
matrix = arr.reshape(3, 4)
print(f"\nReshaped to (3, 4):\n{matrix}")
# CRITICAL: Reshape returns a VIEW!
print(f"\nIs it a view? {matrix.base is arr}")
matrix[0, 0] = 999
print(f"After matrix[0,0]=999: original arr[0]={arr[0]}")
print("They share memory! (Topic #24)")
# ============================================================================
# Transpose
# ============================================================================
print("\n" + "="*80)
print("Transpose - Flip Dimensions")
print("="*80)
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(f"Original (2,3):\n{matrix}")
transposed = matrix.T
print(f"\nTransposed (3,2):\n{transposed}")
print(f"Is view? {transposed.base is matrix}")
# ============================================================================
# Ravel and Flatten
# ============================================================================
print("\n" + "="*80)
print("Ravel vs Flatten")
print("="*80)
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(f"Matrix:\n{matrix}")
ravel = matrix.ravel() # Returns view if possible
flatten = matrix.flatten() # Always returns copy
print(f"\nravel(): {ravel}")
print(f"Is view? {ravel.base is matrix}")
print(f"\nflatten(): {flatten}")
print(f"Is view? {flatten.base is matrix}")
print("""
\nravel(): Fast (view if possible)
flatten(): Slower (always copies)
""")
print("""
\n🎯 KEY TAKEAWAYS:
1. reshape() returns views (fast!)
2. .T (transpose) returns views
3. ravel() tries to return view
4. flatten() always copies
5. Check .base to verify view vs copy
🔜 NEXT: 03_mathematical_ops.py
""")
```
Contiguity and Reshape Pitfalls¶
Non-Contiguous Arrays
After slicing, an array may become non-contiguous in memory. Reshaping a non-contiguous array forces a copy instead of returning a view:
```python import numpy as np
a = np.arange(12).reshape(3, 4) sliced = a[:, ::2] # every other column — non-contiguous print(sliced.flags['C_CONTIGUOUS']) # False
reshaped = sliced.reshape(-1) # works, but creates a COPY reshaped[0] = 999 print(sliced[0, 0]) # unchanged — it's a copy, not a view ```
Check arr.flags['C_CONTIGUOUS'] before relying on reshape returning a view. If contiguity matters, call np.ascontiguousarray(arr) first.
Exercises¶
Exercise 1.
Create a 1D array of 24 elements using np.arange(24). Reshape it into a 3D array of shape (2, 3, 4). Then reshape the 3D array back to a 1D array using -1 as the dimension argument. Verify that the values are unchanged.
Solution to Exercise 1
import numpy as np
a = np.arange(24)
b = a.reshape(2, 3, 4)
print(b.shape) # (2, 3, 4)
c = b.reshape(-1)
print(c.shape) # (24,)
print(np.array_equal(a, c)) # True
Exercise 2.
Given a = np.arange(12), reshape it into shape (3, -1) and print the result. Then reshape the original array into shape (-1, 2) and print the result. Explain what -1 does in each case.
Solution to Exercise 2
import numpy as np
a = np.arange(12)
b = a.reshape(3, -1)
print(b.shape) # (3, 4) — NumPy infers 12/3 = 4
print(b)
c = a.reshape(-1, 2)
print(c.shape) # (6, 2) — NumPy infers 12/2 = 6
print(c)
# -1 tells NumPy to compute that dimension automatically
# so that the total number of elements is preserved.
Exercise 3.
Create a 2D array a = np.arange(6).reshape(2, 3). Attempt to reshape it into shape (4, 2) and catch the ValueError. Print the error message and explain why the reshape fails.
Solution to Exercise 3
import numpy as np
a = np.arange(6).reshape(2, 3)
try:
b = a.reshape(4, 2)
except ValueError as e:
print(f"Error: {e}")
# Error: cannot reshape array of size 6 into shape (4,2)
# 4 * 2 = 8 != 6, so the reshape is impossible.