NumPy cheat sheet

A scannable NumPy reference: 24 short snippets across 12 topics, each linking back to the lesson it came from.

At a glance

TopicWhat it covers
NumPy arraysA Python list stores pointers to objects scattered in memory. A NumPy ndarray stores fixed-size numbers in onelesson
Indexing, slicing and masksA slice of an array is a view onto the same memory, not a copy. Changing the slice changes the original. .copy() breakslesson
Vectorised maths and broadcastingThese are ufuncs: functions that operate element by element at C speed and support an out= parameter so you can writelesson
Reshaping, stacking and splittingreshape returns a new view when the data is contiguous, so it is cheap. -1 means "work this dimension out for me"lesson
Views, copies and memory layoutTwo arrays can point at the same bytes. When they do, one is a view of the other: writing through the view changes thelesson
Sorting, searching and set operationsnp.sort returns a sorted copy; arr.sort() sorts in place. When you need the permutation rather than the values, uselesson
Linear algebra with numpy.linalgThe single most expensive misunderstanding in NumPy: * multiplies element by element, while @ performs a matrixlesson
Random number generation with the Generator APInp.random.default_rng() returns an explicit Generator object. It has better statistical properties than the legacylesson
File I/O: save, load, npz and memory-mapped arraysNumPy's own formats store dtype, shape and order alongside the bytes, so a round trip is exact. CSV cannot do that: itlesson
Structured arrays, datetimes and string dtypesA structured array stores records with named fields in one contiguous buffer. It is the closest NumPy gets to a tablelesson
Performance: einsum, ufunc tricks and avoiding copiesIn einsum you write the index letters of the inputs, then an arrow and the letters you want to keep. Repeated letterslesson
Debugging array code: shape errors and NumPy 2 pitfallsThe message operands could not be broadcast together with shapes (3,4) (4,3) is a complete diagnosis if you read it thelesson

Quick snippets

NumPy arrays

Shape, ndim and dtype

import numpy as np

a = np.array([1, 2, 3])            # shape (3,), dtype int64
b = np.array([[1, 2, 3], [4, 5, 6]])  # shape (2, 3)

a.shape      # (3,)
a.ndim       # 1
a.dtype      # dtype('int64')
a.size       # 3  -> total elements
b.shape      # (2, 3)  rows, columns

Shape, ndim and dtype

np.zeros((2, 3))          # all zeros
np.ones((2, 3))           # all ones
np.full((2, 2), 7)        # filled with 7
np.eye(3)                 # identity
np.arange(0, 10, 2)       # 0 2 4 6 8
np.linspace(0, 1, 5)      # 5 evenly spaced values
np.random.default_rng(0).normal(size=(2, 2))

dtypes and precision

np.array([1, 2, 3], dtype=np.float64)
np.array([1.7, 2.9]).astype(np.int64)   # truncates toward zero: [1, 2]

a = np.arange(5)
a.mean()      # 2.0  - float, because mean can be fractional
a.sum()       # 10
a.max(), a.min(), a.std()

Full lesson: NumPy arrays →

Indexing, slicing and masks

Slicing returns views

a = np.arange(10)
a[2:5]           # array([2, 3, 4])
a[::-1]          # reversed
a[::2]           # every other element

v = a[0:3]
v[0] = 99
a[0]             # 99  -> the slice was a view

c = a[0:3].copy()
c[0] = -1
a[0]             # still 99

Slicing returns views

m = np.arange(12).reshape(3, 4)
m[1, 2]          # row 1, column 2
m[1]             # entire row 1
m[:, 1]          # entire column 1
m[0:2, 1:3]      # block: rows 0-1, columns 1-2

Boolean masks and fancy indexing

scores = np.array([88, 42, 95, 67, 71])

scores > 70                      # array([ True, False,  True, False,  True])
scores[scores > 70]              # array([88, 95, 71])
scores[(scores > 60) & (scores < 90)]   # use & | ~ with parentheses

scores[scores < 70] = 0          # conditional assignment
np.where(scores > 70, "pass", "fail")

Full lesson: Indexing, slicing and masks →

Vectorised maths and broadcasting

Element-wise operations

a = np.array([1, 2, 3])
b = np.array([10, 20, 30])

a + b          # array([11, 22, 33])
a * 2          # array([2, 4, 6])
a ** 2         # array([1, 4, 9])

np.sqrt(a)
np.exp(a)
np.log(a)

a.sum(), a.prod(), a.mean(), a.std(), a.cumsum()

Element-wise operations

out = np.empty_like(a, dtype=float)
np.multiply(a, 2.5, out=out)     # no new allocation

Broadcasting rules

m = np.ones((3, 4))
v = np.array([1, 2, 3, 4])

