Key ideas
- Arrays hold one dtype and have a
shape;np.zeros,np.onesandnp.emptydefault to float64 (8 bytes per element, sonp.zeros((10, 10)).nbytesis 800).np.arange(start, stop, step)covers the half-open interval , sonp.arange(10, 50)ends at 49; with a non-integer step, rounding can change the length and even includestop, so prefernp.linspace(start, stop, num)4. - Basic indexing (slices
start:stop:step, integers,None/np.newaxis,...) returns views that share memory with the original; an integer for every axis gives a single scalar instead2,3. Indices are zero-based and negative ones count from the end; an integer index removes its axis, sox[:, 0]is 1-D whilex[:, [0]]andx[:, 0:1]stay 2-D. - Advanced indexing with integer arrays or boolean masks always returns a copy2,3. Assigning through it works (
a[[0, 2]] = 9changesa), but a chained write such asa[[0, 2]][0] = 9lands in a temporary copy and is lost. A boolean mask with as many dimensions as the array returns a 1-D array of the selected elements in row-major order2. - Assignment converts values to the array's dtype: writing 2.9 into an integer array stores 22. With repeated indices,
x[idx] += 1adds only once per distinct index, because the sum is computed on a temporary and written back2; the unbufferednp.add.at(x, idx, 1)(ornp.bincount) counts every repetition5. - Broadcasting compares shapes from the trailing dimension; two sizes fit when they are equal or one of them is 1, and missing leading dimensions count as 11. Insert axes with
None/np.newaxisto choose which dimensions line up, e.g.b[:, None]makes a column. - Reductions (
sum,mean,std,max,argmax…) remove the axis they reduce;axiscan be a tuple, andkeepdims=Truekeeps a size-1 axis so the result broadcasts back7. Withoutaxis,argmax/argminreturn an index into the flattened array (convert withnp.unravel_index)8.np.stddivides by withddof=0by default, i.e. the population standard deviation; passddof=1for the sample standard deviation7. - reshape reads and refills elements in C (row-major) order by default and returns a view when possible; it regroups, it does not transpose.
ravelgives a view when possible,flattenalways a copy;arr.basetells a view (notNone) from a copy3. - Element-wise vs matrix:
*multiplies element-wise,@/np.matmulis the matrix product;np.einsumwrites either explicitly ('ii'is the trace,'ij,jk->ik'the matrix product). - Casting in place. Ufuncs accept
out=to write into an existing array, and since NumPy 1.10 their defaultcastingis'same_kind'5:a += 0.5on an integer array raises aUFuncTypeError(float → int), whilea = a + 0.5builds a new float array. - Python scalars are "weak" (NumPy ≥ 2.0): their kind counts but their precision is ignored, so the array's dtype wins, e.g.
uint8_array + 10staysuint8and silently wraps around, without a warning for arrays6.
Formulas
- Broadcast shape: align right, then take the larger size where one of them is 1;
(4, 1, 3)with(5, 1)gives(4, 5, 3). - Floor division and remainder:
a == b * (a // b) + a % b;//rounds toward and%takes the sign of the divisor, so and . np.std(x, ddof=d)7.- Strides of a C-ordered
(m, n)float64 array:(8n, 8)bytes.
Worked example
a has shape (4, 1, 3) and b shape (5, 1). Pad b on the left to (1, 5, 1) and compare position by position: vs → 4, vs → 5, vs → 3, so a + b is (4, 5, 3). Adding a (5,) array to a instead fails, because 5 meets the trailing 3; (3,), (5, 3) and (4, 1) all work, the last giving (4, 4, 3).
Row-normalising X = [[0, 1, 2], [3, 4, 5]]: X.sum(axis=1) is [3, 12] with shape (2,), which lines up with the 3 columns and raises; X / X.sum(axis=1, keepdims=True) divides each row by its own sum, and the rows then sum to 1.
In code
import numpy as np
a = np.arange(6)
a[::2][0] = 9; a[[1, 3]][0] = 9 # slice = view (write sticks); fancy index = copy (lost)
print(a) # [9 1 2 3 4 5]
x, y = np.zeros(3), np.zeros(3)
x[[0, 0, 2]] += 1; np.add.at(y, [0, 0, 2], 1)
print(x, y) # [1. 0. 1.] [2. 0. 1.]
u = np.array([250, 5], dtype=np.uint8) + 10
print(u, u.dtype) # [ 4 15] uint8
i = np.array([1, 2, 3])
try: i += 0.5
except TypeError as e: print(type(e).__name__) # UFuncTypeError
print(np.std([1, 2, 3, 4]).round(3), np.std([1, 2, 3, 4], ddof=1).round(3)) # 1.118 1.291
print(np.arange(1, 1.3, 0.1)) # [1. 1.1 1.2 1.3]: stop included by rounding
Common pitfalls
a + bwith shapes(4, 3)and(4,)fails: the 4 meets the trailing 3; useb[:, None](b.Tdoes nothing to a 1-D array)1.- Chained writes through fancy indexing or boolean masks are lost, because these return copies2,3.
x[idx] += 1with repeated indices counts each index once2.a > 1 & a < 4parses asa > (1 & a) < 4, a chained comparison that raisesValueError(ambiguous truth value); write(a > 1) & (a < 4), and use&,|,~rather thanand,or,noton arrays.np.nan == np.nanisFalse; test withnp.isnan.Z.sort()sorts in place and returnsNone;np.sort(Z)returns a sorted copy.reshape(3, 2)of[[0, 1, 2], [3, 4, 5]]is[[0, 1], [2, 3], [4, 5]], not the transpose; for image axes usetranspose, notreshape.- The array's dtype wins:
int_array += 0.5raises instead of upcasting, anduint8_array + 10wraps around instead of growing to a wider type5,6.
References
- NumPy documentation: Broadcasting — General broadcasting rules
- NumPy documentation: Indexing on ndarrays — Basic indexing; Advanced indexing (Integer array indexing, Boolean array indexing); Assigning values to indexed arrays
- NumPy documentation: Copies and views — View; Copy; Other operations; How to tell if the array is a view or a copy
- NumPy documentation: numpy.arange — Parameters (stop); Returns; Warning
- NumPy documentation: Universal functions (ufunc) — Optional keyword arguments (out, casting); Methods (ufunc.at)
- NumPy documentation: Data type promotion in NumPy — Detailed behavior of Python scalars
- NumPy documentation: numpy.std — Parameters (ddof, keepdims); Notes
- NumPy documentation: numpy.argmax — Parameters (axis); Examples (np.unravel_index)