Toolmingo
Guides11 min read

NumPy Cheat Sheet: The Complete Quick Reference

A complete NumPy cheat sheet — array creation, indexing, slicing, math, broadcasting, linear algebra, random numbers, and the most common pitfalls.

The NumPy operations you look up every time — from array creation to broadcasting, linear algebra, random sampling, and performance tips. This reference covers the full scientific Python workflow.

Quick reference

The 25 patterns that cover 90% of daily NumPy work.

Pattern Code
Import import numpy as np
Create array np.array([1, 2, 3])
Zeros np.zeros((3, 4))
Ones np.ones((2, 3))
Range np.arange(0, 10, 2)
Linspace np.linspace(0, 1, 50)
Shape a.shape(3, 4)
Reshape a.reshape(4, 3)
Flatten a.flatten()
Data type a.dtype
Cast type a.astype(np.float32)
Index a[1, 2] or a[1][2]
Slice row a[1, :]
Slice col a[:, 2]
Boolean mask a[a > 5]
Sum a.sum() / a.sum(axis=0)
Mean a.mean() / a.mean(axis=1)
Dot product a @ b or np.dot(a, b)
Stack np.vstack([a, b]) / np.hstack([a, b])
Concatenate np.concatenate([a, b], axis=0)
Unique np.unique(a)
Sort np.sort(a) / np.argsort(a)
Where np.where(a > 0, a, 0)
Random int np.random.randint(0, 10, size=(3, 3))
Random float rng.random((3, 3))

Installation and setup

pip install numpy
import numpy as np

# Always pin versions in production
# pip install numpy==2.0.0

# Check version
print(np.__version__)

Creating arrays

From Python data

# 1D array
a = np.array([1, 2, 3, 4, 5])
print(a.shape)   # (5,)
print(a.dtype)   # int64 (platform dependent)

# 2D array
m = np.array([[1, 2, 3],
              [4, 5, 6]])
print(m.shape)   # (2, 3)

# Specify dtype
f = np.array([1, 2, 3], dtype=np.float64)
c = np.array([1+2j, 3+4j], dtype=np.complex128)

Built-in constructors

np.zeros((3, 4))         # 3×4 of 0.0
np.ones((2, 3))          # 2×3 of 1.0
np.full((2, 2), 7)       # 2×2 of 7
np.eye(4)                # 4×4 identity matrix
np.empty((2, 3))         # uninitialised (fast allocation)

# Ranges
np.arange(0, 10, 2)      # [0 2 4 6 8]  (like range())
np.arange(0.0, 1.0, 0.1) # works with floats too

# Evenly spaced points
np.linspace(0, 1, 5)     # [0.   0.25 0.5  0.75 1.  ]
np.logspace(0, 3, 4)     # [1. 10. 100. 1000.]

# Like another array
np.zeros_like(m)         # same shape/dtype as m, filled 0
np.ones_like(m)
np.full_like(m, -1)

From files

# CSV → array (no header)
a = np.loadtxt("data.csv", delimiter=",")

# CSV with header → skip first row
a = np.loadtxt("data.csv", delimiter=",", skiprows=1)

# Binary format (faster)
np.save("array.npy", a)
a = np.load("array.npy")

# Multiple arrays
np.savez("arrays.npz", x=a, y=b)
data = np.load("arrays.npz")
a = data["x"]

Array properties

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

a.shape     # (2, 3)   — tuple of dimension sizes
a.ndim      # 2        — number of dimensions
a.size      # 6        — total number of elements
a.dtype     # dtype('int64')
a.itemsize  # 8        — bytes per element
a.nbytes    # 48       — total bytes (size × itemsize)

Indexing and slicing

Basic indexing

a = np.array([[10, 20, 30],
              [40, 50, 60],
              [70, 80, 90]])

a[0, 0]     # 10   — row 0, col 0
a[-1, -1]   # 90   — last row, last col
a[1, :]     # [40 50 60]  — entire row 1
a[:, 2]     # [30 60 90]  — entire col 2
a[0:2, 1:]  # [[20 30] [50 60]]  — submatrix

