5.8 KiB
title, tags, category, difficulty, created
| title | tags | category | difficulty | created | ||||
|---|---|---|---|---|---|---|---|---|
| Introduction to NumPy |
|
Lesson | Beginner to Intermediate | 2026-05-26 |
Introduction to NumPy
[!abstract] What is NumPy? NumPy (Numerical Python) is the foundational library for scientific computing in Python. It provides a high-performance multidimensional array object, tools for working with these arrays, and linear algebra, Fourier transform, and random number capabilities.
Why use NumPy instead of standard Python lists?
- Speed: NumPy arrays are written in C, making mathematical operations up to 100x faster than standard Python lists.
- Memory Efficiency: NumPy arrays use contiguous blocks of memory, whereas Python lists store pointers to objects scattered across memory.
- Vectorization: It allows performing mathematical operations on whole arrays without writing slow
forloops.
📐 The N-Dimensional Array (ndarray)
The core of NumPy is the ndarray (N-dimensional array). It is a grid of values, all of the same type (homogenous), indexed by a tuple of non-negative integers.
graph TD
A[ndarray] --> B["1D Array (Vector) <br> Shape: (n,)"]
A --> C["2D Array (Matrix) <br> Shape: (m, n)"]
A --> D["3D Array (Tensor) <br> Shape: (p, m, n)"]
Essential Array Attributes
Every array has attributes that describe its structure:
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.ndim) # Number of dimensions (axes) -> 2
print(arr.shape) # Tuple representing sizes in each dimension -> (2, 3)
print(arr.size) # Total number of elements -> 6
print(arr.dtype) # Data type of the elements -> int64
🛠️ Creating Arrays
First, import the library using the standard alias:
import numpy as np
1. From Python Lists
# 1D Vector
v = np.array([1, 2, 3])
# 2D Matrix
m = np.array([[1, 2], [3, 4]])
2. Built-in Placeholders
NumPy provides functions to initialize arrays with placeholders, avoiding manual creation:
# Array of zeros
zeros = np.zeros((3, 4)) # 3 rows, 4 columns
# Array of ones
ones = np.ones((2, 3), dtype=np.int32)
# Range of numbers (similar to range())
range_arr = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
# Linearly spaced numbers
linspace_arr = np.linspace(0, 1, 5) # [0.0, 0.25, 0.5, 0.75, 1.0]
# Identity Matrix
eye_matrix = np.eye(3) # 3x3 identity matrix
3. Random Number Generation
# Uniform random values between [0.0, 1.0)
rand_arr = np.random.rand(2, 2)
# Standard normal distribution (mean=0, std=1)
randn_arr = np.random.randn(2, 2)
# Random integers
rand_ints = np.random.randint(1, 100, size=(5,))
⚡ Element-wise Operations & Vectorization
In standard Python, to add two lists element-wise, you need a list comprehension or loop. In NumPy, you do it directly.
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
print(x + y) # [5, 7, 9]
print(x * y) # [4, 10, 18]
print(x ** 2) # [1, 4, 9]
📡 Broadcasting
Broadcasting is a powerful mechanism that allows NumPy to perform arithmetic operations on arrays of different shapes. The smaller array is "broadcast" across the larger array so that they have compatible shapes.
matrix = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 10
# The scalar is added to every single element
print(matrix + scalar)
# [[11, 12, 13]
# [14, 15, 16]]
🔍 Indexing, Slicing & Masking
Slicing 2D Arrays
Slicing follows the format array[row_start:row_end, col_start:col_end].
arr = np.array([
[10, 11, 12],
[20, 21, 22],
[30, 31, 32]
])
# Get row at index 1
print(arr[1, :]) # [20, 21, 22]
# Get column at index 2
print(arr[:, 2]) # [12, 22, 32]
# Slice a subgrid (top-left 2x2)
print(arr[0:2, 0:2])
# [[10, 11]
# [20, 21]]
🎭 Boolean Masking (Conditional Filtering)
You can filter arrays using conditions. NumPy returns elements where the condition resolves to True.
data = np.array([1, 5, 8, 12, 3, 15])
# Create a boolean mask
mask = data > 5 # [False, False, True, True, False, True]
# Filter using the mask
filtered_data = data[mask] # [8, 12, 15]
🧮 Common Aggregations & Axis Operations
Aggregations allow you to compute statistics over entire arrays or along specific axes:
axis=0: Down the columns (collapses rows).axis=1: Across the rows (collapses columns).
arr = np.array([[1, 2], [3, 4]])
# Sum of all elements
print(np.sum(arr)) # 10
# Sum down the columns (vertical)
print(np.sum(arr, axis=0)) # [4, 6]
# Sum across the rows (horizontal)
print(np.sum(arr, axis=1)) # [3, 7]
🔄 Reshaping and Transposing
You can change the shape of an array without changing its data using .reshape() or .T (Transpose).
flat = np.arange(1, 7) # [1, 2, 3, 4, 5, 6]
# Reshape into a 2x3 matrix
matrix = flat.reshape(2, 3)
# [[1, 2, 3]
# [4, 5, 6]]
# Transpose matrix (swap rows and columns)
transposed = matrix.T
# [[1, 4]
# [2, 5]
# [3, 6]]
💡 Best Practices
[!important] Avoid Standard Loops Standard loops in Python are interpreted, which adds massive overhead. Vectorized operations execute in compiled C, taking advantage of CPU caches and SIMD instructions.
Example Comparison:
# ❌ Extremely Slow values = np.random.rand(1_000_000) reciprocal = [1 / x for x in values] # ✅ Near Instantaneous reciprocal = 1 / values
[!tip] Use In-place Operations to Save Memory Instead of creating a new copy, perform calculations directly on the existing array if possible using syntax like
+=,-=, or*=.a = np.ones(1000000) b = np.ones(1000000) # Allocates new memory a = a + b # Modifies 'a' in-place (saves memory allocation time) a += b