Master NumPy, the high-performance numerical computing library
Introduction
NumPy (Numerical Python) is the foundational library for high-speed numerical computing in Python. Machine learning requires processing large amounts of data efficiently, and NumPy is an essential tool for that purpose.
In this chapter, you will learn the following:
- Creating and performing basic operations on NumPy arrays
- Array shape manipulation (reshape, transpose)
- Indexing and slicing
- Universal functions (mathematical operations)
- Broadcasting
- Statistical functions and linear algebra
Why do we need NumPy?
Compared to Python lists, NumPy arrays are 10 to 100 times faster. In large-scale data processing, this speed difference becomes critical.
1. Creating NumPy Arrays
1.1 Basic Array Creation
Example 1: How to Create Arrays
import numpy as np
# Create an array from a list
arr1 = np.array([1, 2, 3, 4, 5])
print("1D array:", arr1)
print("Type:", type(arr1))
print("Data type:", arr1.dtype)
# Output:
# 1D array: [1 2 3 4 5]
# Type: <class 'numpy.ndarray'>
# Data type: int64
# 2D array (matrix)
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print("\n2D array:")
print(arr2)
print("Shape:", arr2.shape) # (2, 3) = 2 rows, 3 columns
print("Number of dimensions:", arr2.ndim)
print("Number of elements:", arr2.size)
# Output:
# 2D array:
# [[1 2 3]
# [4 5 6]]
# Shape: (2, 3)
# Number of dimensions: 2
# Number of elements: 6
# Specify the data type
arr3 = np.array([1.5, 2.3, 3.7], dtype=np.float32)
print("\nfloat32 array:", arr3)
print("Data type:", arr3.dtype)
1.2 Convenient Array Creation Functions
Example 2: Creating Special Arrays
import numpy as np
# Array filled with zeros
zeros = np.zeros((3, 4)) # 3 rows, 4 columns
print("Zero array:")
print(zeros)
# Array filled with ones
ones = np.ones((2, 3))
print("\nOnes array:")
print(ones)
# Consecutive values
arange = np.arange(0, 10, 2) # From 0 to less than 10, step 2
print("\narange:", arange) # [0 2 4 6 8]
# Evenly spaced values
linspace = np.linspace(0, 1, 5) # 5 values from 0 to 1
print("linspace:", linspace) # [0. 0.25 0.5 0.75 1. ]
# Identity matrix
identity = np.eye(3) # 3x3 identity matrix
print("\nIdentity matrix:")
print(identity)
# Random array
np.random.seed(42) # Seed for reproducibility
random = np.random.rand(2, 3) # Uniform distribution over 0-1
print("\nRandom array:")
print(random)
# Random numbers following a normal distribution
normal = np.random.randn(3, 3) # Mean 0, standard deviation 1
print("\nNormal distribution:")
print(normal)
2. Array Shape Manipulation
Example 3: reshape, flatten, transpose
import numpy as np
# Original array
arr = np.arange(12)
print("Original array:", arr) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
print("Shape:", arr.shape) # (12,)
# reshape: change the shape
reshaped = arr.reshape(3, 4) # Reshape into 3 rows, 4 columns
print("\nreshape (3, 4):")
print(reshaped)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# Automatic calculation using -1
reshaped2 = arr.reshape(2, -1) # 2 rows, number of columns computed automatically
print("\nreshape (2, -1):")
print(reshaped2) # Becomes 2 rows, 6 columns
# flatten: convert to a 1D array
flattened = reshaped.flatten()
print("\nflatten:", flattened)
# [ 0 1 2 3 4 5 6 7 8 9 10 11]
# transpose: swap rows and columns
transposed = reshaped.T
print("\ntranspose:")
print(transposed)
# [[ 0 4 8]
# [ 1 5 9]
# [ 2 6 10]
# [ 3 7 11]]
# Swap axes of a multidimensional array
arr3d = np.arange(24).reshape(2, 3, 4)
print("\nShape of 3D array:", arr3d.shape) # (2, 3, 4)
swapped = np.swapaxes(arr3d, 0, 2)
print("After swapping axes:", swapped.shape) # (4, 3, 2)
3. Indexing and Slicing
Example 4: Accessing Array Elements
import numpy as np
# 1D array
arr1d = np.array([10, 20, 30, 40, 50])
print("Array:", arr1d)
print("arr1d[0]:", arr1d[0]) # 10
print("arr1d[-1]:", arr1d[-1]) # 50 (last element)
print("arr1d[1:4]:", arr1d[1:4]) # [20 30 40]
# 2D array
arr2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
print("\n2D array:")
print(arr2d)
# Element access
print("arr2d[0, 0]:", arr2d[0, 0]) # 1
print("arr2d[1, 2]:", arr2d[1, 2]) # 7 (row 2, column 3)
# Retrieve a row
print("First row:", arr2d[0]) # [1 2 3 4]
print("All rows, column 2:", arr2d[:, 1]) # [ 2 6 10]
# Slicing
print("Sub-array:")
print(arr2d[0:2, 1:3])
# [[2 3]
# [6 7]]
# Boolean indexing
mask = arr2d > 5
print("\nMask for elements greater than 5:")
print(mask)
print("Elements greater than 5:", arr2d[mask])
# [ 6 7 8 9 10 11 12]
# Replace elements that satisfy a condition
arr_copy = arr2d.copy()
arr_copy[arr_copy > 5] = 0
print("\nSet elements greater than 5 to 0:")
print(arr_copy)
4. Universal Functions
Example 5: Mathematical Operations
import numpy as np
# Create arrays
a = np.array([1, 2, 3, 4])
b = np.array([10, 20, 30, 40])
# Basic operations (element-wise)
print("a + b =", a + b) # [11 22 33 44]
print("a - b =", a - b) # [-9 -18 -27 -36]
print("a * b =", a * b) # [10 40 90 160]
print("a / b =", a / b) # [0.1 0.1 0.1 0.1]
print("a ** 2 =", a ** 2) # [ 1 4 9 16]
# Mathematical functions
arr = np.array([0, np.pi/6, np.pi/4, np.pi/3, np.pi/2])
print("\nTrigonometric functions:")
print("sin:", np.sin(arr))
print("cos:", np.cos(arr))
print("tan:", np.tan(arr))
# Exponential and logarithmic functions
x = np.array([1, 2, 3, 4])
print("\nExponential and logarithmic:")
print("exp(x):", np.exp(x)) # e^x
print("log(x):", np.log(x)) # Natural logarithm
print("log10(x):", np.log10(x)) # Common logarithm
print("sqrt(x):", np.sqrt(x)) # Square root
# Other functions
y = np.array([-2.5, -1.5, 0.5, 1.5, 2.5])
print("\nOthers:")
print("abs(y):", np.abs(y)) # Absolute value
print("ceil(y):", np.ceil(y)) # Round up
print("floor(y):", np.floor(y)) # Round down
print("round(y):", np.round(y)) # Round to nearest
# Maximum and minimum
print("\nMax and min:")
print("max:", np.max(x)) # 4
print("min:", np.min(x)) # 1
print("argmax:", np.argmax(x)) # 3 (index of the maximum value)
print("argmin:", np.argmin(x)) # 0 (index of the minimum value)
5. Broadcasting
Broadcasting is a powerful NumPy feature that performs operations between arrays of different shapes.
Example 6: Broadcasting Examples
import numpy as np
# Operations with a scalar
arr = np.array([1, 2, 3, 4])
print("arr + 10 =", arr + 10) # [11 12 13 14]
print("arr * 2 =", arr * 2) # [2 4 6 8]
# Operations between 1D and 2D
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
vector = np.array([10, 20, 30])
result = matrix + vector
print("\nMatrix + vector:")
print(result)
# [[11 22 33]
# [14 25 36]
# [17 28 39]]
# Row vector and column vector
row = np.array([[1, 2, 3]]) # Shape: (1, 3)
col = np.array([[10], [20], [30]]) # Shape: (3, 1)
result2 = row + col
print("\nRow vector + column vector:")
print(result2)
# [[11 12 13]
# [21 22 23]
# [31 32 33]]
# Practical example: standardization (transform to mean 0, standard deviation 1)
data = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=float)
mean = data.mean(axis=0) # Mean per column
std = data.std(axis=0) # Standard deviation per column
normalized = (data - mean) / std
print("\nStandardization:")
print("Original data:")
print(data)
print("Mean:", mean)
print("Standard deviation:", std)
print("After standardization:")
print(normalized)
6. Statistical Functions
Example 7: Computing Statistics
import numpy as np
# Create data
np.random.seed(42)
data = np.random.randn(100) # 100 normal random numbers
# Basic statistics
print("Mean:", np.mean(data))
print("Median:", np.median(data))
print("Standard deviation:", np.std(data))
print("Variance:", np.var(data))
print("Minimum:", np.min(data))
print("Maximum:", np.max(data))
print("Range:", np.ptp(data)) # max - min
# Percentiles
print("\nQuartiles:")
print("25%:", np.percentile(data, 25))
print("50%:", np.percentile(data, 50))
print("75%:", np.percentile(data, 75))
# Statistics of a 2D array (specifying the axis)
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print("\nStatistics of the 2D array:")
print("Total sum:", np.sum(matrix)) # 45
print("Sum per column:", np.sum(matrix, axis=0)) # [12 15 18]
print("Sum per row:", np.sum(matrix, axis=1)) # [ 6 15 24]
print("\nMean per column:", np.mean(matrix, axis=0)) # [4. 5. 6.]
print("Mean per row:", np.mean(matrix, axis=1)) # [2. 5. 8.]
# Cumulative statistics
arr = np.array([1, 2, 3, 4, 5])
print("\nCumulative sum:", np.cumsum(arr)) # [ 1 3 6 10 15]
print("Cumulative product:", np.cumprod(arr)) # [ 1 2 6 24 120]
7. Linear Algebra
Example 8: Matrix Operations
import numpy as np
# Create matrices
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# Element-wise product
print("Element-wise product (A * B):")
print(A * B)
# [[ 5 12]
# [21 32]]
# Matrix product (dot product)
print("\nMatrix product (A @ B):")
print(A @ B) # or np.dot(A, B)
# [[19 22]
# [43 50]]
# Dot product of vectors
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])
print("\nDot product of vectors:", np.dot(v1, v2)) # 32 = 1*4 + 2*5 + 3*6
# Transpose
print("\nTranspose of A:")
print(A.T)
# Inverse matrix
A_inv = np.linalg.inv(A)
print("\nInverse of A:")
print(A_inv)
# Verify that it produces the identity matrix
print("\nA @ A_inv (should be the identity matrix):")
print(A @ A_inv)
# Determinant
det = np.linalg.det(A)
print("\nDeterminant of A:", det)
# Eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(A)
print("\nEigenvalues:", eigenvalues)
print("Eigenvectors:")
print(eigenvectors)
# Norm (magnitude of a vector)
v = np.array([3, 4])
print("\nL2 norm:", np.linalg.norm(v)) # 5.0 = sqrt(3^2 + 4^2)
8. Practical Example: Data Preprocessing
Example 9: Data Preprocessing for Machine Learning
import numpy as np
# Sample data (height, weight, age)
np.random.seed(42)
data = np.random.randn(100, 3) * 10 + [170, 60, 30]
print("Data shape:", data.shape) # (100, 3)
print("First 5 rows:")
print(data[:5])
# 1. Basic statistics
print("\n=== Basic statistics ===")
print("Mean:", data.mean(axis=0))
print("Standard deviation:", data.std(axis=0))
print("Minimum:", data.min(axis=0))
print("Maximum:", data.max(axis=0))
# 2. Handling missing values (data containing NaN)
data_with_nan = data.copy()
data_with_nan[0, 0] = np.nan
data_with_nan[5, 1] = np.nan
print("\n=== Handling missing values ===")
print("Number of missing values:", np.isnan(data_with_nan).sum())
# Impute missing values with the mean
for col in range(data_with_nan.shape[1]):
col_mean = np.nanmean(data_with_nan[:, col])
data_with_nan[np.isnan(data_with_nan[:, col]), col] = col_mean
print("Number of missing values after imputation:", np.isnan(data_with_nan).sum())
# 3. Standardization (Z-score normalization)
mean = data.mean(axis=0)
std = data.std(axis=0)
normalized_data = (data - mean) / std
print("\n=== After standardization ===")
print("Mean:", normalized_data.mean(axis=0)) # Approximately 0
print("Standard deviation:", normalized_data.std(axis=0)) # Approximately 1
# 4. Min-max normalization (scale to 0-1)
min_vals = data.min(axis=0)
max_vals = data.max(axis=0)
min_max_scaled = (data - min_vals) / (max_vals - min_vals)
print("\n=== After min-max normalization ===")
print("Minimum:", min_max_scaled.min(axis=0)) # 0
print("Maximum:", min_max_scaled.max(axis=0)) # 1
Example 10: Manipulating Image Data
import numpy as np
# Data simulating an image (5x5 grayscale image)
image = np.array([
[0, 50, 100, 150, 200],
[50, 100, 150, 200, 250],
[100, 150, 200, 250, 255],
[150, 200, 250, 255, 255],
[200, 250, 255, 255, 255]
], dtype=np.uint8)
print("Original image:")
print(image)
print("Shape:", image.shape) # (5, 5)
# 1. Flipping the image
flipped_h = np.flip(image, axis=1) # Horizontal flip
flipped_v = np.flip(image, axis=0) # Vertical flip
print("\nHorizontal flip:")
print(flipped_h)
# 2. Rotating the image (90 degrees)
rotated = np.rot90(image)
print("\n90-degree rotation:")
print(rotated)
# 3. Cropping the image
cropped = image[1:4, 1:4] # Crop the central 3x3
print("\nCrop (3x3):")
print(cropped)
# 4. Brightness adjustment
brightened = np.clip(image + 50, 0, 255).astype(np.uint8)
print("\nBrightness +50:")
print(brightened)
# 5. Contrast adjustment
contrast = np.clip(image * 1.5, 0, 255).astype(np.uint8)
print("\nContrast x1.5:")
print(contrast)
# 6. Simulating an RGB image (5x5x3)
rgb_image = np.random.randint(0, 256, (5, 5, 3), dtype=np.uint8)
print("\nShape of RGB image:", rgb_image.shape) # (5, 5, 3)
# Mean per channel
print("R channel mean:", rgb_image[:, :, 0].mean())
print("G channel mean:", rgb_image[:, :, 1].mean())
print("B channel mean:", rgb_image[:, :, 2].mean())
Summary
In this chapter, you learned the basics of NumPy:
- ✅ Array creation: np.array, np.zeros, np.ones, np.arange, np.linspace
- ✅ Shape manipulation: reshape, flatten, transpose
- ✅ Indexing: slicing, boolean indexing
- ✅ Mathematical operations: universal functions, trigonometric functions, exponentials and logarithms
- ✅ Broadcasting: operations between arrays of different shapes
- ✅ Statistical functions: mean, std, min, max, sum
- ✅ Linear algebra: dot product, matrix product, inverse matrix, eigenvalues
Next step: In Chapter 3, you will use this knowledge to learn data analysis with Pandas.
Exercises
Exercise 1: Array Manipulation
Problem: Create an array containing the numbers from 1 to 50 and reshape it into a 5-row, 10-column matrix. Then, compute the sum of each row.
# Solution example
import numpy as np
# Array from 1 to 50
arr = np.arange(1, 51)
print("Array:", arr)
# Reshape into 5 rows, 10 columns
matrix = arr.reshape(5, 10)
print("\n5x10 matrix:")
print(matrix)
# Sum of each row
row_sums = matrix.sum(axis=1)
print("\nSum of each row:", row_sums)
# Output: [ 55 155 255 355 455]
# Verification: sum of the first row
print("Verification:", sum(range(1, 11))) # 55
Exercise 2: Boolean Indexing
Problem: From the 100 numbers between 0 and 99, extract the numbers that are multiples of 3 but not multiples of 5.
# Solution example
import numpy as np
# Array from 0 to 99
numbers = np.arange(100)
# Condition: multiple of 3 and not a multiple of 5
condition = (numbers % 3 == 0) & (numbers % 5 != 0)
result = numbers[condition]
print("Result:", result)
print("Count:", len(result))
# Output: [ 3 6 9 12 18 21 24 27 33 36 39 42 48 51 54 57 63 66 69 72 78 81 84 87 93 96 99]
# Count: 27
Exercise 3: Statistical Processing
Problem: Generate 1000 data points following a normal distribution with mean 50 and standard deviation 10, and compute the frequency for each histogram bin (bins: 0-20, 20-40, 40-60, 60-80, 80-100).
# Solution example
import numpy as np
# Generate normally distributed data
np.random.seed(42)
data = np.random.normal(50, 10, 1000)
# Statistics
print("Mean:", data.mean())
print("Standard deviation:", data.std())
# Histogram (frequency count)
bins = [0, 20, 40, 60, 80, 100]
hist, edges = np.histogram(data, bins=bins)
print("\nHistogram:")
for i in range(len(hist)):
print(f"{edges[i]}-{edges[i+1]}: {hist[i]} items")
# Example output:
# 0-20: 22 items
# 20-40: 159 items
# 40-60: 638 items
# 60-80: 175 items
# 80-100: 6 items
Exercise 4: Matrix Operations
Problem: For the following matrix A, find (1) the inverse matrix, (2) the determinant, and (3) the eigenvalues. Also verify that A @ A_inv produces the identity matrix.
\[ A = \begin{bmatrix} 2 & 1 \\ 1 & 3 \end{bmatrix} \]
# Solution example
import numpy as np
A = np.array([[2, 1],
[1, 3]])
# (1) Inverse matrix
A_inv = np.linalg.inv(A)
print("Inverse matrix:")
print(A_inv)
# (2) Determinant
det = np.linalg.det(A)
print("\nDeterminant:", det) # 5.0
# (3) Eigenvalues
eigenvalues, eigenvectors = np.linalg.eig(A)
print("\nEigenvalues:", eigenvalues)
print("Eigenvectors:")
print(eigenvectors)
# Verification: A @ A_inv = I (identity matrix)
identity = A @ A_inv
print("\nA @ A_inv (identity matrix):")
print(identity)
print("Difference from the identity matrix:", np.allclose(identity, np.eye(2)))
Exercise 5: Image Data Processing
Problem: Generate a 10x10 random image data (0-255), then (1) make the entire image twice as bright, and (2) extract the central 5x5 region. Note that brightness must be constrained to the range 0-255.
# Solution example
import numpy as np
# Generate a random image
np.random.seed(42)
image = np.random.randint(0, 256, (10, 10), dtype=np.uint8)
print("Original image:")
print(image)
print("Average brightness:", image.mean())
# (1) Make twice as bright (constrained to 0-255)
brightened = np.clip(image * 2, 0, 255).astype(np.uint8)
print("\nTwice as bright:")
print(brightened)
print("Average brightness:", brightened.mean())
# (2) Extract the central 5x5 (indices 2:7)
center = image[2:7, 2:7]
print("\nCentral 5x5:")
print(center)
print("Shape:", center.shape) # (5, 5)