Introduction
NumPy is the foundation of Python’s scientific computing stack. This guide covers practical NumPy patterns you’ll use regularly โ from array creation and manipulation to linear algebra and vectorized operations.
Array Creation
import numpy as np
# From Python lists
a = np.array([1, 2, 3, 4, 5])
m = np.array([[1, 2, 3], [4, 5, 6]])
# Built-in constructors
zeros = np.zeros((3, 4)) # 3x4 array of 0.0
ones = np.ones((2, 3)) # 2x3 array of 1.0
eye = np.eye(4) # 4x4 identity matrix
full = np.full((2, 3), 7) # 2x3 array of 7
empty = np.empty((3, 3)) # uninitialized (fast)
# Ranges
arange = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
linspace = np.linspace(0, 1, 5) # [0.0, 0.25, 0.5, 0.75, 1.0]
logspace = np.logspace(0, 3, 4) # [1, 10, 100, 1000]
# Random
rand = np.random.rand(3, 3) # uniform [0, 1)
randn = np.random.randn(3, 3) # standard normal
randint = np.random.randint(0, 10, (3, 3)) # integers
seed = np.random.seed(42) # reproducibility
Array Properties and Reshaping
a = np.arange(12)
print(a.shape) # => (12,)
print(a.ndim) # => 1
print(a.size) # => 12
print(a.dtype) # => int64
# Reshape
m = a.reshape(3, 4) # 3x4 matrix
m = a.reshape(2, -1) # 2 rows, infer columns (2x6)
m = a.reshape(-1, 3) # infer rows, 3 columns (4x3)
# Flatten
flat = m.flatten() # always returns a copy
flat = m.ravel() # returns a view when possible
# Transpose
print(m.T) # transpose
print(m.T.shape) # => (4, 3) if m is (3, 4)
# Add/remove dimensions
a = np.array([1, 2, 3])
print(a[np.newaxis, :].shape) # => (1, 3)
print(a[:, np.newaxis].shape) # => (3, 1)
print(np.expand_dims(a, 0).shape) # => (1, 3)
Indexing and Slicing
m = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Basic indexing
print(m[1, 2]) # => 6 (row 1, col 2)
print(m[0]) # => [1 2 3] (first row)
print(m[:, 1]) # => [2 5 8] (second column)
# Slicing
print(m[0:2, 1:3]) # => [[2 3], [5 6]]
print(m[::2, ::2]) # => [[1 3], [7 9]] (every other)
# Boolean indexing
mask = m > 5
print(m[mask]) # => [6 7 8 9]
m[m < 3] = 0 # in-place modification
# Fancy indexing
rows = [0, 2]
cols = [1, 2]
print(m[rows, cols]) # => [2 9] (m[0,1] and m[2,2])
Math Operations
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
# Element-wise arithmetic
print(a + b) # => [ 6 8 10 12]
print(a * b) # => [ 5 12 21 32]
print(a ** 2) # => [ 1 4 9 16]
print(a / b) # => [0.2 0.33 0.43 0.5]
# Universal functions (ufuncs)
print(np.sqrt(a)) # => [1. 1.41 1.73 2. ]
print(np.exp(a)) # => [ 2.72 7.39 20.09 54.6 ]
print(np.log(a)) # => [0. 0.693 1.099 1.386]
print(np.abs(np.array([-1, -2, 3]))) # => [1 2 3]
# Aggregations
print(a.sum()) # => 10
print(a.mean()) # => 2.5
print(a.std()) # => 1.118
print(a.min(), a.max()) # => 1 4
print(a.argmin()) # => 0 (index of min)
print(a.argmax()) # => 3 (index of max)
print(a.cumsum()) # => [ 1 3 6 10]
print(a.cumprod()) # => [ 1 2 6 24]
# Axis-wise aggregations
m = np.array([[1, 2, 3], [4, 5, 6]])
print(m.sum(axis=0)) # => [5 7 9] (column sums)
print(m.sum(axis=1)) # => [ 6 15] (row sums)
print(m.mean(axis=0)) # => [2.5 3.5 4.5]
Broadcasting
Broadcasting allows operations between arrays of different shapes:
# Scalar broadcast
a = np.array([1, 2, 3])
print(a + 10) # => [11 12 13]
print(a * 2) # => [2 4 6]
# 1D + 2D broadcast
m = np.array([[1, 2, 3],
[4, 5, 6]])
row = np.array([10, 20, 30])
print(m + row)
# => [[11 22 33]
# [14 25 36]]
# Column broadcast
col = np.array([[100], [200]])
print(m + col)
# => [[101 102 103]
# [204 205 206]]
# Outer product via broadcasting
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
print(a[:, np.newaxis] * b)
# => [[10 20 30]
# [20 40 60]
# [30 60 90]]
Euclidean Norm (np.linalg.norm)
A = np.array([[1, 2], [1, 3]])
# Row-wise norms (L2 norm of each row)
norms = np.linalg.norm(A, axis=1)
print(norms)
# => [2.23606798 3.16227766]
# Row 0: sqrt(1ยฒ + 2ยฒ) = sqrt(5) โ 2.236
# Row 1: sqrt(1ยฒ + 3ยฒ) = sqrt(10) โ 3.162
# Column-wise norms
col_norms = np.linalg.norm(A, axis=0)
print(col_norms)
# => [1.41421356 3.60555128]
# Frobenius norm (entire matrix)
frob = np.linalg.norm(A)
print(frob) # => 3.872983...
# Normalize rows to unit vectors
normalized = A / norms[:, np.newaxis]
print(np.linalg.norm(normalized, axis=1)) # => [1. 1.]
Vectorize Functions
np.vectorize applies a Python function element-wise to arrays:
def classify(x):
if x < 0:
return "negative"
elif x == 0:
return "zero"
else:
return "positive"
vec_classify = np.vectorize(classify)
arr = np.array([-2, -1, 0, 1, 2])
print(vec_classify(arr))
# => ['negative' 'negative' 'zero' 'positive' 'positive']
Note: np.vectorize is convenient but not faster than a loop โ it’s syntactic sugar. For performance, use NumPy’s built-in ufuncs or np.where:
# Faster equivalent using np.where
result = np.where(arr < 0, "negative", np.where(arr == 0, "zero", "positive"))
Linear Algebra
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# Matrix multiplication
print(A @ B) # => [[19 22], [43 50]]
print(np.dot(A, B)) # same
# Determinant
print(np.linalg.det(A)) # => -2.0
# Inverse
print(np.linalg.inv(A))
# => [[-2. 1. ]
# [ 1.5 -0.5]]
# Eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(A)
print(eigenvalues) # => [-0.372 5.372]
# Solve linear system Ax = b
b = np.array([1, 2])
x = np.linalg.solve(A, b)
print(x) # => [0. 0.5]
print(A @ x) # => [1. 2.] (verify)
# SVD decomposition
U, S, Vt = np.linalg.svd(A)
print(S) # singular values
# Rank
print(np.linalg.matrix_rank(A)) # => 2
Combining Arrays
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# Concatenate
print(np.concatenate([a, b])) # => [1 2 3 4 5 6]
m1 = np.array([[1, 2], [3, 4]])
m2 = np.array([[5, 6], [7, 8]])
print(np.vstack([m1, m2])) # vertical stack (add rows)
# => [[1 2], [3 4], [5 6], [7 8]]
print(np.hstack([m1, m2])) # horizontal stack (add columns)
# => [[1 2 5 6], [3 4 7 8]]
# Stack along new axis
print(np.stack([a, b], axis=0)) # => [[1 2 3], [4 5 6]]
print(np.stack([a, b], axis=1)) # => [[1 4], [2 5], [3 6]]
Useful Patterns
# Clip values to a range
a = np.array([-2, -1, 0, 1, 2, 3])
print(np.clip(a, 0, 2)) # => [0 0 0 1 2 2]
# Unique values and counts
a = np.array([1, 2, 2, 3, 3, 3])
values, counts = np.unique(a, return_counts=True)
print(values, counts) # => [1 2 3] [1 2 3]
# Sort
a = np.array([3, 1, 4, 1, 5, 9, 2, 6])
print(np.sort(a)) # => [1 1 2 3 4 5 6 9]
print(np.argsort(a)) # => [1 3 6 0 2 4 7 5] (indices)
# Where (conditional selection)
a = np.array([1, -2, 3, -4, 5])
print(np.where(a > 0, a, 0)) # => [1 0 3 0 5]
# Tile and repeat
print(np.tile([1, 2], 3)) # => [1 2 1 2 1 2]
print(np.repeat([1, 2], 3)) # => [1 1 1 2 2 2]
Comments