Fancy indexing

# Integer array indexing
rows = np.array([0, 2])
cols = np.array([1, 2])
a[rows, cols]    # [20, 90]  — pairs (0,1) and (2,2)

# Select specific rows
a[[0, 2]]        # rows 0 and 2

# Boolean indexing (masking)
mask = a > 40
a[mask]          # [50 60 70 80 90]
a[a > 40]        # same, inline

# Assign via mask
a[a < 30] = 0    # zero out elements below 30

np.where

# where(condition, value_if_true, value_if_false)
np.where(a > 50, a, 0)        # keep values > 50, else 0
np.where(a > 50, 1, -1)       # +1 / -1 flag array
indices = np.where(a == 50)    # returns (row_indices, col_indices)

Reshaping and stacking

Reshape

a = np.arange(12)       # [0 1 2 ... 11]

a.reshape(3, 4)         # 3 rows, 4 cols
a.reshape(4, 3)         # 4 rows, 3 cols
a.reshape(2, 2, 3)      # 3D array
a.reshape(-1, 3)        # -1 = infer automatically → (4, 3)

# Flatten (returns copy) vs ravel (returns view when possible)
a.flatten()             # always copy
a.ravel()               # view if possible (faster)

# Add/remove dimensions
a.reshape(1, 12)        # (1, 12)
a[np.newaxis, :]        # same — add axis at position 0
a.squeeze()             # remove dimensions of size 1

Stack and split

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

# 1D arrays → 2D
np.vstack([a, b])   # [[1 2 3] [4 5 6]]  vertical stack (row-wise)
np.hstack([a, b])   # [1 2 3 4 5 6]      horizontal stack (col-wise for 1D)

# Stack along new axis
np.stack([a, b], axis=0)   # [[1 2 3] [4 5 6]]  same as vstack
np.stack([a, b], axis=1)   # [[1 4] [2 5] [3 6]]

# General concatenate
np.concatenate([a, b], axis=0)

# Split
np.split(a, 3)              # [array([1]), array([2]), array([3])]
np.array_split(a, 4)        # uneven splits allowed
np.vsplit(m, 2)             # split matrix along rows
np.hsplit(m, 3)             # split matrix along cols

Math and statistics

Element-wise operations

a = np.array([1.0, 4.0, 9.0])
b = np.array([2.0, 2.0, 3.0])

a + b          # [3. 6. 12.]
a - b          # [-1.  2.  6.]
a * b          # [2. 8. 27.]
a / b          # [0.5 2. 3.]
a ** 2         # [1. 16. 81.]
a % b          # [1. 0. 0.]

# Universal functions (ufuncs)
np.sqrt(a)     # [1. 2. 3.]
np.abs(a)
np.exp(a)
np.log(a)      # natural log
np.log2(a)
np.log10(a)
np.sin(a)
np.cos(a)
np.ceil(a)
np.floor(a)
np.round(a, 2)
np.clip(a, 0, 5)   # clamp values to [0, 5]

Reductions

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

a.sum()              # 21  — total
a.sum(axis=0)        # [5 7 9]  — column sums
a.sum(axis=1)        # [6 15]   — row sums

a.min()              # 1
a.max()              # 6
a.argmin()           # 0  — flat index of minimum
a.argmax()           # 5  — flat index of maximum
a.argmin(axis=0)     # column-wise argmin

a.mean()             # 3.5
a.std()              # standard deviation
a.var()              # variance
np.median(a)         # 3.5
np.percentile(a, 75) # 75th percentile

np.cumsum(a, axis=1) # cumulative sum along cols
np.cumprod(a)
np.diff(a, axis=1)   # first-order differences

Broadcasting

Broadcasting lets NumPy operate on arrays with different shapes — no loops, no copies.

Rules (applied from trailing dimensions)

Shape A:  (3, 1, 4)
Shape B:      (5, 4)
Result:   (3, 5, 4)

Rule: dimensions must be equal OR one of them must be 1.

