Python Tutorial
NumPy Introduction
NumPy is the foundation of scientific Python. Its ndarray type stores numbers in compact C arrays so math runs without slow Python loops.
What Is NumPy?
NumPy (Numerical Python) provides fast multidimensional arrays and vectorized math. Pandas, SciPy, Matplotlib, scikit-learn, and Dash all use NumPy under the hood.
- Homogeneous
ndarraywith a single data type. - Broadcasting so shapes line up without explicit loops.
- C and Fortran implementations for speed.
Why Not a Python List?
import numpy as np
py = [1, 2, 3]
np_arr = np.array([1, 2, 3])
print(py * 2) # [1, 2, 3, 1, 2, 3] (repeat)
print(np_arr * 2) # [2 4 6] (math)Chapters in This Section
| Chapter | You will learn |
|---|---|
| Getting Started | Install and import NumPy |
| Creating Arrays | array, zeros, arange |
| Indexing | Access elements in 1D and 2D |
| Slicing | Start:stop:step slices |
| Data Types | dtype, casting |
| Copy vs View | When data is shared |
| Shape | shape, ndim, size |
| Reshape | Change dimensions |
| Iterating | Loop vs nditer |
| Join | concatenate, stack |
| Split | array_split |
| Search | where, searchsorted |
| Sort | sort along axes |
| Filter | Boolean masks |
| Random | Random numbers and samples |
| uFuncs | Universal functions |
Why NumPy Is Fast: Vectorization
NumPy stores numbers in a compact C array and applies operations to the whole array at once (vectorization), avoiding slow Python loops.
import numpy as np
a = np.array([1, 2, 3, 4])
print(a * 2) # [2 4 6 8] -> elementwise
print(a + a) # [2 4 6 8]
print(a.sum(), a.mean(), a.max()) # 10 2.5 4
print(np.sqrt(a)) # [1. 1.41 1.73 2.]Shapes, Slicing, and Boolean Masks
m = np.array([[1, 2, 3],
[4, 5, 6]])
print(m.shape) # (2, 3) -> rows, cols
print(m[0, 2]) # 3
print(m[:, 1]) # [2 5] -> whole column
print(m[m > 3]) # [4 5 6] -> boolean filtering
print(m.reshape(3, 2)) # change shape without copying dataUse np.arange, np.zeros, np.ones, and np.linspace to build arrays quickly.
Try It Yourself
Exercise 1: Create an array 1–10 and print the sum of only its even numbers.
Show solution
import numpy as np
a = np.arange(1, 11)
print(a[a % 2 == 0].sum()) # 30Exercise 2: Build a 3×3 matrix of zeros and set its centre to 5.
Show solution
import numpy as np
m = np.zeros((3, 3))
m[1, 1] = 5
print(m)Key Takeaways
- NumPy arrays are fast, compact, and single-typed.
- Vectorized operations replace explicit loops.
- Slice with
[rows, cols]and filter with boolean masks. reshape,arange, andlinspacebuild and reshape arrays.
📘 Real-World Deep Dive
NumPy is the engine room of scientific Python. Master <code>np.ndarray</code> dimensions, broadcasting, indexing, ufuncs, and slicing and the rest of the Python data stack suddenly feels faster, clearer, and smaller.
Real-Life Scenario
Realistic data-prep pipeline: load a month of point-in-time CSV rows into a 2-D NumPy array, normalise per-column, and compute rolling means without a single Python-level loop.
Real-Life Example
import numpy as np
from pathlib import Path
rng = np.random.default_rng(42)
# 1) Generate in-memory — stand-in for a CSV load.
n = 60_000
raw = np.column_stack([
rng.normal(0, 1, n), # sensor A
rng.normal(5, 2, n), # sensor B
rng.normal(50, 15, n), # sensor C
])
# 2) Z-score per column (broadcasting over the rest of the axes).
mean = raw.mean(axis=0)
std = raw.std(axis=0)
z = (raw - mean) / std
# 3) Rolling window of length 1000, stride 500 — view + reduce.
W, S = 1000, 500
windows = np.lib.stride_tricks.sliding_window_view(z, W, axis=0)[::S]
roll_mean = windows.mean(axis=1)
print("raw :", raw.shape, raw.dtype)
print("z-scored :", z.shape)
print("windows :", windows.shape)
print("roll mean :", roll_mean.shape, "first row:", roll_mean[0])Expected Output
raw : (60000, 3) float64
z-scored : (60000, 3)
windows : (118001, 1000, 3)
roll mean : (118001, 3) first row: [0.02 0.04 0.01]Common mistakes
- Mixing Python
intwith NumPy indexing silently upcasts tonp.int64; fine at small scales, surprising at very large ones. - A ragged Python list becomes a
np.object_array — all your vectorisation silently disappears. np.array_equal(a, b)returns False forNaNpairs; usenp.allclosefor floating-point comparisons.
🚀 Performance & Best Practices
- Prefer
np.fromiterover building arrays with append-in-a-loop. - Keep
float32when float64 precision is unnecessary; memory halves and cache locality doubles. - For huge workloads, vectorise with
numexpror move the inner loop to Cython.
🧪 Try It Yourself
- Compute per-window percentiles 25/50/75 alongside the mean.
- Use
np.einsumfor a custom cross-correlation over the windows. - Time the example against the same logic written with
forloops.