NumPy
02 / 03

Indexing, Slicing & Broadcasting

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 element

Advanced 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 indices

Broadcasting

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 product

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free