Cheat sheet · Data & MLOps
Python, Pandas and NumPy cheat sheet
The key ideas on one page — plus common pitfalls and interview tips. Skim it before an interview, or print it for revision.
Cheat sheet
NumPy is the foundational array library for numerical computing in Python: a contiguous, typed n-dimensional array (ndarray) plus vectorized operations, broadcasting, and views over a strided memory buffer. Mastery means understanding dtype/memory layout, broadcasting rules, views vs copies, and writing loop-free vectorized code that runs in compiled C.
The ndarray & Memory Model
- An ndarray is a contiguous block of homogeneous memory described by metadata: shape, dtype, strides, and a pointer to the data buffer. Most operations manipulate this metadata cheaply rather than copying data.
- strides = bytes to step in each dimension to move one element along that axis; reshape/transpose/basic-slicing change shape and strides without copying data (a 'view').
- Memory order: C-contiguous (row-major, last axis varies fastest) vs F-contiguous (column-major, Fortran). Check with arr.flags; it affects cache performance and whether ops can avoid copies.
- dtype controls size & semantics: int8/16/32/64, uint*, float16/32/64, bool, complex64/128, datetime64/timedelta64, and structured dtypes. Default float is float64.
- Default integer dtype is platform's pointer width: as of NumPy 2.0 it is int64 on all 64-bit systems (Windows included; pre-2.0 Windows defaulted to int32) and int32 on 32-bit systems.
- nbytes = itemsize (bytes per element) x size (total elements). float32 halves memory and bandwidth vs float64 — key for large ML data.
- Fixed-width integer types overflow/wrap silently: np.int8 wraps past 127. Object dtype (dtype=object) stores Python pointers and kills vectorized performance.
Creation & Reshaping
- Constructors: np.array, np.zeros, np.ones, np.empty (uninitialized/fast), np.full, np.arange (start, stop, step; stop exclusive), np.linspace (count, endpoint-inclusive by default), np.eye/np.identity.
- Random (modern API, 2026 best practice): rng = np.random.default_rng(seed); then rng.random(), rng.normal(), rng.integers(). The Generator API is faster, statistically better, and explicit about state — prefer it over legacy np.random.seed/rand.
- reshape(-1) flattens; a single -1 means 'infer this dimension from the total size and the rest'. ravel() returns a view when possible; flatten() always copies.
- Add/remove axes: arr[:, None] or np.newaxis inserts an axis; np.expand_dims / np.squeeze. np.transpose / arr.T / np.moveaxis / np.swapaxes permute axes (views).
- Combine: np.concatenate (existing axis), np.stack (new axis), np.vstack/hstack/dstack, np.r_/np.c_. Split with np.split/array_split.
- np.asarray avoids a copy when the input is already a compatible ndarray; np.array(..., copy=True) forces a copy. (In NumPy 2.0, copy=False raises if a copy is unavoidable; use np.asarray for 'copy only if needed'.)
Indexing: Views vs Copies (high-yield)
- Basic slicing (arr[1:3, ::2]) returns a VIEW — mutating it mutates the original. This is the #1 interview gotcha.
- Fancy/advanced indexing with integer arrays or boolean masks returns a COPY: arr[[0, 2]], arr[arr > 0].
- Boolean masking: arr[mask] selects; arr[mask] = 0 assigns in place. np.where(cond, a, b) is the vectorized ternary; np.where(cond) alone returns index tuples.
- Combine conditions with & | ~ and parentheses (NOT Python and/or, which raise 'ambiguous truth value' on arrays): arr[(arr > 0) & (arr < 10)].
- np.take / np.put, np.argwhere / np.nonzero return index positions. np.clip(arr, lo, hi) bounds values element-wise.
- For independence when you sliced, call .copy() explicitly; use np.shares_memory(a, b) to test aliasing.
Broadcasting & Vectorization
- Broadcasting rule: align shapes from the RIGHT (trailing axes). Dimensions are compatible if equal or one of them is 1; size-1 dims are stretched virtually with no data copy. Missing leading dims are treated as 1.
- Example: (3,1) + (1,4) -> (3,4); (256,256,3) * (3,) -> scales channels. Incompatible (3,) + (4,) raises ValueError.
- Vectorize: replace Python loops with array ops — they run in compiled C and are typically 10-100x faster. np.vectorize is NOT fast; it is a Python-loop convenience wrapper.
- Ufuncs (np.add, np.exp, np.maximum, ...) operate element-wise with optional out= to write in place and avoid allocations, plus .reduce/.accumulate/.outer methods.
- Use out= and in-place operators (+=, *=) to cut allocations in hot loops. np.einsum('ij,jk->ik', A, B) expresses contractions/reductions compactly.
- np.newaxis enables outer-product-style ops: a[:, None] - b[None, :] builds a pairwise difference matrix.
Aggregations, Axis & NaN Handling
- Reductions take axis: arr.sum(axis=0) collapses rows -> per-column result; axis=1 -> per-row. axis=None (default) reduces everything to a scalar.
- keepdims=True preserves reduced dims as size 1 so the result broadcasts back: x - x.mean(1, keepdims=True).
- Mental model: axis is the dimension that DISAPPEARS. For a (rows, cols) matrix, axis=0 means 'collapse down the rows' (column stats).
- NaN propagates through normal reductions (any NaN -> NaN). Use np.nansum/nanmean/nanstd to ignore NaNs; detect with np.isnan, np.isfinite.
- Float comparison needs tolerance: np.isclose / np.allclose, never ==, because e.g. 0.1 + 0.2 != 0.3.
- Cumulative & order ops: cumsum, cumprod, argmin/argmax (index of extreme), sort/argsort, unique, bincount, percentile/quantile.
Linear Algebra & Performance
- Matrix multiply: @ operator or np.matmul; np.dot for 1-D/2-D dot products. Use * for element-wise. np.linalg: inv, solve, det, eig/eigh, svd, qr, cholesky, norm, pinv, lstsq.
- Prefer np.linalg.solve(A, b) over inv(A) @ b — it is faster and numerically more stable. These call optimized, multithreaded BLAS/LAPACK (OpenBLAS/MKL); performance depends on the linked backend.
- Avoid building large intermediates; prefer fused ops, out=, views, and float32 where precision allows to halve bandwidth.
- np.einsum and np.tensordot handle general tensor contractions; einsum supports optimize=True (and np.einsum_path) to reorder for fewer FLOPs.
- Interop: arrays share buffers with PyTorch/JAX/pandas via the buffer/DLPack protocols (torch.from_numpy is zero-copy on CPU). np.frombuffer/np.memmap map bytes/disk data without loading into RAM.
- NumPy 2.x (2.0 released June 2024) cleaned the C-API and namespace, removed aliases (np.float_, np.int0, np.bool8), and changed scalar promotion (NEP 50). The Array API standard is now built into the main namespace (the experimental np.array_api module was removed); use array-api-compat for backend-agnostic code across NumPy/CuPy/JAX/PyTorch.