NumPy Cheatsheet - NumPy Functions & Array Operations Reference

This reference is for developers who frequently process arrays in Python, from quick data cleaning to embedding matrix math: creating arrays with np.array/np.zeros, pulling out data by index and slice, reshaping, drawing statistics with sum/mean, and matrix multiplication with a @ b. Unlike a bare API manual, entries follow the practical chain of create → select → reshape → compute → save. After reading you should be able to pick the right function for each stage and dodge the most common broadcast and view-vs-copy pitfalls.

Languages·40 commands·Last updated 2026-07-21
numpypythonData AnalysisArrays

Typical Use Case

For engineers doing data cleaning, statistical computation and numerical experiments, especially those coming from pandas or R who need ndarray semantics first. NumPy is fast because it vectorises, and the price is that shapes and broadcasting are stricter than Python lists. Covers array creation, boolean masks and fancy indexing, reshape, flatten and concatenation, and the maths with broadcasting. Two rules first: reshape returns a view while flatten returns a copy, and broadcasting surprises.

Array Creation 9

np.array([1, 2, 3])
Create array from a list
np.zeros((3, 4))
3x4 array of zeros
np.ones((2, 3))
2x3 array of ones
np.arange(0, 10, 2)
Evenly spaced array [0, 2, 4, 6, 8]
np.linspace(0, 1, 5)
5 points evenly spaced from 0 to 1
np.eye(3)
3x3 identity matrix
np.random.rand(3, 3)
3x3 uniform random numbers
np.random.randn(3, 3)
3x3 standard normal random numbers
np.random.randint(0, 10, (2, 3))
2x3 random integers

Indexing & Slicing 6

a[0]
First element
a[1:3]
Slice: take items 2 and 3
a[a > 5]
Boolean indexing: items > 5
a[[0, 2, 4]]
Fancy indexing: selected positions
a[1:3, 2:4]
2D slice: rows 1-2, cols 2-3
np.where(a > 5, a, 0)
Conditional replace: keep > 5, else 0

Shape Manipulation 8

a.shape
View array shape
a.reshape(2, 3)
Reshape to 2x3
a.flatten()
Flatten to 1D
a.T
Transpose
np.concatenate([a, b], axis=0)
Concatenate along rows
np.vstack([a, b])
Stack vertically
np.hstack([a, b])
Stack horizontally
np.split(a, 3)
Split into 3 equal parts

Math & Statistics 10

a.sum()
Sum all elements
a.mean()
Mean
a.std()
Standard deviation
a.min() / a.max()
Min / max
a.argmin() / a.argmax()
Index of min / max
a.cumsum()
Cumulative sum
np.dot(a, b)
Matrix dot product
a @ b
Matrix multiplication (Python 3.5+)
np.sort(a)
Sort
np.unique(a)
Unique values

Broadcasting & Saving 7

a + 1
Broadcast: add 1 to every element
a * 2
Broadcast: multiply every element by 2
a + b (compatible shapes)
Broadcasted arithmetic (compatible shapes)
np.save("data.npy", a)
Save as .npy file
np.load("data.npy")
Load .npy file
np.savetxt("data.csv", a, delimiter=",")
Save as CSV
np.loadtxt("data.csv", delimiter=",")
Load from CSV

Parameter matrix

