Skip to main content
LearnMLAcademy
Beginner10 min read

NumPy Essentials for ML

Master NumPy array indexing, broadcasting, matrix math, and operations, with Python code, architecture diagrams, worked examples, and interview quiz.

What you will master in this lesson
Intuitive core mechanisms & practical real-world intuition
Step-by-step mathematical breakdown & visual diagrams
Runnable Python implementation & practical trade-offs

NumPy for Machine Learning

NumPy (Numerical Python) is one of the most important numerical libraries in the Python ML ecosystem. pandas and scikit-learn work closely with NumPy arrays, while frameworks such as TensorFlow and PyTorch use their own tensor objects with similar array-based ideas. Learning NumPy gives you a strong foundation for understanding how numerical data is represented and processed in ML.

NumPy in Simple Words

Think of NumPy as a fast way to store and calculate with many numbers together. In Machine Learning, one row often represents one example and one column represents one feature.

StudentStudy HoursAttendance %Previous Marks
A27055
B48572
C59080

Rows

Each row is one student (one sample).

Columns

Each numeric column is a feature.

NumPy Array

NumPy can store the numeric part as one 2D array.

Python List vs NumPy Array

QuestionPython ListNumPy Array
Main useGeneral Python dataNumerical calculations
Element typesCan mix typesUsually one data type
Math on all valuesUsually needs a loop/comprehensionVectorised operations
⚡

Fast vectorised ops

Often much faster than Python loops

🔢

N-dimensional arrays

Scalars, vectors, matrices, tensors

🧮

Linear algebra

dot, matmul, inv, eig

📊

Statistical functions

mean, std, percentile, corrcoef

python
# Installation (run once in terminal)
pip install numpy

# Import convention — always use 'np' alias
import numpy as np
print(np.__version__)   # prints the NumPy version installed on your system
Python 3.12 · Verified
Example: 2.x (your version may differ)
🔢

1. Creating Arrays

The ndarray is NumPy's core data structure — think of it as a turbo-charged Python list

python
# From Python list
a = np.array([10, 20, 30, 40, 50])
print(a)
print(type(a))
print(a.dtype)    # data type of elements
print(a.shape)    # tuple: (5,) means 1D array of 5 elements
print(a.ndim)     # number of dimensions
Python 3.12 · Verified
[10 20 30 40 50]
<class 'numpy.ndarray'>
int64
(5,)
1

See a 2D Array Before Coding It

Here the array has 3 rows and 3 columns, so its shape is (3, 3).

123456789
shape = (3, 3)
3 rows × 3 columns
ndim = 2
It has two axes
size = 9
9 values in total
python
# 2D array (matrix) — common in ML for feature matrices
X = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])
print(X.shape)   # (3, 3) → 3 rows, 3 columns (3 samples × 3 features)
print(X.ndim)    # 2 dimensions
Python 3.12 · Verified
(3, 3)
2
python
# Built-in array creators — essential in ML
np.zeros((3, 4))          # 3×4 matrix of zeros  (initialise weights)
np.ones((2, 5))           # 2×5 matrix of ones
np.eye(4)                 # 4×4 identity matrix
np.arange(0, 10, 2)       # [0, 2, 4, 6, 8]  — like range()
np.linspace(0, 1, 5)      # [0. 0.25 0.5 0.75 1.0]  — evenly spaced
np.random.randn(3, 3)     # 3×3 standard normal random values
np.random.randint(1,100, size=(4,)) # 4 random integers 1–99

print(np.arange(0, 10, 2))
print(np.linspace(0, 1, 5))
Python 3.12 · Verified
[0 2 4 6 8]
[0.   0.25 0.5  0.75 1.  ]
✂️

2. Indexing & Slicing

Access specific rows, columns, or subsets — critical for train/test splits and feature extraction

python
a = np.array([10, 20, 30, 40, 50])

# Basic indexing (0-based)
print(a[0])     # 10  — first element
print(a[-1])    # 50  — last element
print(a[1:4])   # [20 30 40]  — slice (start:stop, stop excluded)
print(a[::2])   # [10 30 50]  — every 2nd element
Python 3.12 · Verified
10
50
[20 30 40]
[10 30 50]

How 2D Indexing Looks

For X[row, column], choose the row first and then the column.

Col 0Col 1Col 2
Row 0123
Row 1456
Row 2789

X[:, 1] means: all rows, but only column 1 → [2, 5, 8].

python
# 2D indexing — very common in ML
X = np.array([[1,2,3],[4,5,6],[7,8,9]])

print(X[0])         # First row:   [1 2 3]
print(X[:, 1])      # Second column: [2 5 8]
print(X[0:2, 1:3])  # Rows 0-1, Cols 1-2  — submatrix
print(X[-1, -1])    # Last row, last col: 9
Python 3.12 · Verified
[1 2 3]
[2 5 8]
[[2 3]
 [5 6]]