m + v            # (3,4) + (4,)  -> v broadcast across rows ✅
# m + np.array([1, 2, 3])   # (3,4) + (3,) -> ValueError ❌

col = np.array([[10], [20], [30]])   # shape (3, 1)
m + col          # one value per row

# normalise each column to 0..1
norm = (m - m.min(axis=0)) / (m.max(axis=0) - m.min(axis=0))

Full lesson: Vectorised maths and broadcasting →

Reshaping, stacking and splitting

Reshape and transpose

a = np.arange(12)

a.reshape(3, 4)
a.reshape(3, -1)        # -1 -> 4
a.reshape(-1, 6)        # -1 -> 2
a.ravel()               # flatten (view when possible)

m = np.arange(6).reshape(2, 3)
m.T                     # transpose: (3, 2)
m.T.shape
m.flatten()             # always a copy

Stacking and concatenating

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

np.concatenate([a, b])       # array([1, 2, 3, 4, 5, 6])
np.stack([a, b])             # shape (2, 3)
np.column_stack([a, b])      # shape (3, 2) - a tidy two-column table

Splitting and masking shapes

m = np.arange(12).reshape(3, 4)

np.split(m, 3)               # three (1, 4) pieces
np.hsplit(m, 2)              # split columns
top, bottom = np.vsplit(m, [2])   # rows 0-1, then 2

np.newaxis                   # add an axis
v = np.array([1, 2, 3])
v[:, None].shape             # (3, 1)
v[None, :].shape             # (1, 3)

Full lesson: Reshaping, stacking and splitting →

Views, copies and memory layout

C order, Fortran order and strides

a = np.arange(6, dtype=np.int64).reshape(2, 3)
a.strides                    # (24, 8) - bytes per step along each axis
a.T.strides                  # (8, 24) - the transpose is just a view
a.T.flags["C_CONTIGUOUS"]    # False

np.ascontiguousarray(a.T)    # force a real copy into C order
np.asfortranarray(a)         # column-major copy for BLAS-friendly code
a[:, 1].strides              # (24,) - a column slice, not contiguous

Where the copies come from

m = np.ones((1000, 1000))

m.ravel()               # view when the array is contiguous
m.flatten()             # always a copy
m.T @ m                 # no copy: transposes feed BLAS directly
m.astype(np.float32)    # always a copy: the dtype changes
m[m > 0.5]              # copy: boolean mask output
np.asarray(m) is m      # True - no copy when it is already an array
np.array(m, copy=False) # NumPy 2 raises if a copy would be required

Full lesson: Views, copies and memory layout →

Sorting, searching and set operations

Searching in sorted data

grades = np.array([10, 20, 30, 40])   # must be sorted

np.searchsorted(grades, 25)                    # 2 - insert position
np.searchsorted(grades, [5, 35, 40], side="right")   # array([0, 3, 4])

# bucket thousands of scores into grade bands in one call
scores = np.array([7, 22, 41, 15])
band = np.searchsorted(grades, scores, side="right")   # array([0, 1, 3, 1])

np.nonzero(a > 1)          # tuple of index arrays, one per dimension
np.where(a > 1)            # same thing for a single condition

Full lesson: Sorting, searching and set operations →

Linear algebra with numpy.linalg

matmul, dot and the asterisk

A = np.array([[3.0, 1.0], [1.0, 2.0]])
B = np.array([[1.0, 0.0], [0.0, 1.0]])

A * B          # element-wise, shape (2, 2)
A @ B          # matrix product - use this
np.matmul(A, B)
A.dot(B)       # same result for 2-D inputs

v = np.array([1.0, 2.0])
A @ v                    # matrix times vector -> shape (2,)
np.inner(v, v)           # 5.0
np.outer(v, v)           # shape (2, 2) outer product

Decompositions and norms

S = np.array([[2.0, 1.0], [1.0, 2.0]])

vals, vecs = np.linalg.eig(S)      # general eigenvalues
vals, vecs = np.linalg.eigh(S)     # symmetric/Hermitian: faster and real
U, s, Vt = np.linalg.svd(M, full_matrices=False)   # singular values
L = np.linalg.cholesky(S)          # S = L @ L.T for positive-definite S

np.linalg.norm(v)                  # Euclidean length
np.linalg.norm(M, axis=0)          # per-column norms
np.linalg.matrix_rank(M)
np.linalg.cond(A)                  # large condition number means unstable
np.linalg.det(A)

Full lesson: Linear algebra with numpy.linalg →

Random number generation with the Generator API

Choosing a distribution

rng.normal(0, 1, 1000)        # heights, measurement error
rng.poisson(3.0, 1000)        # counts of rare events per interval
rng.exponential(2.0, 1000)    # waiting times between events
rng.beta(2, 5, 1000)          # proportions and rates in [0, 1]
rng.lognormal(0, 0.5, 1000)   # multiplicative effects, incomes
rng.multinomial(10, [0.2, 0.8], size=3)   # dice-like draws