ParameterEffectExample
np.array([1, 2, 3])Create an array from a list; dtype determines the element type and is inferred from the values by defaultnp.array([1, 2, 3])
np.zeros((3, 4))Create an all-zero array of the given shape; note the argument is a tuple, written as (3, 4), not 3, 4np.zeros((3, 4))
np.ones((2, 3))Create an all-ones array of the given shape, often used as a mask or initial weightsnp.ones((2, 3))
np.arange(0, 10, 2)Arithmetic sequence that excludes the stop value; a floating-point step can produce an unexpected end value due to precisionnp.arange(0, 10, 2)
np.linspace(0, 1, 5)Split a closed interval into 5 evenly spaced points (including both ends), so function curves do not miss the endpointsnp.linspace(0, 1, 5)
np.eye(3)Generate a 3x3 identity matrix, a basic building block for linear algebranp.eye(3)
np.random.rand(3, 3)Generate a matrix of random numbers uniformly distributed in [0, 1)np.random.rand(3, 3)
np.random.randn(3, 3)Generate a matrix of standard normal random numbers; the difference from rand is the distribution, not the value rangenp.random.randn(3, 3)
np.random.randint(0, 10, (2, 3))Generate random integers of the given shape, left-closed and right-opennp.random.randint(0, 10, (2, 3))
a[0]Index by dimension; the result may still be an array rather than a scalar, depending on the dimensiona[0]
a[1:3]Slice view that does not copy data; modifying the slice changes the original arraya[1:3]
a[a > 5]Boolean mask filtering that returns a copy—the most direct way to filter dataa[a > 5]
a[[0, 2, 4]]Fancy indexing with an index array to pick arbitrary elements, returning a copya[[0, 2, 4]]
a[1:3, 2:4]Multi-dimensional slicing: slice the first dimension, then the seconda[1:3, 2:4]
np.where(a > 5, a, 0)The three-argument form picks values element-wise by condition, the core of vectorized conditional assignmentnp.where(a > 5, a, 0)
a.shapeCheck the shape; far more reliable than using len to judge a multi-dimensional arraya.shape
a.reshape(2, 3)Returns a view without copying data; modifying view elements affects the original array, and the shape cannot be changed in placea.reshape(2, 3)
a.flatten()Returns a one-dimensional copy; the biggest difference from reshape is whether it copiesa.flatten()
a.TTranspose view that swaps rows and columns; a matrix multiplication in the wrong direction often stems from thisa.T
np.concatenate([a, b], axis=0)Concatenate along the given axis; the array shapes must match exactly except along the concatenation axisnp.concatenate([a, b], axis=0)
np.vstack / np.hstackStack by row/column, shorthand for concatenate on the two axesnp.vstack([a, b])
np.split(a, 3)Split the array into 3 parts; returns a list with the parts in original ordernp.split(a, 3)
a.sum() / a.mean()Aggregates all elements by default; specifying axis folds along one dimension while keeping the othersa.sum()
a.std()Standard deviation, population by default; the most commonly used step in data normalizationa.std()
a.argmin() / a.argmax()Returns the flattened index of the extreme value; for 2D arrays it is only meaningful together with unravel_indexa.argmin() / a.argmax()
a.cumsum()Cumulative sum, the basis for cumulative distributions, differences, and rolling statisticsa.cumsum()
np.dot(a, b) / a @ bDot product and matrix multiplication, two equivalent forms; the shapes must satisfy the matrix multiplication rulesnp.dot(a, b)
np.sort(a)Returns a sorted copy without changing the original array; for in-place sorting use a.sort()np.sort(a)
np.unique(a)Deduplicate and sort in ascending order—the easiest one-liner for value distribution statisticsnp.unique(a)
a + 1 / a * 2Broadcasting operation where a scalar automatically expands to every element, no loop neededa + 1
a + b (as long as the shapes are compatible)Broadcasting requires right-alignment and per-dimension compatibility (one is 1 or they are equal), otherwise a broadcast error is raiseda + b (as long as the shapes are compatible)
np.save("data.npy", a)Save as .npy binary format, preserving dtype and shape; reads are lossless and fastnp.save("data.npy", a)
np.load("data.npy")Read back .npy; it returns an array rather than a path (easily confused with other uses of np.load)np.load("data.npy")
np.savetxt(..., delimiter=",")Export as plain-text CSV: readable but loses dtype information and is slow for large filesnp.savetxt("data.csv", a, delimiter=",")
np.loadtxt("data.csv", delimiter=",")Read back CSV; specifying dtype prevents integers from being read as floatnp.loadtxt("data.csv", delimiter=",")

Common pitfalls

SymptomYou change an element of the reshape result, and the original array changes too.

Causereshape returns a view when the array is contiguous, and only copies when necessary.

FixWhen you need an independent copy, call copy() first or use flatten(); to check whether memory is shared, look at the base attribute.

SymptomAdding two arrays raises "operands could not be broadcast".

CauseThe shapes are neither equal nor 1 in some dimension; broadcasting matches dimension by dimension, right-aligned.

FixAlign the dimensions first (a.reshape(-1, 1)) or extend explicitly with np.broadcast_to; do not rush to add a loop.

SymptomThe result shape becomes (N, 1) instead of the expected (N,), and indexing behaves oddly.

CauseOperations between a scalar and an array, or a new axis introduced by a[:, None] slicing, left the axis unremoved.

FixCheck each dimension with a.shape; use a.ravel() or a.flatten() when you need to drop to one dimension.

Symptomnp.arange produces a length that is one more or one less than expected.

Causearange excludes the stop value, and floating-point step accumulation error can push the last value past the stop.

FixWhen you need a fixed number of points, use np.linspace(0, 1, 5), which derives the step backward from the point count.

Symptomnp.zeros(3, 4) raises a TypeError.