9
python
# Boolean (conditional) indexing — filter rows meeting a condition
scores = np.array([45, 72, 88, 55, 91, 63])

# Who scored above 70?
mask = scores > 70
print(mask)           # [False  True  True False  True False]
print(scores[mask])   # [72 88 91]

# One-liner
print(scores[scores > 70])
Python 3.12 · Verified
[False  True  True False  True False]
[72 88 91]
[72 88 91]
➕

3. Vectorised Math Operations

Apply numerical operations across arrays without writing a Python loop for each element

Vectorisation: One Instruction, Many Numbers

[1, 2, 3, 4]
× 3 →
[3, 6, 9, 12]

You write one NumPy operation; NumPy applies it across the array without you writing a Python loop for each value.

python
a = np.array([1, 2, 3, 4])

# Element-wise arithmetic (no loop needed!)
print(a + 10)      # [11 12 13 14]
print(a * 3)       # [ 3  6  9 12]
print(a ** 2)      # [ 1  4  9 16]
print(np.sqrt(a))  # [1.  1.414 1.732 2.]

# Two arrays
b = np.array([10, 20, 30, 40])
print(a + b)       # [11 22 33 44]
print(a * b)       # [10 40 90 160]
Python 3.12 · Verified
[11 12 13 14]
[ 3  6  9 12]
[ 1  4  9 16]
[1.         1.41421356 1.73205081 2.        ]
[11 22 33 44]
[ 10  40  90 160]

Broadcasting — See the Idea First

NumPy can apply a shorter compatible row of values across several rows automatically.

123−2.53.54.5=-1.5-1.5-1.5
456−2.53.54.5=1.51.51.5

The row [2.5, 3.5, 4.5] is not literally copied first; NumPy follows broadcasting rules so the operation behaves as if it were aligned with each compatible row.

python
# Broadcasting — NumPy aligns compatible shapes for arithmetic
# This subtraction pattern is one idea used in feature scaling.
X = np.array([[1, 2, 3],
              [4, 5, 6]])          # shape (2, 3)
mean = np.array([2.5, 3.5, 4.5])  # shape (3,) → broadcast across rows

print(X - mean)   # subtract mean of each column from every row
Python 3.12 · Verified
[[-1.5 -1.5 -1.5]
 [ 1.5  1.5  1.5]]
📊

4. Statistical Functions

These are the building blocks of data analysis and feature engineering

python
data = np.array([23, 45, 12, 67, 34, 89, 56, 11, 78, 42])

print(f"Mean:     {np.mean(data):.2f}")
print(f"Median:   {np.median(data):.2f}")
print(f"Std dev:  {np.std(data):.2f}")
print(f"Variance: {np.var(data):.2f}")
print(f"Min:      {np.min(data)}, Max: {np.max(data)}")
print(f"25th pct: {np.percentile(data, 25)}")
print(f"75th pct: {np.percentile(data, 75)}")
print(f"Sum:      {np.sum(data)}")
Python 3.12 · Verified
Mean:     45.70
Median:   43.50
Std dev:  25.46
Variance: 648.41
Min:      11, Max: 89
25th pct: 25.75
75th pct: 64.25
Sum:      457

Understanding axis with Student Marks

StudentMathsEnglishScienceRow mean
axis=1
180759081.67
265887275.00
392608579.00
Column mean
axis=0
79.0074.3382.33
axis=0 → combine values down the rows, giving one result per column/subject.
axis=1 → combine values across the columns, giving one result per row/student.
python
# Axis-wise stats — critical for working with feature matrices
X = np.array([[80, 75, 90],   # Student 1: Maths, English, Science
              [65, 88, 72],   # Student 2
              [92, 60, 85]])  # Student 3

print("Column means (avg per subject):", np.mean(X, axis=0))
print("Row means   (avg per student):", np.mean(X, axis=1))
print("Overall max:", np.max(X))
Python 3.12 · Verified
Column means (avg per subject): [79.   74.33 82.33]
Row means   (avg per student):  [81.67 75.   79.  ]
Overall max: 92
🔄

5. Reshaping & Stacking

Reshape arrays to fit model input requirements — one of the most-used operations in ML pipelines

Reshape Changes the Arrangement, Not the Number of Values

Shape (12,)

[1 2 3 4 5 6 7 8 9 10 11 12]

reshape(3, 4) →

Shape (3, 4)

123456789101112

12 values before, 12 values after. A reshape is valid only when the total number of elements is compatible with the requested shape.

python
a = np.arange(1, 13)   # [1 2 3 4 5 6 7 8 9 10 11 12]
print(a.shape)         # (12,)

