NumPy: Indexing, Slicing & Broadcasting
Basic Indexing & Slicing
a = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]])
# Element access
a[0, 1] # 2 (row 0, col 1)
a[-1, -1] # 12 (last row, last col)
# Slicing: [start:stop:step]
a[0, :] # [1 2 3 4] — first row
a[:, 0] # [1 5 9] — first column
a[0:2, 1:3] # [[2,3],[6,7]] — submatrix
a[::2, ::2] # [[1,3],[9,11]] — every other row and col
a[::-1] # reversed rows
# Slices are VIEWS — modifying slice modifies original
row = a[0, :] # view
row[0] = 99 # modifies a!
a.copy()[0] # explicit copy to avoid this
# 3D indexing
b = np.arange(24).reshape(2, 3, 4)
b[0, :, :] # first "sheet"
b[:, 1, :] # middle rows of all sheets
b[1, 2, 3] # scalar elementAdvanced Indexing
a = np.array([10, 20, 30, 40, 50])
# Integer array indexing (fancy indexing) — always returns copy
idx = np.array([0, 2, 4])
a[idx] # [10 30 50]
a[[0, 0, 3]] # [10 10 40] — duplicates allowed
# 2D fancy indexing
b = np.arange(16).reshape(4, 4)
rows = np.array([0, 1, 2])
cols = np.array([1, 2, 3])
b[rows, cols] # [b[0,1], b[1,2], b[2,3]] = [1, 6, 11]
# Boolean indexing — most common in data analysis
a = np.array([1, -2, 3, -4, 5])
mask = a > 0
a[mask] # [1, 3, 5] — only positive elements
a[a > 0] # same, inline
a[a < 0] = 0 # set negatives to zero IN PLACE
# np.where — conditional element selection
np.where(a > 0, a, 0) # positive as-is, others 0
np.where(a > 0, 'pos', 'neg') # string labels
# np.nonzero / np.argwhere
np.nonzero(a > 0) # tuple of index arrays
np.argwhere(a > 0) # 2D array of indicesBroadcasting
Broadcasting lets NumPy operate on arrays with different shapes without copying data. Arrays are compatible when dimensions are equal or one of them is 1.
# Broadcasting rules (right-align shapes, pad with 1s on left):
# Shape (3, 4) + shape (4,) → (3, 4) + (1, 4) → broadcast to (3, 4) ✓
# Shape (3, 1) + shape (1, 4) → broadcast to (3, 4) ✓
# Shape (3, 4) + shape (3,) → (3, 4) + (3, 1)??? — ERROR: 4 ≠ 3 and 3 ≠ 1
# Scalar broadcasts to any shape
a = np.ones((3, 4))
a * 5 # every element × 5
# 1D row vector added to each row of 2D matrix
matrix = np.arange(12).reshape(3, 4)
row = np.array([1, 2, 3, 4]) # shape (4,) → treated as (1, 4)
matrix + row # shape (3, 4) — row added to each of 3 rows
# 1D column vector added to each column
col = np.array([[1], [2], [3]]) # shape (3, 1)
matrix + col # shape (3, 4) — each row i gets col[i] added to all elements
# Practical: center data (subtract column means)
data = np.random.rand(100, 5)
means = data.mean(axis=0) # shape (5,)
centered = data - means # broadcasts: (100, 5) - (5,) → (100, 5)
# Normalize each row to unit length
norms = np.linalg.norm(data, axis=1, keepdims=True) # shape (100, 1)
normalized = data / norms # (100, 5) / (100, 1) → (100, 5)
# Outer product via broadcasting
a = np.array([1, 2, 3]) # shape (3,)
b = np.array([10, 20, 30]) # shape (3,)
a[:, np.newaxis] * b # shape (3, 1) × (3,) → (3, 3) outer productKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free