rng.random(3) < 0.3           # Bernoulli with p = 0.3
rng.integers(1, 7, size=10)   # a fair die, values 1 to 6

Full lesson: Random number generation with the Generator API →

File I/O: save, load, npz and memory-mapped arrays

Text files

np.savetxt("data.csv", a, delimiter=",", fmt="%.3f",
           header="a,b,c", comments="")

np.loadtxt("data.csv", delimiter=",", skiprows=1)   # numeric, fast, all-or-nothing

# tolerant version: handles missing values, headers and mixed types
t = np.genfromtxt("data.csv", delimiter=",", names=True,
                  dtype=None, encoding="utf-8")
t["a"]

d = np.loadtxt("data.csv", delimiter=",", skiprows=1,
               usecols=(0, 2), max_rows=100)

Arrays bigger than memory

# create a file-backed array, then write through it in blocks
mm = np.memmap("big.dat", dtype=np.float32, mode="w+", shape=(10000, 10000))
for start in range(0, 10000, 500):
    mm[start:start + 500] = 0.0
mm.flush()

# later, and on a machine without enough RAM to hold it all
mm = np.memmap("big.dat", dtype=np.float32, mode="r", shape=(10000, 10000))
mm[42, :10]          # only the pages touched are read from disk
mm[:, 3].mean()      # a column still walks the whole file: slow but possible

Full lesson: File I/O: save, load, npz and memory-mapped arrays →

Structured arrays, datetimes and string dtypes

Structured arrays

dt = np.dtype([("name", "U10"), ("age", "i4"), ("score", "f8")])
rows = np.array([("ada", 36, 91.5), ("bob", 41, 78.0)], dtype=dt)

rows["age"]                       # array([36, 41], dtype=int32)
rows[rows["age"] > 38]            # boolean mask over a field
rows["score"].mean()              # 84.75
np.sort(rows, order="score")      # sort records by a field

rows[0]                           # a record, not a view
rows[0]["age"] = 37               # writes through to the buffer
rows["score"] = rows["score"] + 1 # update one field across all records

String dtypes and their traps

s = np.array(["abc", "de"], dtype="U3")   # fixed width, space padded
s.astype("U10")                 # widening is safe
np.char.upper(s)                # vectorised string operations
np.char.add(s, "_x")
np.char.str_len(s)

np.array(["long text"], dtype="U3")   # silently truncated to 'lon'
s.astype(object)                # unlimited length, much slower

Full lesson: Structured arrays, datetimes and string dtypes →

Performance: einsum, ufunc tricks and avoiding copies

einsum notation

A = rng.random((100, 200))
B = rng.random((200, 50))
M = rng.random((4, 4))
T = rng.random((3, 4, 5))

np.einsum("ij,jk->ik", A, B)     # matrix product
np.einsum("ii->i", M)            # diagonal
np.einsum("ij->ji", A)           # transpose
np.einsum("ij,ij->", A, A)       # sum of all squares
np.einsum("ij,ij->i", A, A)      # one dot product per row
np.einsum("ij,kj->ik", A, B)     # row-wise similarity matrix
np.einsum("...ij->...ji", T)     # ellipsis: batch-aware transpose

Full lesson: Performance: einsum, ufunc tricks and avoiding copies →

Debugging array code: shape errors and NumPy 2 pitfalls

What changed in NumPy 2

np.array(x, copy=False)     # NumPy 2: raises if a copy is needed
np.asarray(x)               # the portable way to say "no copy if possible"
np.array(x, copy=None)      # explicit: copy only when required

np.float_                   # removed -> np.float64
np.NaN, np.Inf              # removed -> np.nan, np.inf
np.trapz(y, x)              # renamed -> np.trapezoid(y, x)
a.ptp()                     # removed -> np.ptp(a)

print(np.float64(3.0))      # NumPy 2 repr: np.float64(3.0) rather than 3.0

Full lesson: Debugging array code: shape errors and NumPy 2 pitfalls →

FAQ

Is this NumPy cheat sheet free to use?
Yes. No sign-up and no tracking: the page is static, every example is on the page itself, and you can print it or save it as a one-page reference.
Where do the examples come from?
Every snippet is taken from the 12 lessons of the NumPy course on this site, and each section links back to the lesson it was pulled from.
How do I go deeper than a cheat sheet?
Open the full NumPy course — it carries the worked explanations, the edge cases and the exercises behind every line here.

Python 3 pandas Matplotlib Jupyter Notebook Flask FastAPI

Last refreshed 2026-09-27.