# Reshape to 2D matrix
B = a.reshape(3, 4)    # 3 rows × 4 columns
print(B)
print(B.shape)         # (3, 4)

# Reshape to 3D tensor (for deep learning)
C = a.reshape(2, 2, 3)
print(C.shape)         # (2, 2, 3)

# -1 lets NumPy infer the dimension automatically
D = a.reshape(-1, 3)   # infer rows → 4 rows × 3 columns
print(D.shape)         # (4, 3)
Python 3.12 · Verified
(12,)
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
(3, 4)
(2, 2, 3)
(4, 3)
python
# Stacking arrays — used to combine features
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(np.hstack([a, b]))       # [1 2 3 4 5 6]  — side by side
print(np.vstack([a, b]))       # [[1 2 3]        — stacked rows
                               #  [4 5 6]]

# Adding a new column to a feature matrix
X    = np.array([[1, 2], [3, 4], [5, 6]])   # 3×2
bias = np.ones((3, 1))                       # 3×1 bias column
print(np.hstack([X, bias]))
Python 3.12 · Verified
[1 2 3 4 5 6]
[[1 2 3]
 [4 5 6]]
[[1. 2. 1.]
 [3. 4. 1.]
 [5. 6. 1.]]
🧮

6. Linear Algebra (Essential for ML)

Dot products and matrix operations appear in many important ML algorithms

Dot Product — A Tiny Numerical Example

Suppose a model has two features [2, 3] and two weights [0.5, 1.0].

Step 1 — Multiply matching positions:
(2 × 0.5) + (3 × 1.0)
Step 2 — Calculate each product:
1 + 3
Step 3 — Add them:
4

So the dot product is 4. ML models repeatedly perform calculations like this when combining feature values with learned weights.

python
# Dot product — at the heart of linear regression and neural networks
# y = X·w  (prediction = features dot weights)
X = np.array([[1, 2], [3, 4], [5, 6]])  # 3 samples × 2 features
w = np.array([0.5, 1.5])               # 2 weights

predictions = np.dot(X, w)   # or X @ w (Python 3.5+)
print("Predictions:", predictions)

# Matrix multiplication
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print("A @ B =")
print(A @ B)
Python 3.12 · Verified
Predictions: [ 3.5  7.5 11.5]
A @ B =
[[19 22]
 [43 50]]
python
# Useful linear algebra operations
A = np.array([[2, 1], [5, 3]])

print("Transpose:\n", A.T)
print("Determinant:", np.linalg.det(A))
print("Inverse:\n", np.linalg.inv(A))

# Eigenvalues & eigenvectors (used in PCA)
vals, vecs = np.linalg.eig(A)
print("Eigenvalues:", vals)
Python 3.12 · Verified
Transpose:
 [[2 5]
  [1 3]]
Determinant: 1.0
Inverse:
 [[ 3. -1.]
  [-5.  2.]]
Eigenvalues: [0.20871215 4.79128785]
🤖

7. Complete ML Example: Linear Regression with NumPy

Build a small linear regression model from scratch using NumPy to understand the matrix idea behind least squares

python
import numpy as np

# Dataset: Study hours → Exam score
hours  = np.array([1, 2, 3, 4, 5, 6, 7, 8])
scores = np.array([52, 58, 64, 70, 75, 82, 88, 95])

# Linear Regression: score = w * hours + b
# Normal-equation form for learning: w = (XᵀX)⁻¹ Xᵀy
# (Production libraries generally use more numerically stable solvers.)
X = np.column_stack([np.ones(len(hours)), hours])  # add bias column
y = scores

# Solve for weights [b, w]
weights = np.linalg.inv(X.T @ X) @ X.T @ y
b, w = weights

print(f"Intercept (b): {b:.2f}")
print(f"Slope (w):     {w:.2f}")
print(f"Equation:  score = {w:.2f} × hours + {b:.2f}")

# Predict for 9 hours of study
new_hours = 9
prediction = w * new_hours + b
print(f"\nPrediction for {new_hours} hours: {prediction:.1f}")
Python 3.12 · Verified
Intercept (b): 45.68
Slope (w):     6.07
Equation:  score = 6.07 × hours + 45.68

Prediction for 9 hours: 100.3

Why did the model predict about 100.3 marks?

The model learned approximately score = 6.07 × hours + 45.68. For 9 hours: (6.07 × 9) + 45.68 ≈ 100.3. The model simply extends the straight-line pattern beyond the training range of 1–8 hours; it does not automatically know that an exam may have a maximum of 100 marks. This is called extrapolation. It is a reminder to combine model output with real-world constraints and domain knowledge.

Common NumPy Questions

What is the difference between shape, ndim, and size?

shape tells you the length of each dimension, ndim tells you how many dimensions there are, and size tells you the total number of values.

