Milan Ghimire

Learning NumPy: Arrays and Vectorization

My notes on NumPy - the ndarray, why vectorization beats Python loops, and how broadcasting lets arrays of different shapes work together.

Why NumPy

NumPy is the foundation almost every ML library is built on. Its big idea is the ndarray - an N-dimensional array stored in one contiguous block of memory, so maths over millions of numbers happens in fast compiled code instead of slow Python loops.

Creating arrays

import numpy as np

a = np.array([1, 2, 3])          # 1D
b = np.zeros((2, 3))             # 2x3 of zeros
c = np.arange(0, 10, 2)          # [0 2 4 6 8]
d = np.linspace(0, 1, 5)         # 5 evenly spaced points

a.shape      # (3,)
a.dtype      # dtype of the elements

Vectorization: stop writing loops

The habit shift from plain Python: do maths on whole arrays at once instead of element by element. It is shorter and much faster.

# slow, Python-style
result = [x ** 2 for x in range(1000)]

# fast, NumPy-style
arr = np.arange(1000)
result = arr ** 2          # applied to every element at once

This is what people mean by "vectorized" code - one operation over the whole array, no explicit loop.

Broadcasting: the part worth understanding

Broadcasting is how NumPy handles operations on arrays of different shapes. The smaller array is "stretched" across the larger one without actually copying data.

prices = np.array([100, 200, 300])
prices * 1.13          # add 13% tax to every element -> scalar broadcast

matrix = np.ones((3, 3))
row    = np.array([1, 2, 3])
matrix + row           # row is added to every row of the matrix

The rule I memorised: compare shapes from the right; dimensions must be equal or one of them must be 1. That single rule explains most "shapes not aligned" errors I used to get.

What I keep using

  • Slicing: arr[2:5], matrix[:, 0] (a whole column).
  • Aggregations: arr.sum(), arr.mean(axis=0) - the axis argument chooses which dimension to collapse.
  • Reshaping: arr.reshape(2, 5) - same data, new shape (this is exactly the .view(-1, 320) idea I later met in PyTorch).

A living note - refined as I do more numerical work.