You have the linear algebra. NumPy for machine learning is how you actually run it. Every ML library in Python — scikit-learn, PyTorch, TensorFlow — is built on NumPy arrays or something that behaves like them.
Why NumPy instead of Python lists
A Python list can hold anything, so every element carries type information and lives wherever memory allows. A NumPy array holds one type in one contiguous block, which lets operations run in compiled C rather than interpreted Python.
Shape is the concept that matters
Every array has a shape — a tuple giving its size along each dimension. Most errors you will hit are shape errors, so it is worth building the habit of checking early and often.
| Shape | Dimensions | In machine learning this is… |
|---|---|---|
() | 0 | A scalar — a single loss value |
(4,) | 1 | A vector — one row’s features |
(100, 4) | 2 | A matrix — your whole dataset: 100 rows, 4 features |
(32, 28, 28) | 3 | A batch of 32 greyscale images |
The convention throughout machine learning is rows are samples, columns are features. A dataset of 100 houses with 4 measurements each has shape (100, 4) — never (4, 100).
Broadcasting: the idea worth understanding properly
Broadcasting is NumPy stretching a smaller array across a larger one so their shapes line up, without ever copying the data.

This is not a curiosity — it is how feature scaling works. Subtracting a (4,) array of column means from a (100, 4) dataset subtracts the right mean from the right column, across all 100 rows, in one operation.
🧪 The broadcasting rule
(100, 4) and (4,) → compare 4 with 4 ✓, then 100 with nothing ✓ → works(100, 4) and (100,) → compare 4 with 100 ✗ → failsThat second case trips up nearly everyone. To subtract a value per row rather than per column, reshape to
(100, 1) first.The operations you will actually use
np.array() · np.zeros() · np.ones() · np.arange() · np.linspace() · np.random.default_rng()@ or np.matmul for matrix multiply · np.dot · .mean() · .std() · .sum()The one to internalise is axis. On a (100, 4) dataset, arr.mean(axis=0) gives 4 numbers — the mean of each column, which is what you want for scaling. arr.mean(axis=1) gives 100 numbers, the mean of each row, which is almost never what you want. The rule: axis is the dimension that disappears.
Vectorise instead of looping
⚠️ If you are writing a for-loop over an array, stop
arr ** 2. Instead of looping to find values above a threshold, write arr[arr > 5].Boolean indexing like that is worth learning early — it is how you filter rows, count matches, and replace outliers, all without a loop.
Two gotchas that cause real bugs
Views versus copies. Slicing an array gives you a view into the original, not a copy. Modify the slice and you modify the original. When you want independence, call .copy() explicitly.
Integer division truncates. An array created from whole numbers has an integer dtype, and dividing can silently discard the fractional part in some operations. If you are doing maths on data, make it float — np.array([1, 2, 3], dtype=float).
🔑 Key Takeaways
- NumPy for machine learning is about arrays: one type, contiguous memory, compiled speed.
- Rows are samples, columns are features — shape
(n_samples, n_features). - Broadcasting compares shapes from the right; dimensions must match or be 1.
axisis the dimension that disappears —axis=0gives per-column results.- Slices are views, not copies. Use
.copy()when you need independence.
Further reading
The official NumPy absolute beginners guide is genuinely good and takes about an hour, and the broadcasting documentation works through more shape combinations than fit here.
Where to go next
- pandas basics — the next post, for loading real datasets
- Step-by-step PCA with NumPy — all of this applied
- Matrix multiplication dimensions — the shape rule in the maths
- Start Here — the full learning path