You’ve made it through core Python. This section bridges into NumPy — the foundation of every Python scientific and ML library. NumPy’s central type is the array, a fixed-size grid of numbers that supports very fast maths.
Install NumPy
If you’ve been following along with uv, add NumPy to your project:
uv add numpy
Then in your script:
import numpy as np
np is the universal nickname. Don’t import it any other way.
Creating arrays
import numpy as np
a = np.array([1, 2, 3, 4, 5])
print(a) # [1 2 3 4 5]
print(type(a)) # <class 'numpy.ndarray'>
np.array turns any Python sequence (a list, a tuple) into an array.
Some helpers:
np.zeros(5) # array of zeros [0. 0. 0. 0. 0.]
np.ones(5) # array of ones [1. 1. 1. 1. 1.]
np.full(5, 7) # array filled with 7 [7 7 7 7 7]
np.arange(0, 10, 2) # like range, returns array [0 2 4 6 8]
np.linspace(0, 1, 5) # 5 evenly spaced values [0. 0.25 0.5 0.75 1.]
np.random.rand(5) # 5 random floats in [0, 1)
Array attributes
Every array carries information about its shape:
a = np.array([[1, 2, 3], [4, 5, 6]])
print(a.shape) # (2, 3) — 2 rows, 3 columns
print(a.ndim) # 2 — number of dimensions
print(a.size) # 6 — total number of elements
print(a.dtype) # int64 — data type of elements
The shape is a tuple: (rows, cols) for a 2D array, (depth, rows, cols) for 3D.
Multi-dimensional arrays
NumPy isn’t limited to one dimension. You can build matrices, cubes, and beyond:
matrix = np.array([
[1, 2, 3],
[4, 5, 6],
])
print(matrix.shape) # (2, 3)
cube = np.zeros((2, 3, 4)) # 2x3x4 cube of zeros
print(cube.shape) # (2, 3, 4)
In ML, you’ll see:
- 1D — a vector of features
- 2D — a batch of vectors (matrix)
- 3D — a batch of images (grayscale)
- 4D — a batch of colour images (with RGB channels)
The exact same operations work on all shapes.
dtype — the type of elements
Every NumPy array has a single element type. Common ones:
np.array([1, 2, 3]).dtype # int64
np.array([1.0, 2.0, 3.0]).dtype # float64
np.array([True, False]).dtype # bool
# specify explicitly
a = np.array([1, 2, 3], dtype=np.float32)
print(a.dtype) # float32
For ML, float32 is the most common type — small enough for performance, precise enough for training.
Reshaping
Change an array’s shape without changing its data:
a = np.arange(12) # 1D, 12 elements
print(a.shape) # (12,)
b = a.reshape(3, 4) # 2D, 3 rows × 4 cols
print(b)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
c = a.reshape(2, 2, 3) # 3D
print(c.shape) # (2, 2, 3)
The total number of elements must stay the same. 12 = 3 × 4 = 2 × 2 × 3.
You can use -1 to mean “figure this dimension out”:
a = np.arange(12)
b = a.reshape(3, -1) # NumPy infers the other side as 4
print(b.shape) # (3, 4)
From and to lists
Converting back to a Python list:
a = np.array([1, 2, 3])
print(a.tolist()) # [1, 2, 3]
Useful when you need to interop with JSON or non-NumPy code.
A small example
import numpy as np
scores = np.array([78, 92, 65, 85, 90])
print(f"Mean: {scores.mean()}")
print(f"Max: {scores.max()}")
print(f"Min: {scores.min()}")
print(f"Stdev: {scores.std():.2f}")
print(f"Above 80: {(scores > 80).sum()}")
Mean: 82.0
Max: 92
Min: 65
Stdev: 9.78
Above 80: 3
The interesting line is (scores > 80).sum():
scores > 80produces an array of booleans..sum()adds them up, treatingTrueas 1 andFalseas 0.
That’s the result: how many scores are above 80. Three lines of equivalent plain-Python logic, in one expression.
What’s next
You can build arrays. Next, the real reason NumPy exists — array operations, fast element-wise maths.