Why is broadcasting useful in Machine Learning?

It lets NumPy combine compatible arrays without writing explicit loops. A common example is subtracting one mean value per feature from every row during feature scaling.

Does reshape() always copy the data?

Not necessarily. NumPy returns a view when possible, but a copy can be required in some memory layouts. For beginners, the key idea is that reshaping changes how the same values are organised as long as the element count is compatible.

Should I learn all NumPy linear algebra before Machine Learning?

No. Start with arrays, indexing, vectorised operations, statistics, reshaping, and dot products. Learn deeper linear algebra gradually as algorithms such as PCA and Linear Regression require it.

🎯 NumPy Cheat Sheet — Essential ML Operations

OperationCodeUsed in
Create arraynp.array([1,2,3])Everything
Shape of arrayX.shapeDebugging
ReshapeX.reshape(n, -1)Model inputs
Column meanX.mean(axis=0)Feature scaling
Boolean filterX[X > 0]Data cleaning
Dot productX @ w or np.dot(X,w)Linear/Neural nets
TransposeX.TMatrix math
Stack arraysnp.hstack([a,b])Combining features
Random seednp.random.seed(42)Reproducibility
Concatenatenp.concatenate([a,b])Joining datasets

Apply this concept in a real hands-on project

You have studied the idea. Now use it in working Python code, see the calculation and output, and explain what changed. Each project includes step-by-step instructions, full source and tests.

Practice challenge: predict an outcome first, run the code, and explain the difference.

Track Your Progress0% curriculum done

Finished "NumPy Essentials for ML"?

Mark this lesson as completed to update your progress bar and track your milestones across the full curriculum.

Overall Progress0 / 167 lessons

Was this tutorial helpful?

Your feedback helps us continuously refine and improve our open-access tutorials.

Summary / Key Takeaways

  • NumPy arrays store one data type compactly for fast vectorized operations.
  • Shape records axis lengths, and reshape must preserve element count.
  • Indexing selects values; many slices share memory with the source array.
  • Broadcasting combines compatible shapes without manual copies.
  • Matrix multiplication differs from element-wise multiplication and requires matching inner dimensions.
  • Check shape, dtype, missing values, and unintended views before modelling.

Knowledge Check

Progress: 0/4

Test your active recall. Choose one answer for each question, then check it to reveal the step-by-step reasoning.

Question 1 of 4
What is the fundamental performance difference between a Python list and a NumPy array?
Question 2 of 4
What does NumPy broadcasting allow you to do?
Question 3 of 4
You have an array of shape (1000, 10). What does arr.reshape(100, 100) produce?
Question 4 of 4
What is the difference between b = arr and b = arr.copy() in NumPy?

Knowledge Check Review & Explanations

Review the questions, verified correct answers, and conceptual explanations for this lesson.

1What is the fundamental performance difference between a Python list and a NumPy array?▼

Correct Answer: NumPy arrays store a single data type in contiguous memory, enabling vectorised operations in C that are 10-100x faster than Python loops on equivalent data

Python lists store Python objects (each with type metadata and reference overhead). NumPy arrays store raw C values in a contiguous block, enabling vectorised operations without Python interpreter overhead per element.

2What does NumPy broadcasting allow you to do?▼

Correct Answer: Perform arithmetic between arrays of different but compatible shapes without writing explicit loops — NumPy automatically expands the smaller array to match the larger one

Adding a shape (1000,) bias vector to a shape (1000, 512) weight matrix without broadcasting would require a Python loop over 1000 elements. Broadcasting handles this in a single vectorised C operation.

3You have an array of shape (1000, 10). What does arr.reshape(100, 100) produce?▼

Correct Answer: A new (100, 100) view of the same 10,000 elements rearranged according to the new shape

reshape is valid as long as the total number of elements is preserved: 1000×10 = 100×100 = 10,000. No data is copied — reshape returns a view with a different interpretation of the same memory.

4What is the difference between b = arr and b = arr.copy() in NumPy?▼

Correct Answer: b = arr creates a view sharing the same memory (modifying b modifies arr); arr.copy() creates an independent copy with its own memory

This is one of the most common NumPy bugs. In-place modification of what looks like a 'copy' unintentionally corrupts the original. Use .copy() or np.copy() whenever you need true independence.

Keep moving through the path

Continue Learning

Free Download: ML Interview Cheatsheet

100 essential Machine Learning interview questions and answers covering foundations, algorithms, evaluation, deep learning, Python, system design and interview strategy. Enter your email to download it instantly.

Optional Learning Resources

These resources may complement the lesson. They are optional and are not required to use ML Academy.

As an Amazon Associate I earn from qualifying purchases. Other links are identified separately when they are not affiliate links.

Next