CauseThis is a function call, not a tuple literal, so Python treats it as three separate arguments.

FixWrite np.zeros((3, 4)); when passing a tuple with a comma, the parentheses are mandatory.

SymptomIndexing a[0] gives a whole row instead of a single number.

Causea is a 2D array, so indexing the first dimension returns a row.

FixReduce the dimension explicitly: a[0, 0] or a[0][0], or run a.ravel() first and then index.

SymptomRandom results differ on every run, making experiments impossible to reproduce.

CauseThe global random state is used directly, with no seed set.

FixFix the seed at the start of the script before generating, and record the version number; you cannot skip this step when comparing experiments.

Symptomnp.load returns a MemMap instead of an array, and changes are unexpectedly written back to disk.

Causenp.load uses mmap mode by default for .npy, sharing a memory map.

FixWhen you need a normal array, pass mmap_mode=None (the default is a plain read—check the return type).

Troubleshooting

  1. 1First look at each array's shape and type

    print([(x.shape, x.dtype) for x in arrs])

    Shape and dtype are the root cause of ninety percent of broadcasting and indexing problems.

  2. 2Check whether the underlying memory is shared

    print(arr.reshape(-1).base)

    A non-None base means it is a view, and changes affect each other.

  3. 3Reproduce the specific dimensions of a broadcasting error

    print(np.broadcast(a, b))

    When broadcasting fails, the incompatible axes are exposed right here.

  4. 4Confirm the value range after normalization

    print((a - a.mean()) / a.std())

    Columns whose standard deviation is 0 produce NaN and must be removed first.

  5. 5Check the shape and dtype of the file read back

    print(np.load("data.npy").shape)

    Compare with the value before saving to confirm it was not compressed or converted.

  6. 6Confirm the NumPy version

    python -c "import numpy; print(numpy.__version__)"

    Cross-version behavior differences (especially random numbers and the default dtype) are a hot spot for environment problems.

Command Examples

Views versus copies: slicing and fancy indexing

a = np.arange(12).reshape(3, 4) # slicing returns a view; modifying it affects the original array a[1:3, 2:4] = -1 # fancy indexing returns a copy; modifying it does not affect the original array b = a[[0, 2]] b[0, 0] = 99 # flatten always returns a copy c = a.flatten() c[0] = 100

The same data fetched different ways has different identity: assigning into a slice silently writes through to the original shared buffer, while assigning into a fancy-index or flatten result touches only a copy. Slice freely to read; copy explicitly before you intend to modify.

Boolean masks and np.where for threshold filtering

a = np.arange(10) hot = a[a > 5] # keep only elements greater than 5 clamped = np.where(a > 5, a, 0) # keep values greater than 5, otherwise set to 0 b = np.array([[1, 9], [7, 3]]) rows = b[b[:, 0] > 5] # pick rows by condition count = int((a > 5).sum()) # count the number of values above the threshold

Masking assumes a one-dimensional array or a mask matching the selected dimension. Writing a[a > 5] on a 2-D array first flattens everything to one dimension and then selects — the shape changes silently. To pick rows or columns from 2-D data, extract a column first and compare against it.

Broadcasting elementwise ops across compatible shapes

a = np.arange(6).reshape(2, 3) b = np.array([10, 20, 30]) # shape (3,) c = a + b # (2,3) + (3,) → (2,3), broadcast by column d = a * 2 # broadcasting also works when a dimension is 1 a = np.arange(3).reshape(3, 1) b = np.arange(3).reshape(1, 3) e = a + b # (3,1) + (1,3) → (3,3)

Broadcasting expands only dimensions of length 1, which removes a lot of tiling boilerplate. Two arrays of the same rank with unequal, non-1 lengths raise a shape error — but when both are length 1 it stays silent, and the resulting shape can be the opposite of what you pictured. Print .shape first.

Check the element count before reshaping

a = np.arange(12) # 12 elements a.reshape(3, 4) # 12 = 3×4 b = a.reshape(-1, 1) # -1 means that dimension is inferred automatically c = a.reshape(2, -1) # the other dimension is computed as 6 # a mismatched element count raises an error immediately # a.reshape(5, 5)

reshape does not move data, it only changes how it is described, so the element count must match or you get a ValueError. Let NumPy infer one dimension with -1 rather than doing the division yourself. Reach for reshape to reinterpret layout; use flatten when you truly want a copy.

Linear algebra plus dedupe with counts

a = np.array([[1, 0], [0, 1]]) b = np.array([[2, 3], [4, 5]]) np.dot(a, b) a @ b # deduplicate and count occurrences vals, counts = np.unique([1, 1, 2, 3, 3, 3], return_counts=True)

