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():

  1. scores > 80 produces an array of booleans.
  2. .sum() adds them up, treating True as 1 and False as 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.

Toggle theme (T)