NumPy Array Operations in Python
Learn NumPy basics in Python! A fun and easy guide to super-fast arrays, matrices, and data science math without using slow for-loops.
Try it yourself
Run this code directly in your browser. Click "Open in full editor" to experiment further.
Click Run to see output
Or press Ctrl + Enter
How it works
Welcome to NumPy (Numerical Python) โ the bedrock under every serious piece of scientific Python. Pandas, SciPy, scikit-learn, Matplotlib, OpenCV, PyTorch, TensorFlow, JAX โ they all either are NumPy under the hood or speak its array protocol. If you want to do data science, ML, or image processing in Python, learning numpy basics is non-negotiable.
Why do we need NumPy at all when Python has lists? Because Python lists are generic containers โ they can hold mixed types, which forces Python to store every element as a full object with type info, refcounts, and pointer indirection. Multiplying a million-element list by 2 means a million method dispatches. NumPy throws all that overhead in the bin. ๐
ndarray: The Core Object
The numpy ndarray is a homogeneous, contiguous, n-dimensional block of memory. Three words doing a lot of work:
dtype (e.g. int64, float32, bool). No mixed types.Every ndarray has three attributes you'll use constantly:
| Attribute | What it means |
|---|---|
shape | Tuple of sizes per axis, e.g. (3, 4) is 3 rows ร 4 cols |
dtype | Element type, e.g. float64, int32, uint8 |
strides | Byte step to move one slot along each axis (this is how views work without copying) |
Why NumPy is 10-100x Faster Than Python Lists
Vectorization python is the term, and it stacks four things:
1. C-implemented loops โ the inner loop is compiled C, not bytecode.
2. Contiguous memory โ the CPU prefetcher loves linear access.
3. SIMD via BLAS/OpenBLAS/MKL โ matrix ops dispatch to AVX/NEON.
4. No per-element type checks โ dtype is known once.
A quick benchmark you can run yourself:
import numpy as np, time
n = 10_000_000
lst = list(range(n))
arr = np.arange(n)
t = time.time(); s = sum(x*x for x in lst); print("list:", time.time()-t)
t = time.time(); s = (arr*arr).sum(); print("numpy:", time.time()-t)Expect a 50โ100ร speedup. The gap widens with float math and matrix products.
Creating Arrays
np.array([1, 2, 3]) # from a list
np.zeros((3, 4)) # 3x4 of 0.0
np.ones((2, 2), dtype=int) # 2x2 of 1
np.arange(0, 10, 2) # [0 2 4 6 8]
np.linspace(0, 1, 5) # 5 evenly spaced points incl. endpoints
np.random.default_rng().standard_normal((3, 3)) # modern Generator API
np.eye(4) # 4x4 identity matrix
np.full((2, 3), 7) # constant fillTip: Prefernp.random.default_rng()over legacynp.random.rand/randnโ it's the recommended API in NumPy 2.x.
Indexing & Slicing
a = np.arange(12).reshape(3, 4)
a[0, 2] # element at row 0, col 2
a[1] # entire row 1 (1D view)
a[:, 1] # entire column 1
a[1:, :2] # rows 1+, first two columns
a[[0, 2], :] # fancy indexing: rows 0 and 2
a[a > 5] # boolean mask: all elements > 5Critical rule: basic slicing returns a view on the same memory โ modifying a[1:] modifies a. Fancy indexing (integer arrays, boolean masks) returns a copy. If you need an independent buffer, call .copy().
Broadcasting: The Magic Rule
NumPy broadcasting is how arrays of different shapes combine without explicit loops. The rules:
1. If arrays differ in ndim, pad the smaller shape with 1s on the left.
2. Along each axis, sizes must be equal or one of them must be 1.
3. The size-1 axis is virtually stretched to match the other.
A = np.ones((3, 4)) # shape (3, 4)
row = np.array([1,2,3,4])# shape (4,) -> treated as (1, 4)
A + row # row stretched down all 3 rows
col = np.array([[10],[20],[30]]) # shape (3, 1)
A + col # col stretched across all 4 colsMismatched shapes like (3,4) + (3,) fail โ use [:, None] to add an axis explicitly.
Common Pitfalls
1. List vs array confusion โ [1,2,3] * 3 gives [1,2,3,1,2,3,1,2,3]. np.array([1,2,3]) * 3 gives [3,6,9]. Different worlds.
2. View vs copy surprises โ b = a[::2]; b[0] = 99 mutates a. Use a[::2].copy() when in doubt.
3. Integer overflow โ np.array([200], dtype=np.uint8) + 100 silently wraps to 44. NumPy 2.x raises on out-of-range scalars (np.int8(1) + 1000) but arrays still wrap.
4. Broadcasting mismatches โ always print(x.shape, y.shape) when you get ValueError: operands could not be broadcast together.
5. Axis confusion โ axis=0 collapses rows (computes per column); axis=1 collapses columns (per row). The axis disappears.
6. `.shape` vs `len()` โ len(a) only returns the size of the first axis. Use a.shape or a.size for the full picture.
7. Equality โ a == b returns a boolean array, not a single bool. Use np.array_equal(a, b) or np.allclose(a, b) for floats.
NumPy vs Pure Python vs Pandas vs Cupy/JAX
| Tool | Pick when |
|---|---|
| Pure Python list | Few items, mixed types, no math |
| NumPy | Numeric arrays, math, image/audio buffers |
| Pandas | Tabular data with named columns, time series |
| CuPy | Same NumPy API, on NVIDIA GPU |
| JAX | NumPy API + autograd + JIT |
| PyTorch | Deep learning with autograd |
The numpy vs python list question answers itself: if it's numeric and bigger than a few hundred elements, use NumPy.
Real-World Uses
(H, W, 3) uint8 array. OpenCV, Pillow, scikit-image all hand you NumPy arrays.float32 arrays; FFTs and filters are pure NumPy/SciPy.X of shape (n_samples, n_features) is the scikit-learn interface.Frequently Asked Questions
Why is NumPy faster than a list?
Homogeneous dtype + contiguous memory + compiled C loops + SIMD via BLAS. You skip the interpreter for the hot loop entirely.
What's the difference between `np.array` and `np.asarray`?
np.array always copies by default. np.asarray returns the input unchanged if it's already an ndarray of the right dtype โ use it in library code to avoid copies.
Why does my slice modify the original?
Basic slicing returns a view sharing memory. Add .copy() to detach it.
What's the right dtype to use?
float64 for general math, float32 for ML/GPU to halve memory, int64 for indices, uint8 for images, bool for masks. Mind promotion rules (NEP 50, adopted in NumPy 2.0).
Should I learn NumPy or Pandas first?
NumPy first. Pandas is built on NumPy, and every Pandas Series/DataFrame has an underlying ndarray you'll need to reach for eventually.
What about JAX or PyTorch โ are arrays the same?
Conceptually yes โ both mirror the NumPy API. JAX is closest (drop-in jax.numpy); PyTorch tensors differ slightly (.view vs .reshape) but the mental model transfers. Master NumPy first; the rest is dialect.
Related examples
Data Analysis with Pandas in Python
Learn Pandas for data analysis in Python! A beginner-friendly guide to DataFrames, filtering data, grouping, and handling messy missing data just like Excel.
Data Visualization with Matplotlib
Learn data visualization in Python with Matplotlib! A fun guide to creating line plots, scatter plots, and bar charts the recommended object-oriented way.