NumPy is the numerical foundation every ML library is built on. Create arrays, run vectorized math without loops, and use broadcasting to combine different shapes.
Why: NumPy’s ndarray is a fast, typed, N-dimensional grid of numbers — pandas, scikit-learn, and PyTorch all speak it. When: reach for NumPy whenever you do math on many numbers at once. Where: install once; every later lesson and course assumes it.
pip install numpyWhy: an array’s shape (its dimensions) and dtype (the element type) define everything about how it behaves and how much memory it uses. When: check shape constantly — most bugs are a shape mismatch. Where: unlike a Python list, every element shares one dtype, which is what makes NumPy fast.
import numpy as np
a = np.array([1, 2, 3, 4]) # 1-D array
m = np.array([[1, 2, 3], [4, 5, 6]]) # 2-D array (2 rows, 3 cols)
print(a.shape, a.dtype) # (4,) int64
print(m.shape, m.dtype) # (2, 3) int64
# Build arrays without typing every value:
print(np.zeros((2, 3))) # 2x3 of 0.0
print(np.arange(0, 10, 2)) # [0 2 4 6 8]
print(np.linspace(0, 1, 5)) # 5 evenly spaced points from 0 to 1Why: NumPy applies an operation to every element at once in fast C code, so you never write a Python loop over numbers — it is both shorter and orders of magnitude faster. When: any time you would loop to transform numbers, use a vectorized expression instead. Where: this is the single biggest habit shift from plain Python.
prices = np.array([10.0, 25.0, 8.0, 40.0])
# Operate on the WHOLE array at once — no loop:
with_tax = prices * 1.15
print(with_tax) # [11.5 28.75 9.2 46.]
print(prices.sum(), prices.mean(), prices.max())
print(prices > 20) # [False True False True] — element-wise
print(prices[prices > 20]) # [25. 40.] — boolean mask selectionWhy: broadcasting lets NumPy stretch a smaller array across a larger one so you can, say, subtract a per-column mean from a whole matrix without loops — the core trick behind feature scaling. When: it kicks in automatically when shapes are compatible. Where: shapes align from the right; a dimension of 1 (or missing) is stretched to match.
X = np.array([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]]) # shape (2, 3)
col_mean = X.mean(axis=0) # shape (3,) — mean of each column
print(col_mean) # [2.5 3.5 4.5]
centered = X - col_mean # (2,3) - (3,) broadcasts across rows
print(centered) # each column now has mean 0