CodeNFacts
CodeHub
Home

All Categories


Sign In
NumPyPython · Data Science

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.

.md file · works offline · ~1 min read per section

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.

hello_numpy.py
import numpy as np

arr = np.array([1, 2, 3, 4])
print(arr, type(arr), arr.dtype)
# [1 2 3 4] <class 'numpy.ndarray'> int64

Why NumPy is Used (and Needed)

  • Speed: vectorized C loops replace slow Python for loops — 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.
speed_comparison.py
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 faster

Install & Import

NumPy is a third-party package — install it once per environment:

terminal
pip install numpy

Import convention used everywhere (blogs, docs, this page):

import.py
import numpy as np

ndarray & 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)

dtypes.py
a = np.array([1, 2, 3], dtype=np.float32)
print(a.dtype)          # float32
b = a.astype(np.int64)  # explicit cast
print(b.dtype)          # int64

Array Creation

creation.py
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

AttributeMeaning
arr.shapedimensions, e.g. (3, 4)
arr.ndimnumber of axes
arr.sizetotal element count
arr.dtypeelement data type
arr.itemsizebytes per element
arr.nbytestotal memory in bytes

Indexing & Slicing

indexing.py
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 2

Tip: basic slicing returns a view (shares memory with the original); boolean and fancy indexing return a copy.

Reshape, Stack & Split

reshape.py
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 parts

Broadcasting

NumPy compares shapes from the right and applies these rules:

  1. Equal dimensions match directly.
  2. A dimension of size 1 stretches to match the other array.
  3. Missing leading dimensions are treated as size 1.
broadcasting.py
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 copied

Vectorization & 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.

vectorize.py
data = np.array([1, 2, 3, 4])

# slow: python loop
squares = [x ** 2 for x in data]

# fast: vectorized
squares = data ** 2

Math & Statistics Formulas

Meanx̄ = (Σxᵢ) / nnp.mean(a)
Varianceσ² = Σ(xᵢ - x̄)² / nnp.var(a)
Std Deviationσ = √(σ²)np.std(a)
Z-scorez = (x - μ) / σstandardization
Min-Max Scalex' = (x-min)/(max-min)normalization to [0,1]
Dot Producta·b = Σ aᵢbᵢnp.dot(a, b)
Matrix MultCᵢⱼ = Σₖ Aᵢₖ BₖⱼA @ B
L1 Norm‖v‖₁ = Σ|vᵢ|np.linalg.norm(v, 1)
L2 Norm‖v‖₂ = √(Σvᵢ²)np.linalg.norm(v)
Determinant (2×2)|A| = ad − bcnp.linalg.det(A)
Correlationr = cov(x,y) / (σx·σy)np.corrcoef(x, y)
Medianmiddle value of sorted xnp.median(a)

Linear Algebra (np.linalg)

linalg.py
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 diagonal

Random Module (np.random)

random.py
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 shuffle

Aggregation & Axis

aggregation.py
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):

2D array — shape (3, 4)01234567891011axis=0axis=1

Broadcasting a smaller shape across a larger one:

(3, 4) + (4,) → broadcast (4,) across every row+shape (4,) stretched virtually — no data copied

Memory layout — NumPy stores arrays row-major (C order) by default:

Row-major (C order) — default in NumPy012345memory: [0, 1, 2, 3, 4, 5] — travels left→right, then next row

Cheat Sheet

Creationnp.array, np.zeros, np.ones, np.arange, np.linspace, np.eye
Inspectarr.shape · arr.ndim · arr.size · arr.dtype
Reshapearr.reshape() · arr.flatten() · arr.ravel() · arr.T
Combinenp.concatenate · np.vstack · np.hstack · np.stack
Elementwise+ - * / ** np.sqrt np.exp np.log
Statisticsnp.mean · np.median · np.std · np.var · np.percentile
Linear Algebranp.dot / @ · np.linalg.inv · np.linalg.det · np.linalg.eig
Randomnp.random.rand · np.random.randn · np.random.randint
Boolean / Searchnp.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.dot is 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.