Examples

a = np.array([[1], [2], [3]])   # shape (3, 1)
b = np.array([10, 20, 30])      # shape (3,) → treated as (1, 3)

a + b
# [[11 21 31]
#  [12 22 32]
#  [13 23 33]]

# Normalise each row (subtract row mean)
m = np.array([[1, 2, 3], [4, 5, 6]])
m - m.mean(axis=1, keepdims=True)
# [[-1.  0.  1.]
#  [-1.  0.  1.]]
# keepdims=True preserves shape (2,1) so broadcasting works

Linear algebra

import numpy as np

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

# Matrix multiplication
a @ b                     # preferred syntax (Python 3.5+)
np.matmul(a, b)           # same
np.dot(a, b)              # also works for 2D

# Element-wise vs matrix multiply
a * b                     # element-wise (Hadamard product)
a @ b                     # matrix multiply

# Transpose
a.T
a.transpose()

# Linear algebra functions
np.linalg.det(a)          # determinant
np.linalg.inv(a)          # inverse
np.linalg.norm(a)         # Frobenius norm by default
np.linalg.norm(a, ord=2)  # spectral norm
np.linalg.eig(a)          # eigenvalues and eigenvectors
np.linalg.svd(a)          # singular value decomposition
np.linalg.solve(a, b)     # solve Ax = b (prefer over inv(a) @ b)

# Matrix powers
np.linalg.matrix_power(a, 3)

Random numbers

# Modern API — always prefer rng over legacy np.random.*
rng = np.random.default_rng(seed=42)   # reproducible

rng.random((3, 3))            # uniform [0, 1)
rng.integers(0, 10, size=5)   # integers in [0, 10)
rng.normal(loc=0, scale=1, size=(2, 3))   # normal distribution
rng.uniform(low=-1, high=1, size=100)
rng.choice(np.arange(10), size=5, replace=False)  # without replacement
rng.shuffle(a)                # in-place shuffle
rng.permutation(a)            # shuffled copy

# Distributions
rng.binomial(n=10, p=0.5, size=1000)
rng.poisson(lam=3, size=1000)
rng.exponential(scale=1.0, size=1000)

# Legacy API (avoid in new code)
# np.random.seed(42)          # global seed — not thread-safe
# np.random.rand(3, 3)        # same as rng.random

Data types

dtype Description Bytes
np.bool_ Boolean 1
np.int8 Signed 8-bit 1
np.int16 Signed 16-bit 2
np.int32 Signed 32-bit 4
np.int64 Signed 64-bit 8
np.uint8 Unsigned 8-bit 1
np.float16 Half precision 2
np.float32 Single precision 4
np.float64 Double precision 8
np.complex64 2× float32 8
np.complex128 2× float64 16
a = np.array([1, 2, 3])
a.astype(np.float32)     # cast
a.astype("f4")           # shorthand: f4 = float32, i4 = int32, u1 = uint8

Sorting and searching

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

np.sort(a)              # sorted copy
np.sort(a)[::-1]        # descending
a.sort()                # in-place (no return value)

np.argsort(a)           # indices that would sort a
a[np.argsort(a)]        # same as np.sort(a)

# Structured: sort 2D by column
m = np.array([[3, 1], [1, 4], [2, 2]])
m[m[:, 0].argsort()]    # sort rows by first column

# Searching
np.searchsorted(np.sort(a), 4)  # insertion point in sorted array
np.nonzero(a)                   # indices of non-zero elements
np.argmin(a)                    # index of min
np.argmax(a)                    # index of max

# Set operations on arrays
np.unique(a)
np.intersect1d(a, b)
np.union1d(a, b)
np.setdiff1d(a, b)     # in a but not b
np.isin(a, [1, 3])     # boolean mask

Practical patterns

Normalise to [0, 1]

def normalise(a):
    lo, hi = a.min(), a.max()
    return (a - lo) / (hi - lo)

Standardise (z-score)

def standardise(a):
    return (a - a.mean()) / a.std()

Moving average