np.dot and @ agree on 1-D vectors but differ on 2-D matrices — inner-product rule versus true matrix multiplication. Keep them straight. unique sorts ascending, so preserving original order means sorting argsort yourself; return_counts=True is the quickest frequency profile and beats wrapping collections.Counter.

Common Pitfalls

  • Division on integer arrays (a / b) yields floats by default. Code written under the old truncating assumption silently miscomputes averages and ratios.
  • A single index a[i] returns a scalar, not a length-1 array, so applying a second dimension index to it raises an out-of-bounds error. Use a[i:i+1] when you need to keep the rank.
  • Slices are views: assigning into one silently writes into the shared buffer of the original array. Writing block by block in a loop without realising this pollutes the source data with no error at all.
  • Broadcasting between incompatible shapes stays silent when some dimension happens to be 1 on both sides, expanding to a shape the opposite of what you expected. Check both .shape values before the operation.
  • np.where returns a new array, so assigning into it changes nothing; an in-place edit requires an explicit a[:] = np.where(...). Forgetting that write-back is silent data loss.
  • np.save / savetxt and np.load / loadtxt both pull the whole file into memory, which exhausts RAM well before you expect on large files. np.load accepts mmap_mode for lazy access, and the plain CSV text format carries no dtype, so loadtxt needs it specified.

Tips

  • NumPy arrays are homogeneous - all elements share one dtype; mixing types is auto-promoted.
  • Broadcasting rule: compare dimensions from the rightmost; they must be equal or one of them is 1.
  • a * b is element-wise; use a @ b or np.dot(a, b) for matrix multiplication.

FAQ

Data I got from slicing changes the original array — how do I avoid that?

The distinction is view versus copy. Slicing like a[1:3] and usually reshape return views, so mutating them mutates the source. Boolean indexing a[a > 5] and fancy indexing a[[0, 2, 4]] return copies, flatten() is documented as returning a copy, and np.sort(a) gives you a new sorted array unless you call a.sort() in place. So slicing is free when you only read; when you need to modify safely, take an explicit copy first, for example np.array(a[1:3]), and edit that.

Are a * b and a @ b the same operation?

They are entirely different. a * b is element-wise multiplication: arrays with compatible shapes broadcast against each other and multiply position by position. a @ b is matrix multiplication (equivalent to np.matmul) — rows times columns then summed — and requires matching inner dimensions. For two vectors of equal length, a * b gives the element-wise product while a @ b gives their dot product as a scalar. Reach for @ or np.dot(a, b) whenever you do linear algebra or matrix transforms, and for plain per-element scaling use broadcasting such as a * 2.

Shapes do not line up and I must not mutate the source — reshape or concatenate?

To change a single array use reshape(2, 3), where the element count must stay identical and the result is usually a view; a.T swaps dimensions so a 2-D matrix exchanges rows and columns while a 1-D array is unchanged; a.flatten() collapses to 1-D and returns a copy. To merge several arrays pick by direction: np.concatenate([a, b], axis=0) is the general form — all dimensions except the concatenated axis must match, with axis=0 stacking rows and axis=1 stacking columns; np.vstack stacks top-to-bottom (matching column counts) and np.hstack side-by-side (matching row counts). To go the other way, np.split(a, 3) divides evenly along the default axis and raises if it does not divide evenly.

How do I filter by a threshold, or replace values that fail a condition?

Two distinct routes. Boolean indexing takes a subset directly: a[a > 5] keeps the elements satisfying a > 5, which is the standard way to threshold-filter or mask values — the same form works column-wise as a[a[:, 0] > 5]. When you want substitution rather than filtering, use the three-argument np.where(a > 5, a, 0): the middle argument is used where the condition holds and the last one everywhere else, giving you a threshold-gated value with zeros elsewhere. Called with only two arguments, np.where acts purely as a condition and returns the indices that match.

Which generator should I reach for: regular sequences or random data?

Use the deterministic helpers when you want a regular series: np.arange(0, 10, 2) yields the arithmetic run [0, 2, 4, 6, 8] with the step given as the third argument, and np.linspace(0, 1, 5) samples five equally spaced points between 0 and 1 inclusive. For randomness use the np.random family: np.random.rand(3, 3) draws uniform values in [0, 1), np.random.randn(3, 3) draws from the standard normal, and np.random.randint(0, 10, (2, 3)) produces a 2×3 array of integers in [0, 10). For structural placeholders use np.zeros((3, 4)), np.ones((2, 3)) and the identity np.eye(3).

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us