NumPy for Numerical Computing
Reviewed & published by Brayan K
NumPy is the foundation of scientific Python. Its ndarray stores numbers in a fast, contiguous, typed block and runs vectorized operations in compiled C — turning slow Python loops into fast array math used by pandas, scikit-learn, and PyTorch.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • What the ndarray is and how to create arrays
- • array, arange, zeros, ones, linspace
- • dtype, shape, and reshape
- • Vectorized elementwise math and broadcasting
- • Indexing, slicing, and boolean masks
- • Aggregations with sum, mean, and axis
- • Why NumPy is far faster than plain Python lists
🧱 1. The ndarray
import numpy as np
a = np.array([1, 2, 3, 4])
print(a) # [1 2 3 4]
print(type(a)) # <class 'numpy.ndarray'>
print(a.shape) # (4,)
print(a.dtype) # int64
# ✅ Expected output:
# [1 2 3 4]
# <class 'numpy.ndarray'>
# (4,)
# int64🏗️ 2. Creating Arrays
NumPy has dedicated constructors for common patterns:
import numpy as np
print(np.arange(0, 10, 2)) # [0 2 4 6 8]
print(np.zeros(3)) # [0. 0. 0.]
print(np.ones((2, 2))) # 2x2 of ones
print(np.linspace(0, 1, 5)) # [0. 0.25 0.5 0.75 1. ]
# ✅ Expected output:
# [0 2 4 6 8]
# [0. 0. 0.]
# [[1. 1.]
# [1. 1.]]
# [0. 0.25 0.5 0.75 1. ]| Function | Produces |
|---|---|
| np.arange(a, b, step) | Values from a up to b by step |
| np.zeros(shape) / np.ones(shape) | Array filled with 0s or 1s |
| np.linspace(a, b, n) | n evenly spaced points, a..b inclusive |
📐 3. Shape, dtype, and reshape
Every array has a shape and a dtype. You can rearrange the layout with reshape:
import numpy as np
a = np.arange(6) # [0 1 2 3 4 5]
print(a.shape) # (6,)
b = a.reshape(2, 3) # 2 rows, 3 cols
print(b)
# [[0 1 2]
# [3 4 5]]
print(b.dtype) # int64
# ✅ Expected output:
# (6,)
# [[0 1 2]
# [3 4 5]]
# int64The total number of elements must stay the same: a size-6 array can become 2x3 or 3x2, but not 2x4.
⚡ 4. Vectorized Operations
import numpy as np
a = np.array([1, 2, 3, 4])
print(a * 2) # [2 4 6 8]
print(a + 10) # [11 12 13 14]
print(a ** 2) # [ 1 4 9 16]
b = np.array([10, 20, 30, 40])
print(a + b) # [11 22 33 44] (elementwise)
# ✅ Expected output:
# [2 4 6 8]
# [11 12 13 14]
# [ 1 4 9 16]
# [11 22 33 44]📡 5. Broadcasting
Broadcasting lets arrays of different but compatible shapes combine. A dimension of size 1 (or a scalar) is virtually stretched to match:
import numpy as np
matrix = np.array([[1, 2, 3],
[4, 5, 6]]) # shape (2, 3)
row = np.array([10, 20, 30]) # shape (3,)
print(matrix + row)
# [[11 22 33]
# [14 25 36]]
# 'row' was broadcast across both rows
# ✅ Expected output:
# [[11 22 33]
# [14 25 36]]No data was copied to a bigger array — NumPy applies the rule virtually, which keeps it fast and memory-light.
🎯 6. Indexing, Slicing, and Masks
Index like lists, but also filter with boolean masks:
import numpy as np
a = np.array([10, 20, 30, 40, 50])
print(a[0]) # 10
print(a[1:4]) # [20 30 40]
print(a[-1]) # 50
# Boolean mask: keep elements > 25
print(a[a > 25]) # [30 40 50]
# ✅ Expected output:
# 10
# [20 30 40]
# 50
# [30 40 50]The expression a > 25 produces a boolean array; using it to index keeps only the True positions.
📊 7. Aggregations and axis
import numpy as np
a = np.array([[1, 2, 3],
[4, 5, 6]])
print(a.sum()) # 21 (all elements)
print(a.mean()) # 3.5
print(a.sum(axis=0)) # [5 7 9] (down columns)
print(a.sum(axis=1)) # [ 6 15] (across rows)
# ✅ Expected output:
# 21
# 3.5
# [5 7 9]
# [ 6 15]axis=0 collapses rows (one result per column); axis=1 collapses columns (one result per row).
🧪 Worked Example: A Whole Sensor Report
Each idea above is small on its own. This is what they look like working together: two temperature sensors, three readings each, and a report built without writing a single loop. Read the comments, run it, then change a number and predict what moves.
import numpy as np
# Two sensors (rows), three readings each (columns), in degrees Celsius.
temps = np.array([[21.5, 22.0, 23.5],
[19.0, 18.5, 20.0]])
print("shape :", temps.shape) # (2, 3) -> 2 rows, 3 columns
print("dtype :", temps.dtype) # float64 -> every element is a 64-bit float
print("size :", temps.size) # 6 -> total number of elements
# Vectorised maths: this ONE expression touches all six numbers.
# No Python loop is written, and none runs - the work happens in compiled C.
fahrenheit = temps * 9 / 5 + 32
print("fahrenheit:")
print(fahrenheit)
# axis=1 collapses the columns -> one average per SENSOR (per row).
print("mean per sensor:", temps.mean(axis=1))
# axis=0 collapses the rows -> one average per READING SLOT (per column).
print("mean per slot :", temps.mean(axis=0))
# A comparison does not give one True/False - it gives an ARRAY of them.
warm_mask = temps > 21
print("mask:")
print(warm_mask)
# Indexing with that mask keeps only the True positions, flattened to 1-D.
print("warm readings:", temps[warm_mask])
# True counts as 1, so summing a mask counts the matches.
print("how many warm?", warm_mask.sum())
# ✅ Expected output:
# shape : (2, 3)
# dtype : float64
# size : 6
# fahrenheit:
# [[70.7 71.6 74.3]
# [66.2 65.3 68. ]]
# mean per sensor: [22.33333333 19.16666667]
# mean per slot : [20.25 20.25 21.75]
# mask:
# [[ True True True]
# [False False False]]
# warm readings: [21.5 22. 23.5]
# how many warm? 3🎯 Your Turn: Reshape, Aggregate, Filter
Three blanks, one for each of the ideas you have just met. Fill them in, run it, and check your output against the expected block at the bottom of the snippet.
import numpy as np
# 🎯 YOUR TURN — replace each ___ and run it.
scores = np.arange(1, 13) # the numbers 1 to 12 in a flat array
print(scores)
grid = scores.___(3, 4) # 👉 rearrange the 12 values into 3 rows of 4
print(grid)
# 👉 which axis collapses the ROWS and leaves one total per column? 0 or 1?
print("column totals:", grid.sum(axis=___))
# 👉 finish the comparison so only values greater than 8 survive the mask
print("big values :", grid[grid ___ 8])
# ✅ Expected output:
# [ 1 2 3 4 5 6 7 8 9 10 11 12]
# [[ 1 2 3 4]
# [ 5 6 7 8]
# [ 9 10 11 12]]
# column totals: [15 18 21 24]
# big values : [ 9 10 11 12]🚀 8. Why NumPy Is Faster Than Lists
| Aspect | Python list | NumPy ndarray |
|---|---|---|
| Memory layout | Scattered pointers | Contiguous block |
| Element type | Mixed, boxed objects | Single fixed dtype |
| Loop execution | Python bytecode | Compiled C |
For heavy numeric work, NumPy is often tens to hundreds of times faster than equivalent pure-Python loops.
🎯 Mini-Challenge: The Exam Board Report
Nothing is pre-written this time. You have a table of marks: three students down the rows, three exam papers across the columns. Produce the report described in the brief using array operations only — if you find yourself writing a for loop, there is a NumPy way to do it instead.
# 🎯 MINI-CHALLENGE: exam board report
#
# 1. import numpy as np
# 2. Build the marks table (3 students down, 3 papers across):
# scores = np.array([[58, 71, 90],
# [42, 65, 77],
# [88, 95, 61]])
# 3. Print "student means:" followed by each STUDENT's average, rounded to
# 1 decimal place (np.round(values, 1) does the rounding)
# 4. Print "paper highs :" followed by the highest mark on each PAPER
# 5. Print "marks under 60:" followed by how many marks in the whole table
# are below 60 (a mask plus .sum())
# 6. Add 5 bonus marks to every mark but never go above 100, then print it
# under the heading "after bonus:" (np.minimum(array, 100) caps values)
#
# ✅ Expected output:
# student means: [73. 61.3 81.3]
# paper highs : [88 95 90]
# marks under 60: 2
# after bonus:
# [[ 63 76 95]
# [ 47 70 82]
# [ 93 100 66]]
# your code here🎉 Conclusion
With NumPy you can:
✔ Store numbers in fast, typed ndarrays
✔ Create arrays with arange, zeros, ones, linspace
✔ Run vectorized math and broadcasting
✔ Filter data with slicing and boolean masks
✔ Aggregate along any axis
Think in arrays, not loops — that is the NumPy mindset.
📋 Quick Reference — NumPy
| Syntax | What it does |
|---|---|
| np.array([1, 2, 3]) | Build an ndarray from a list |
| np.linspace(0, 1, 5) | Evenly spaced values |
| a.reshape(2, 3) | Reorganize into a new shape |
| a[a > 25] | Boolean mask filtering |
| a.sum(axis=0) | Aggregate along an axis |
🎉 Great work! You've completed this lesson.
You now think in arrays and can run fast vectorized numeric code with NumPy.
Practice quiz
What is the core data structure provided by NumPy?
- The ndarray
- The Series
- The matrix list
- The DataFrame
Answer: The ndarray. NumPy's central object is the ndarray (n-dimensional array), a fixed-type, contiguous block of numbers.
Which function creates evenly spaced values like 0, 2, 4, 6, 8?
- np.linspace(0, 10)
- np.ones(5)
- np.arange(0, 10, 2)
- np.zeros(5)
Answer: np.arange(0, 10, 2). np.arange(start, stop, step) works like range() but returns an ndarray, here 0,2,4,6,8.
What does np.linspace(0, 1, 5) return?
- Five integers from 0 to 5
- Five evenly spaced values from 0 to 1 inclusive
- An error
- Five random numbers
Answer: Five evenly spaced values from 0 to 1 inclusive. linspace(start, stop, num) returns num evenly spaced points including both endpoints: 0, 0.25, 0.5, 0.75, 1.
What does the .dtype attribute of an array tell you?
- The number of dimensions
- The total number of elements
- The shape
- The element data type (e.g. int64, float64)
Answer: The element data type (e.g. int64, float64). dtype reports the data type of the array's elements; all elements share one dtype.
If a has shape (2, 3), what does a.reshape(3, 2) produce?
- A 3x2 array with the same 6 elements
- A transposed copy with swapped values
- A 2x3 array unchanged
- An error, sizes differ
Answer: A 3x2 array with the same 6 elements. reshape returns a new view with the same data and total size (6 elements) arranged as 3 rows of 2.
What does arr * 2 do on a NumPy array arr?
- Doubles only the first element
- Doubles every element (vectorized)
- Concatenates arr to itself
- Raises a TypeError
Answer: Doubles every element (vectorized). Arithmetic is elementwise and vectorized: each element is multiplied by 2, with no Python loop.
What is broadcasting in NumPy?
- Sorting arrays in place
- Sending arrays over a network
- Printing arrays to the console
- Automatically stretching shapes so arrays of different sizes can combine elementwise
Answer: Automatically stretching shapes so arrays of different sizes can combine elementwise. Broadcasting lets NumPy combine arrays of compatible (not identical) shapes by virtually expanding dimensions of size 1.
Given a = np.array([1, 2, 3, 4]), what is a[a > 2]?
- array([3, 4])
- array([True, True])
- array([1, 2])
- array([2, 3, 4])
Answer: array([3, 4]). Boolean masking keeps elements where the condition is True, so a[a > 2] is array([3, 4]).
For a 2D array a, what does a.sum(axis=0) compute?
- The sum across each row
- The number of rows
- The sum down each column
- The sum of every element
Answer: The sum down each column. axis=0 collapses the rows, producing one sum per column; axis=1 would sum across each row.
Why is a NumPy vectorized operation usually faster than a Python for-loop?
- It skips some elements
- It runs as optimized C on contiguous typed data instead of per-element Python bytecode
- It uses more memory
- It always uses the GPU
Answer: It runs as optimized C on contiguous typed data instead of per-element Python bytecode. NumPy stores homogeneous data contiguously and runs loops in compiled C, avoiding per-element Python interpreter overhead.
Continue this course
- Previous: Data Validation with Pydantic
- Next: Data Visualization with Matplotlib — Line, scatter, bar and histogram plots with the pyplot and object-oriented APIs
- Quick reference: Python cheat sheet