def moving_avg(a, window):
    return np.convolve(a, np.ones(window) / window, mode="valid")

One-hot encode

def one_hot(labels, num_classes):
    result = np.zeros((len(labels), num_classes))
    result[np.arange(len(labels)), labels] = 1
    return result

labels = np.array([0, 2, 1])
one_hot(labels, 3)
# [[1. 0. 0.]
#  [0. 0. 1.]
#  [0. 1. 0.]]

Batch processing

def batch(data, size):
    for i in range(0, len(data), size):
        yield data[i : i + size]

for chunk in batch(large_array, 1000):
    process(chunk)   # avoids loading all at once

Euclidean distance matrix

# Distance between every pair of rows in X — no Python loops
def pairwise_distances(X):
    diff = X[:, np.newaxis, :] - X[np.newaxis, :, :]  # (n, n, d)
    return np.sqrt((diff ** 2).sum(axis=-1))           # (n, n)

Performance tips

# 1. Avoid Python loops — use vectorised operations
# SLOW
result = [x**2 for x in a]
# FAST
result = a ** 2

# 2. Use views, not copies when possible
b = a[1:4]          # view — no memory allocated
b = a[1:4].copy()   # copy — needed if you mutate b

# 3. Use float32 over float64 for ML/GPU work (2× memory)
weights = np.zeros((1000, 1000), dtype=np.float32)

# 4. Prefer in-place operations
a += 1              # in-place (no new allocation)
a = a + 1           # creates new array

# 5. Contiguous memory layout matters
a = np.ascontiguousarray(a)  # C-contiguous (row-major)
a = np.asfortranarray(a)     # Fortran-contiguous (col-major)

# 6. Check with np.may_share_memory
np.may_share_memory(a, b)    # True if they share memory

# 7. Einstein summation (compact, often faster)
np.einsum("ij,jk->ik", a, b)   # matrix multiply
np.einsum("ii->i", a)           # diagonal elements
np.einsum("ij->i", a)           # row sums

Common mistakes

Mistake Problem Fix
a = np.array([1,2,3]); b = a; b[0] = 99 a is also modified — arrays are references b = a.copy()
np.random.seed(42) in threaded code Global state causes race conditions Use rng = np.random.default_rng(42) per thread
np.dot(a, b) where a is 3D Behaviour differs from @ for N-D arrays Use @ (matmul) for ≥2D
a.sort() expecting a return value In-place sort returns None Use np.sort(a) for a copy
a[mask] = value changing a view Might not propagate to original Ensure you are working on the original, not a slice
float division in older code np.int64 / np.int64 returns float in NumPy ≥1.20 but be explicit Cast: a.astype(float) / b
a.reshape(3, 4) when size doesn't match ValueError Check a.size == 12 first; use -1 for one dim

FAQ

NumPy vs pandas — when to use which?
NumPy operates on typed N-D arrays of a single dtype (fast math). pandas builds on NumPy and adds labelled axes, mixed dtypes, and a rich data-manipulation API. Use NumPy for numerical algorithms and low-level operations; use pandas for tabular data with named columns.

Why does a[1:3] not copy?
NumPy slices return views — objects that point into the same memory buffer. This avoids allocation but means mutations affect the original. Call .copy() when you need independence.

How do I avoid the "ambiguous truth value" error?
if a raises ValueError when a has more than one element. Use a.any(), a.all(), or index a scalar: if a[0].

What is keepdims=True?
Reductions collapse an axis. keepdims=True preserves that axis as size-1, so broadcasting still works: a - a.mean(axis=1, keepdims=True) subtracts each row's mean from that row.

How do I speed up a slow NumPy loop?
Try in order: (1) vectorise with ufuncs, (2) np.einsum, (3) Numba @jit, (4) Cython, (5) CuPy for GPU. Profile with %timeit or cProfile before optimising.

np.nan comparisons always return False — why?
IEEE 754: NaN != NaN. Use np.isnan(a) to detect missing values, and np.nanmean / np.nansum to compute statistics that ignore them.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools