NumPy Indexing and Slicing
1NumPy Indexing: overview
NumPy indexing is how you pick values out of an array: by position and slice (arr[row, col]), by a True/False mask, or by a list of positions, and the way you pick decides whether you get a view of the original data or an independent copy.
Before this lesson: Python Slicing, NumPy Basics
2NumPy Indexing: diagram
3NumPy Indexing: explained step by step
Once your data is in an array, most of the work is choosing parts of it: one reading, a whole column, only the rows that match a rule. NumPy gives you four ways to choose:
- by position:
arr[2]orarr[1, 3] - by slice:
arr[1:4]orarr[:, 2] - by boolean mask:
arr[arr > 10] - by a list of positions (called fancy indexing):
arr[[0, 3, 3]]
They differ in one detail that causes real bugs: some hand you a view of the original data and some hand you a separate copy. You will see which is which below.
Positions and slices
A 1-D array indexes just like a list: the first item is 0, the last is -1, and a slice arr[start:stop:step] stops before stop.
import numpy as np
temps = np.array([18, 21, 24, 19, 26, 24, 20])
print(temps[0], temps[-1])
print(temps[1:4])
print(temps[::2])
print(temps[::-1])A 2-D array needs one index per dimension, separated by a comma: grid[row, column]. A slice in either place keeps that dimension, and a lone colon means "everything along this axis".
import numpy as np
grid = np.arange(1, 13).reshape(3, 4)
print(grid[1, 2])
print(grid[1])
print(grid[:, 2])
print(grid[0:2, 1:3])grid[1][2] also works, but it first builds the whole row and then indexes that row. Write grid[1, 2] instead: it is one step and it is the only form that can slice both axes at once.
A slice is a view, not a copy
Copying a million numbers every time you slice would be slow, so NumPy does not do it. A slice is just another window onto the same memory. Change the window and you change the original.
import numpy as np
prices = np.array([10, 20, 30, 40, 50])
window = prices[1:4]
window[0] = 99
print(prices)
safe = prices[1:4].copy()
safe[0] = -1
print(prices, safe)Call .copy() whenever you want an independent array you can edit freely. Masks and fancy indexing work the other way round: they always return a copy, so editing the result never touches the original.
Boolean masks in depth
A comparison gives a mask: one True or False per value. Combine masks with & (and), | (or) and ~ (not), and put parentheses around every comparison, because & binds more tightly than > or ==. Never use Python's and / or here: they need a single True or False and raise ValueError: The truth value of an array with more than one element is ambiguous.
A mask on the left of an assignment changes the matching values in place. Because True counts as 1, mask.sum() counts matches and mask.mean() gives their fraction.
import numpy as np
scores = np.array([72, -1, 90, 55, -1, 68, 101])
bad = (scores < 0) | (scores > 100)
print("Invalid:", bad.sum(), "of", scores.size)
scores[bad] = 0
print(scores)
print("Share passing:", (scores >= 60).mean())On a 2-D array a mask built from one column picks whole rows. With table[table[:, 1] > 60] the mask has one entry per row, so every row where column 1 is above 60 comes back intact.
import numpy as np
marks = np.array([[101, 72], [102, 58], [103, 91]])
print(marks[marks[:, 1] > 60])Fancy indexing and finding positions
Give an array a list of positions and you get those items, in that order, repeats allowed. Sometimes you need the positions themselves:
np.where(mask)returns the positions where the mask isTrue.arr.argmax()andarr.argmin()return the position of the largest and smallest value.arr.argsort()returns the positions that would sort the array. Use them to reorder another array, such as names ordered by score.np.sort(arr)returns a sorted copy, andarr.sort()sorts in place.
import numpy as np
names = np.array(["Asha", "Ben", "Chen", "Dina"])
scores = np.array([78, 45, 91, 62])
print(names[[2, 0, 2]])
print(np.where(scores > 60)[0])
print(names[scores.argsort()])
print(names[scores.argmax()])When the index is out of bounds
A single position that does not exist raises IndexError: index 5 is out of bounds for axis 0 with size 4. The message names the axis and the size of that axis, which tells you straight away which of the indexes in grid[row, col] is too big. Slices are forgiving: arr[2:100] quietly stops at the end of the array.
Quick reference: NumPy Indexing
- Python NumPy Cheat Sheet — NumPy stores numbers in fixed-type arrays and applies operations to every element at once: build with `np.arr…
4NumPy Indexing: syntax
import numpy as np
arr[i] # one item (negative i counts from the end)
arr[start:stop:step] # slice -> VIEW of the same memory
grid[row, col] # one value in a 2-D array
grid[:, col] # a whole column
grid[row, :] # a whole row
grid[r1:r2, c1:c2] # a block
view = arr[1:4] # edits to view change arr
safe = arr[1:4].copy() # independent copy
arr[arr > 10] # boolean mask -> COPY of the matches
arr[(a > 1) & (a < 9)] # combine with & | ~ (parentheses!)
arr[mask] = 0 # assign through a mask
mask.sum() # count the True values
arr[[0, 3, 3]] # fancy indexing (list of positions) -> COPY
np.where(mask)[0] # positions where mask is True
arr.argmax(), arr.argmin()
arr.argsort() # positions that would sort arr
np.sort(arr) # sorted copy
5NumPy Indexing: run the example
Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.
import numpy as np
# rows = shops, columns = Monday to Friday
sales = np.array([[12, 15, 11, 14, 20],
[25, 27, 26, 30, 28],
[ 8, 9, 14, 10, 13],
[19, 22, 18, 21, 24]])
shops = np.array(["North", "South", "East", "West"])
print("Friday, every shop:", sales[:, 4])
print("East, Wednesday to Friday:", sales[2, 2:])
weekly = sales.sum(axis=1)
print("Busy shops:", shops[weekly > 80])
print("Best first:", shops[np.argsort(weekly)[::-1]])
sales[sales < 12] = 12
print("Lowest day now:", sales.min())Expected outputFriday, every shop: [20 28 13 24]
East, Wednesday to Friday: [14 10 13]
Busy shops: ['South' 'West']
Best first: ['South' 'West' 'North' 'East']
Lowest day now: 12
7NumPy Indexing: how the example works
Hover or tap a step to highlight its lines in the example above.
- Line 3-7A 2-D array: each inner list is one shop (a row) and each position is a weekday (a column). The comment above it says which axis means what, which is worth writing down for any grid.
- Line 8
shopsis a separate 1-D array of names. Row 0 ofsalesbelongs toshops[0], row 1 toshops[1], and so on. - Line 9
sales[:, 4]means every row, column 4: Friday's figure for each shop. The result is a 1-D array of 4 numbers. - Line 10
sales[2, 2:]is row 2 (East), columns 2 to the end: Wednesday, Thursday and Friday. - Line 11
sum(axis=1)adds across each row, giving one weekly total per shop: 72, 136, 54 and 104. - Line 12
weekly > 80is a mask with one True/False per shop. Putting that mask insideshops[...]keeps the names of the shops where it is True. - Line 13
argsortreturns the shop positions from smallest to largest weekly total.[::-1]reverses them, and using those positions as an index reorders the shop names, best first. - Line 15A mask on the left of an assignment changes the array in place: every value below 12 becomes 12. This is a copy-free way to set a floor.
- Line 16
min()is now 12 because no value is lower than the floor. The original array was modified, which is why masks on the left of=need care.
8NumPy Indexing: common mistakes
Indexing past the end of an axis
✗ Wrong
import numpy as np
grid = np.zeros((3, 4))
print(grid[3, 0])✓ Right
import numpy as np
grid = np.zeros((3, 4))
print(grid[-1, 0])The message names the axis and its size. grid has 3 rows (axis 0), so valid row indexes are 0, 1 and 2. Use grid[2, 0] or grid[-1, 0] for the last row. Remember that columns are axis 1, with 4 positions here.
Forgetting the parentheses around each comparison
✗ Wrong
import numpy as np
temps = np.array([12, 18, 25, 31])
print(temps[temps > 15 & temps < 30])✓ Right
import numpy as np
temps = np.array([12, 18, 25, 31])
print(temps[(temps > 15) & (temps < 30)])& is evaluated before > and <, so Python tries to compute 15 & temps first and then chains the comparisons. Wrap every comparison in its own parentheses.
Editing a slice and changing the original
✗ Wrong
import numpy as np
scores = np.array([50, 60, 70, 80])
first_two = scores[:2]
first_two[0] = 0
print(scores)✓ Right
import numpy as np
scores = np.array([50, 60, 70, 80])
first_two = scores[:2].copy()
first_two[0] = 0
print(scores)A slice is a view onto the same memory, so writing through it writes to the original. Call .copy() when you need data you can change safely.
Assigning to a mask result in two steps
✗ Wrong
import numpy as np
scores = np.array([40, 75, 30, 90])
scores[scores < 50][0] = 50
print(scores)✓ Right
import numpy as np
scores = np.array([40, 75, 30, 90])
scores[scores < 50] = 50
print(scores)scores[scores < 50] makes a copy, and [0] = 50 then edits that temporary copy, which is thrown away. Put the mask directly on the left of the assignment instead, so NumPy writes into the original.
Reading argsort() as if it returned sorted values
✗ Wrong
import numpy as np
scores = np.array([70, 90, 60])
print(scores.argsort())✓ Right
import numpy as np
scores = np.array([70, 90, 60])
order = scores.argsort()
print(scores[order])argsort() returns the positions that would put the array in order. Use them as an index to get the sorted values, or call np.sort(scores) directly.
9NumPy Indexing: exercises
Write your answer, then press Check. The checks run in your browser.
1. Slice a seating grid
Write code Easy ✓ Completedseats is a 2-D array: each row is a row of seats in a hall. Write two functions:
back_row(grid)returns the last row ofgrid.first_two_columns(grid)returns the first two columns of every row, so its shape is(number_of_rows, 2).
Both must work for a grid of any size.
import numpy as np
seats = np.arange(1, 21).reshape(4, 5)
def back_row(grid):
pass
def first_two_columns(grid):
pass
print(back_row(seats))
print(first_two_columns(seats))import numpy as np
seats = np.arange(1, 21).reshape(4, 5)
def back_row(grid):
return grid[-1]
def first_two_columns(grid):
return grid[:, :2]
print(back_row(seats))
print(first_two_columns(seats))
2. Predict views, copies and fancy indexing
Predict the output Medium ✓ CompletedRead the code and type exactly what it prints. Think about which lines change prices and which only change a copy.
import numpy as np
prices = np.array([10, 20, 30, 40, 50])
cheap = prices[:3]
cheap[0] = 99
safe = prices[1:4].copy()
safe[0] = -1
print(prices)
print(safe)
print(prices[[4, 0, 4]])
print(prices[prices > 25].sum())cheap is a slice, so it shares memory with prices. safe is a copy. The list [4, 0, 4] picks positions 4, 0 and 4 of the updated prices.[99 20 30 40 50]
[-1 30 40]
[50 99 50]
219
2 more exercises → 6 more examples
Questions learners ask
Why did changing my slice also change the original NumPy array?
Slices are views: they look at the same memory as the original instead of copying it, which keeps slicing fast. Writing through the slice therefore writes to the original. Use arr[1:4].copy() when you want an independent array. Boolean masks and fancy indexing always return copies.
What is the difference between arr[1][2] and arr[1, 2] in NumPy?
Both read the same value in a 2-D array, but arr[1][2] first builds the whole row arr[1] and then indexes it, while arr[1, 2] does it in one step. Only the comma form can slice both axes at once, for example arr[0:2, 1:3], so use it as your default.
Should I use a boolean mask or np.where to find matching values?
Use the mask itself (arr[arr > 5]) when you want the matching values or rows. Use np.where(mask)[0] when you need their positions, for example to look up the same positions in another array. np.where(mask, a, b) is a third form that picks a where the mask is True and b elsewhere.
Further reading
10Next lesson
Up nextNumPy Math and BroadcastingNumPy maths applies functions and summaries to whole arrays, and broadcasting lets arrays of different shapes work toge…Related:
Python ListsPython SlicingNumPy BasicsNumPy Math and Broadcasting
← NumPy Basics · All Data Analysis lessons · Practise in the IDE