NumPy - the array engine behind Python's numeric stack
A complete, example-driven reference: what NumPy is, why it exists, its data types, core formulas, diagrams, a quick cheat sheet, and interview-ready notes - all in one place.
What is NumPy?
NumPy (Numerical Python) is the foundational library for numerical computing in Python. Its core object is the ndarray — a fixed-type, N-dimensional array stored contiguously in memory. Unlike a Python list, every element shares the same data type, which is exactly what lets NumPy hand off math to fast, compiled C code instead of the Python interpreter.
It's the base layer under Pandas, Scikit-learn, TensorFlow, PyTorch, and Matplotlib — if you've done any data science or ML in Python, you've used NumPy whether you called it directly or not.
import numpy as np
arr = np.array([1, 2, 3, 4])
print(arr, type(arr), arr.dtype)
# [1 2 3 4] <class 'numpy.ndarray'> int64Why NumPy is Used (and Needed)
- Speed: vectorized C loops replace slow Python
forloops — often 10–100x faster. - Memory efficiency: a fixed dtype means compact, contiguous storage instead of an array of pointers to Python objects.
- Broadcasting: apply operations across differently-shaped arrays without manually looping or copying data.
- Ecosystem: Pandas DataFrames, Scikit-learn models, and deep learning tensors are all built on or interoperate with ndarray.
- Batteries included: linear algebra, Fourier transforms, random sampling, and statistics ship in the standard library.
import numpy as np, time
n = 1_000_000
py_list = list(range(n))
np_arr = np.arange(n)
t0 = time.time()
py_result = [x * 2 for x in py_list]
print("pure python:", time.time() - t0)
t0 = time.time()
np_result = np_arr * 2
print("numpy:", time.time() - t0) # noticeably fasterInstall & Import
NumPy is a third-party package — install it once per environment:
pip install numpyImport convention used everywhere (blogs, docs, this page):
import numpy as npndarray & Data Types (dtypes)
Every ndarray has one dtype shared by all elements. Common families:
Integers
int8, int16, int32, int64, uint8…uint64
Floats
float16, float32, float64 (default)
Complex
complex64, complex128
Other
bool, object, str_ (unicode)
a = np.array([1, 2, 3], dtype=np.float32)
print(a.dtype) # float32
b = a.astype(np.int64) # explicit cast
print(b.dtype) # int64Array Creation
np.array([1, 2, 3]) # from a list
np.zeros((2, 3)) # all zeros
np.ones((3, 3)) # all ones
np.full((2, 2), 7) # constant-filled
np.eye(3) # identity matrix
np.arange(0, 10, 2) # like range(), array output
np.linspace(0, 1, 5) # 5 evenly spaced points
np.random.rand(2, 2) # uniform random [0, 1)Array Attributes
| Attribute | Meaning |
|---|---|
| arr.shape | dimensions, e.g. (3, 4) |
| arr.ndim | number of axes |
| arr.size | total element count |
| arr.dtype | element data type |
| arr.itemsize | bytes per element |
| arr.nbytes | total memory in bytes |
Indexing & Slicing
arr = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
arr[0] # first row -> [1 2 3 4]
arr[-1] # last row -> [9 10 11 12]
arr[1:3] # rows 1 and 2 (stop excluded)
arr[:, 0] # first column -> [1 5 9]
arr[arr > 5] # boolean mask -> [6 7 8 9 10 11 12]
arr[[0, 2]] # fancy indexing -> rows 0 and 2Tip: basic slicing returns a view (shares memory with the original); boolean and fancy indexing return a copy.
Reshape, Stack & Split
a = np.arange(6)
a.reshape(2, 3) # change shape without copying data
a.flatten() # 1D copy
a.ravel() # 1D view when possible
np.vstack([a, a]) # stack as new rows
np.hstack([a, a]) # stack side by side
np.concatenate([a, a], axis=0)
np.split(a, 3) # split into 3 equal partsBroadcasting
NumPy compares shapes from the right and applies these rules:
- Equal dimensions match directly.
- A dimension of size 1 stretches to match the other array.
- Missing leading dimensions are treated as size 1.
a = np.ones((3, 4)) # shape (3, 4)
b = np.array([1, 2, 3, 4]) # shape (4,)
result = a + b # b is applied to every row
print(result.shape) # (3, 4) — no data was copiedVectorization & Universal Functions
Ufuncs are element-wise operations implemented in compiled code: np.add, np.subtract, np.multiply, np.divide, np.power, np.sqrt, np.exp, np.log, np.sin, np.abs, np.maximum and more.
data = np.array([1, 2, 3, 4])
# slow: python loop
squares = [x ** 2 for x in data]
# fast: vectorized
squares = data ** 2Math & Statistics Formulas
Linear Algebra (np.linalg)
A = np.array([[4, 2], [1, 3]])
np.linalg.inv(A) # inverse
np.linalg.det(A) # determinant
np.linalg.eig(A) # eigenvalues & eigenvectors
np.linalg.solve(A, b) # solve A x = b
A.T # transpose
np.trace(A) # sum of diagonalRandom Module (np.random)
np.random.seed(42) # reproducibility
np.random.rand(3) # uniform [0, 1)
np.random.randn(3) # standard normal (mean 0, std 1)
np.random.randint(0, 10, 5) # random integers
np.random.choice(arr, 3) # random sample
np.random.shuffle(arr) # in-place shuffleAggregation & Axis
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr.sum() # 21 — total
arr.sum(axis=0) # [5 7 9] — collapse rows, per column
arr.sum(axis=1) # [6 15] — collapse columns, per row
arr.min(), arr.max()
arr.argmin(), arr.argmax()
arr.cumsum()Diagrams & Sketches
Axes of a 2D array — axis=0 moves down the rows (per column), axis=1 moves across the columns (per row):
Broadcasting a smaller shape across a larger one:
Memory layout — NumPy stores arrays row-major (C order) by default:
Cheat Sheet
| Creation | np.array, np.zeros, np.ones, np.arange, np.linspace, np.eye |
| Inspect | arr.shape · arr.ndim · arr.size · arr.dtype |
| Reshape | arr.reshape() · arr.flatten() · arr.ravel() · arr.T |
| Combine | np.concatenate · np.vstack · np.hstack · np.stack |
| Elementwise | + - * / ** np.sqrt np.exp np.log |
| Statistics | np.mean · np.median · np.std · np.var · np.percentile |
| Linear Algebra | np.dot / @ · np.linalg.inv · np.linalg.det · np.linalg.eig |
| Random | np.random.rand · np.random.randn · np.random.randint |
| Boolean / Search | np.where · np.any · np.all · np.isin |
Important Points (Interview-Ready)
- An ndarray requires one shared dtype; Python lists can mix types.
- Broadcasting avoids manual loops and unnecessary memory copies.
- Basic slicing → view (shares memory). Boolean / fancy indexing → copy.
- axis=0 = down rows (per column); axis=1 = across columns (per row).
np.dotis matrix/inner product;np.multiply(or*) is element-wise.- Use
np.copy()to detach an array from its source when you don't want shared-memory side effects. - Use
np.nanmean,np.nanstd, etc. to safely ignore NaN values in real-world, messy data.
Want these notes on your device? Grab the full markdown file — every section, formula, and cheat sheet from this page.