Indexing, slicing & boolean masks
Selecting the right elements is most of what you do with arrays. NumPy offers three ways: basic indexing and slicing, boolean masks, and fancy (integer array) indexing. They behave differently with memory, which is the part that trips people up.
Basic indexing and slicing
x = np.arange(12).reshape(3, 4)
x[0, 1] # row 0, col 1
x[:, 2] # third column
x[1:, ::2] # rows 1.., every 2nd column
scores = np.array([45, 88, 67, 92, 30])
passed = scores[scores >= 50] # boolean mask
scores[scores < 50] = 50 # grace marks
Use the comma form x[0, 1], not x[0][1]. The comma form is one operation; the chained form builds a temporary
row array first.
Slice syntax is start:stop:step, with stop excluded. Negative values count from the end: x[-1] is the last
row, x[:, ::-1] reverses the columns.
Views versus copies
Basic slicing returns a view that shares memory with the original:
row = x[0]
row[0] = 999
print(x[0, 0]) # 999 — the original changed too
safe = x[0].copy() # independent
This is a feature: slicing a 2 GB array costs nothing. It becomes a bug when you did not expect it, so call
.copy() whenever you intend to modify a slice independently.
Boolean masks
A mask is an array of True/False the same shape as the data:
sales = np.array([120, 340, 90, 610, 245]) # ₹ thousand
city = np.array(["Pune", "Delhi", "Pune", "Surat", "Delhi"])
big = sales > 200
print(big) # [False True False True True]
print(sales[big].sum()) # 1195
print(sales[(city == "Pune") & (sales > 100)]) # [120]
Combine conditions with & (and), | (or) and ~ (not), and wrap each condition in brackets. Python's
and / or do not work element-wise and raise a ValueError on arrays.
| Helper | Use |
|---|---|
np.where(cond, a, b) |
Element-wise if/else |
np.where(cond) |
Indices where the condition is True |
mask.sum() |
How many rows match |
mask.any() / mask.all() |
Is any / every element True |
np.isin(a, values) |
Membership test against a list |
band = np.where(sales > 300, "high", "normal")
idx = np.where(sales > 300)[0] # array([1, 3])
print(np.isin(city, ["Pune", "Surat"]))
Fancy indexing
Pass an array of positions to pick elements in any order, with repeats allowed:
top = sales.argsort()[::-1][:3] # positions of the three largest
print(city[top], sales[top]) # ['Surat' 'Delhi' 'Delhi'] [610 340 245]
Boolean and fancy indexing always return copies, not views — so sales[big] = 0 works (NumPy handles it as an
assignment), but sales[big][0] = 0 silently modifies a throwaway copy.
Updating in place
gst = np.array([0.05, 0.12, 0.18, 0.05])
gst[gst == 0.05] = 0.00 # exempt the 5% slab
np.clip(sales, 0, 500, out=sales) # cap values in place
Common mistakes
- Using
and/orinstead of&/|on arrays. - Forgetting brackets:
city == "Pune" & sales > 100parses the&first and fails. - Editing a slice and being surprised the original changed (or did not, with a boolean mask).
- Using
x[0][1]in tight loops instead ofx[0, 1].
Practice
Build an array of 500 random exam marks and a matching array of section labels ("A" to "D"). Count marks below
35, raise them to 35 in place, select every section-B mark above the overall mean, and print the five highest
marks with their sections using argsort.