Chapter 7 — Numerical Computing with NumPy

NumPy is the foundation of scientific Python. Its n-dimensional array (ndarray) and vectorized operations make numerical work fast and concise. Nearly every data-science library builds on NumPy, so mastering it pays off everywhere.

Learning Objectives

Prerequisites / Imports

In [1]:
import numpy as np

1 Creating Arrays

Make arrays from lists or with arange, linspace, zeros, ones, eye.

In [1]:
a = np.array([1, 2, 3, 4])
print('1-D:', a, 'dtype:', a.dtype)

b = np.array([[1, 2, 3], [4, 5, 6]])
print('2-D shape:', b.shape)
b
1-D: [1 2 3 4] dtype: int32
2-D shape: (2, 3)
array([[1, 2, 3],
       [4, 5, 6]])
In [1]:
print('arange:', np.arange(0, 10, 2))
print('linspace:', np.linspace(0, 1, 5))
print('zeros 2x3:'); print(np.zeros((2, 3)))
print('eye 3:'); print(np.eye(3))
arange: [0 2 4 6 8]
linspace: [0.   0.25 0.5  0.75 1.  ]
zeros 2x3:
[[0. 0. 0.]
 [0. 0. 0.]]
eye 3:
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

2 Array Attributes

shape, ndim, size, dtype describe an array.

In [1]:
x = np.random.default_rng(0).random((3, 4))
print('shape:', x.shape, 'ndim:', x.ndim, 'size:', x.size)
print('dtype:', x.dtype)
shape: (3, 4) ndim: 2 size: 12
dtype: float64

3 Indexing and Slicing

Use [row, col]; slices return views.

In [1]:
m = np.arange(12).reshape(3, 4)
print(m)
print('row 1:', m[1])
print('col 2:', m[:, 2])
print('sub-block:', m[0:2, 1:3])
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
row 1: [4 5 6 7]
col 2: [ 2  6 10]
sub-block: [[1 2]
 [5 6]]

4 Boolean Indexing (Masking)

Filter arrays with a Boolean mask — a key data-cleaning technique.

In [1]:
data = np.array([5, 12, 3, 18, 7, 25, 2])
mask = data > 10
print('mask:', mask)
print('values > 10:', data[mask])
mask: [False  True False  True False  True False]
values > 10: [12 18 25]

5 Vectorized Operations

Arithmetic applies elementwise without Python loops — fast and clean.

In [1]:
v = np.array([1, 2, 3, 4])
print('v + 10:', v + 10)
print('v * 2:', v * 2)
print('v ** 2:', v ** 2)
print('v * v:', v * v)
v + 10: [11 12 13 14]
v * 2: [2 4 6 8]
v ** 2: [ 1  4  9 16]
v * v: [ 1  4  9 16]

6 Broadcasting

NumPy expands smaller arrays to match shapes, enabling concise math.

In [1]:
row = np.array([1, 2, 3])        # shape (3,)
col = np.array([[10], [20], [30]])  # shape (3,1)
print('broadcast sum:')
print(col + row)
broadcast sum:
[[11 12 13]
 [21 22 23]
 [31 32 33]]

7 Aggregations

sum, mean, std, min, max accept an axis argument.

In [1]:
m = np.arange(12).reshape(3, 4).astype(float)
print('total mean:', m.mean())
print('column means:', m.mean(axis=0))
print('row sums:', m.sum(axis=1))
print('min/max:', m.min(), m.max())
total mean: 5.5
column means: [4. 5. 6. 7.]
row sums: [ 6. 22. 38.]
min/max: 0.0 11.0

8 Random Numbers

Use np.random.default_rng(seed) for reproducible randomness — vital for simulations and ML.

In [1]:
rng = np.random.default_rng(42)
print('5 uniform [0,1):', rng.random(5))
print('3 integers 1..100:', rng.integers(1, 101, size=3))
print('normal samples:', rng.normal(loc=0, scale=1, size=4))
5 uniform [0,1): [0.77395605 0.43887844 0.85859792 0.69736803 0.09417735]
3 integers 1..100: [53 98 74]
normal samples: [-0.31624259 -0.01680116 -0.85304393  0.87939797]

9 Linear Algebra Basics

np.dot / @ for matrix multiplication; np.linalg for solve and inverse.

In [1]:
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print('A @ B:'); print(A @ B)
print('transpose A:'); print(A.T)
print('det:', np.linalg.det(A))
print('solve Ax = [1,2]:', np.linalg.solve(A, [1, 2]))
A @ B:
[[19 22]
 [43 50]]
transpose A:
[[1 3]
 [2 4]]
det: -2.0000000000000004
solve Ax = [1,2]: [0.  0.5]

Case Study: Standardizing Test Scores

Standardize a synthetic set of exam scores to z-scores ($(x - \mu)/\sigma$) using vectorized NumPy — a routine preprocessing step.

In [1]:
rng = np.random.default_rng(7)
scores = rng.normal(loc=75, scale=12, size=50).round(1)
mu, sigma = scores.mean(), scores.std()
z = (scores - mu) / sigma
print(f'mean={mu:.2f} std={sigma:.2f}')
print('first 5 z-scores:', np.round(z[:5], 2))
print('mean of z (should be ~0):', z.mean().round(10))
print('std of z (should be ~1):', z.std().round(10))
mean=71.49 std=10.62
first 5 z-scores: [ 0.33  0.67  0.02 -0.68 -0.19]
mean of z (should be ~0): -0.0
std of z (should be ~1): 1.0

Exercises

  1. Create a 4x4 array of integers from 0 to 15 and reshape it.
  2. Select the second row and third column of a 3x3 array.
  3. Use a Boolean mask to extract all even numbers from an array.
  4. Add a 1-D array of shape (3,) to a 2-D array of shape (4, 3) using broadcasting.
  5. Compute the column-wise means of a 5x3 random array.
  6. Generate 1000 normal samples and count how many are more than 1 standard deviation from the mean.
  7. Multiply two 2x2 matrices with @ and verify against np.dot.
  8. Standardize a synthetic dataset and confirm the result has mean 0 and std 1.
  9. Explain the difference between a view and a copy of a slice.
  10. Use np.linspace to create 50 points from 0 to 2π and compute their sines.

Python Data Science: From Foundations to Applications — Chapter 7