Index, Slice, and Reshape NumPy Arrays
Learn to index, slice, and reshape NumPy arrays in this hands-on data analysis tutorial. Master essential array manipulation skills with step-by-step examples, troubleshooting tips, and what to study next.
Focus: index, slice, and reshape numpy arrays
Ever found yourself staring at a 10,000-row dataset, needing to pull out just the rows where sales spiked, or forced to reshape a flat array into a matrix so you can feed it into a machine-learning model? Without mastering how to index, slice, and reshape NumPy arrays, you'll end up writing slow, ugly Python loops that crawl through your data. By the end of this lesson, you'll manipulate arrays like a pro—fast, clean, and with total confidence.
The Problem This Lesson Solves
Raw datasets are rarely in the shape you need. You might have a flat time series that needs to become a 2D matrix for a heatmap, or a 3D array where you only want one slice. If you're using pure Python lists, operations like extracting a column or rearranging dimensions require nested loops and verbose code that's slow and error-prone.
NumPy's indexing and slicing give you a concise way to pull out exactly the elements you want, while reshaping lets you change array dimensions without copying data. Without these tools, even simple data cleaning becomes a chore.
Core Concept / Mental Model
Think of a NumPy array as a grid of values with a fixed shape. Indexing is like using coordinates to find a single cell (e.g., row 2, column 3). Slicing is like cutting out a rectangular subset of that grid. And reshaping is like rearranging the grid's dimensions while keeping the same underlying elements.
Here's a mental diagram:
Array shape (3, 4):
[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]]
Index arr[1, 2] -> 6
Slice arr[1:, 1:3] -> [[5, 6], [9, 10]]
Reshape to (2, 6) -> [[0,1,2,3,4,5], [6,7,8,9,10,11]]
Keep in mind: indexing and slicing are about reading or writing specific regions. Reshaping changes the layout of the array's dimensions (but not the total number of elements). Once you see arrays as grids with positions, these operations become intuitive.
How It Works Step by Step
Let's break down each operation.
Indexing: Zero-Based Coordinates
Just like Python lists, NumPy indexes are zero-based. For a 1D array, arr[0] is the first element. For 2D, arr[i, j] is the element at row i and column j. You can also use negative indices to count from the end (arr[-1] is the last element).
Slicing: Extracting Subarrays
Slicing uses the syntax arr[start:stop:step]. Return a view (not a copy) of the original array — that means changes to the slice affect the original array. For multi-dimensional arrays, you apply slicing per dimension with commas: arr[rows_start:rows_stop, cols_start:cols_stop].
Reshaping: Changing Dimensions
arr.reshape(new_shape) returns a new view (if possible) with a different shape, but same number of elements. The order of elements follows row-major (C-style) order by default. You can omit one dimension with -1, and NumPy will infer it.
Step-by-Step Workflow
- Import NumPy and create your array.
- Index to pull scalar values or use boolean masks for conditional selection.
- Slice to extract contiguous blocks or steps.
- Reshape when you need to align dimensions for operations like matrix multiplication or feeding into a model.
Hands-On Walkthrough
Now let's put it all into practice. We'll create a sample dataset and apply indexing, slicing, and reshaping.
1. Creating and Indexing a 2D Array
import numpy as np
# Create a 3x4 array
arr_2d = np.arange(12).reshape(3, 4)
print("Original array:\n", arr_2d)
# Indexing: get element at row 1, column 2
print("Element at [1,2]:", arr_2d[1, 2])
# Negative indexing: last element
print("Last element:", arr_2d[-1, -1])
Expected output:
Original array:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
Element at [1,2]: 6
Last element: 11
2. Slicing: Extracting Subarrays
# Slice: rows 1 to 2, columns 1 to 2 (not including row 3, col 3)
sub = arr_2d[1:3, 1:3]
print("Subarray rows 1-2, cols 1-2:\n", sub)
# Slice with step: every other element in a 1D array
arr_1d = np.array([10, 20, 30, 40, 50])
print("Every other:", arr_1d[::2])
Expected output:
Subarray rows 1-2, cols 1-2:
[[ 5 6]
[ 9 10]]
Every other: [10 30 50]
3. Reshaping and Resizing
# Reshape a 1D array of 12 elements into a 3x4 matrix
flat = np.arange(12)
matrix = flat.reshape(3, 4)
print("Reshaped to (3,4):\n", matrix)
# Use -1 to infer dimension
inferred = flat.reshape(2, -1) # 2 rows, 6 columns
print("Reshaped with -1:\n", inferred)
# Flatten back to 1D
print("Flattened:", matrix.flatten())
Expected output:
Reshaped to (3,4):
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
Reshaped with -1:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
Flattened: [ 0 1 2 3 4 5 6 7 8 9 10 11]
Pro tip: Always check the
shapeattribute after reshaping to confirm you got the dimensions you expect. Remember that-1is a wildcard, but only one can be used per reshape call.
4. Boolean Indexing for Conditional Selection
# Select all elements greater than 5
mask = arr_2d > 5
print("Boolean mask:\n", mask)
print("Values > 5:", arr_2d[mask])
# Use boolean indexing to replace values
arr_2d[arr_2d > 5] = 0
print("After replacing >5 with 0:\n", arr_2d)
Expected output:
Boolean mask:
[[False False False False]
[False False True True]
[ True True True True]]
Values > 5: [ 6 7 8 9 10 11]
After replacing >5 with 0:
[[0 1 2 3]
[4 5 0 0]
[0 0 0 0]]
Boolean indexing is a staple in data analysis — it lets you filter rows or columns based on a condition without writing a single loop.
Compare Options / When to Choose What
| Operation | Use Case | Example | Result Type |
|---|---|---|---|
arr[i, j] |
Get a single element | arr[1, 2] |
Scalar |
arr[row_slice, col_slice] |
Extract a submatrix | arr[1:3, 2:] |
View (or copy) |
arr.reshape(shape) |
Change dimensions without changing data | arr.reshape(2, 6) |
View (if possible) |
arr.flatten() |
1D copy of array | arr.flatten() |
Copy |
arr.ravel() |
1D view (when possible) | arr.ravel() |
View |
| Boolean mask | Conditional selection | arr[arr > 5] |
Copy of selected elements |
When to use reshape vs resize? reshape returns a new array (or view) while resize modifies the array in-place and can change the total number of elements. Use reshape when you want to keep the original intact; use resize if you truly need to alter the original.
Troubleshooting & Edge Cases
Common Error: ValueError: cannot reshape array of size 12 into shape (2,5)
This happens when the requested shape doesn't multiply to the same total element count. Always verify arr.size and make sure new_shape matches. Use -1 to let NumPy figure out the missing dimension.
Edge Case: Slicing Returns a View That Modifies the Original
Beginner trap: you slice an array, then modify the slice, and suddenly the original changes. Remember that slicing (with no fancy indexing) returns a view. If you need an independent copy, use .copy() explicitly.
Edge Case: Negative Step Slices
Using a negative step like arr[::-1] reverses the array. This is handy but can be confusing when mixed with start/stop — remember that stop is exclusive and the slice moves backward.
Edge Case: Empty Slices
If start >= stop with a positive step, you get an empty array. This isn't an error, but it can silently produce missing data if you're not careful. Add checks or assertions in your code when the slice bounds are dynamic.
What You Learned & What's Next
You've now unlocked the power to index into arrays, slice out exact portions, and reshape your data to fit any analysis or machine-learning pipeline. You also learned how boolean masking acts as a supercharged filter for your data. These skills are the foundation of efficient data manipulation.
Next up, we'll dive into aggregations and statistical operations in NumPy — learning how to compute means, sums, and other summary statistics across axes. With your indexing and reshaping skills, you'll be ready to analyze large datasets with speed and clarity.
Practice recap
Try this: create a 6×6 array with np.arange(36).reshape(6,6). Slice out the 3×3 block in the top‑right corner, then reshape the entire array into a 12×3 matrix. Finally, use a boolean mask to replace every element greater than 30 with -1. Check the shape after each operation to confirm your understanding.
Common mistakes
- Forgot that slicing returns a view, not a copy—modifying the slice changes the original array. Always use
.copy()if you need a separate array. - Using
reshapeon an array whose total element count doesn't match the new shape → leads to aValueError. Remember-1infers one dimension. - Mixing negative and positive steps incorrectly in slices (e.g.,
arr[5:1]) produces empty arrays. Keep step direction in mind.
Variations
- Use fancy indexing (lists of indices) like
arr[[0, 2, 3], :]for non-contiguous row selection. - Leverage
np.newaxisorNoneto add dimensions for broadcasting, e.g.,arr[:, None]. - Prefer
array.ravel()overflatten()when you want a view and memory efficiency matters.
Real-world use cases
- Extracting a specific time window (rows) from a sensor data array for anomaly detection.
- Reshaping a flattened pixel array into a 2D image for visualisation or CNN input.
- Filtering rows in a financial dataset using boolean masks to find transactions above a threshold.
Key takeaways
- Indexing uses zero‑based coordinates;
arr[i, j]grabs a single element, negative indices count from the end. - Slicing extracts contiguous subarrays and returns a view—always decide if a copy is needed.
- Reshaping changes array dimensions but keeps total element count; use
-1to let NumPy infer a dimension. - Boolean masks enable conditional selection and in‑place substitution without loops.
- Always verify the shape and element count before reshaping to avoid silent bugs.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.