NumPy Arrays Explained Simply
A list built for math
In this lesson
The first of the three libraries. NumPy exists for one reason: doing math to a lot of numbers at once, quickly and in very little code.
Nearly everything else in data Python is built on top of it (pandas stores its columns as NumPy arrays), so half an hour here makes the next two lessons much easier.
Explain it like I’m 5
A NumPy array is a list built for math. Instead of visiting each number in turn, you do one operation to all of them at once.
The loop you stop writing
To add tax to every price in a list, you loop. With an array, you multiply the whole thing, and the loop disappears.
That is called a vectorized operation: one instruction applied to every element. It is shorter to read, and much faster, because the looping happens in compiled code rather than in Python.
import numpy as np
prices_list = [1.20, 2.50, 0.60, 1.80]
# The Python way: a loop, or a comprehension.
with_tax_loop = [price * 1.2 for price in prices_list]
# The NumPy way: one operation, applied to everything.
prices = np.array(prices_list)
with_tax = prices * 1.2
print("list :", [round(p, 2) for p in with_tax_loop])
print("array:", with_tax.round(2))
print("same answer:", np.allclose(with_tax_loop, with_tax))
list : [1.44, 3.0, 0.72, 2.16] array: [1.44 3. 0.72 2.16] same answer: True
Shape, slicing, and quick statistics
An array's shape is how many items it has along each dimension. A plain row of six numbers has shape (6,). A table of 3 rows and 4 columns has shape (3, 4). You will spend real time reading shapes, because most NumPy errors are shape errors.
Arrays also carry their own statistics (.mean(), .max(), .sum()) and can be filtered with a condition, which is the idea pandas builds on directly.
import numpy as np
temps = np.array([12, 15, 19, 22, 17, 14])
print("values:", temps)
print("shape: ", temps.shape)
print("mean: ", temps.mean())
print("max: ", temps.max())
print("first three:", temps[:3])
print("above 15: ", temps[temps > 15])
values: [12 15 19 22 17 14] shape: (6,) mean: 16.5 max: 22 first three: [12 15 19] above 15: [19 22 17]
When shapes do not line up
Arithmetic between two arrays happens element by element, which requires their shapes to be compatible. When they are not, NumPy refuses rather than guessing, and the error message tells you both shapes, which is usually enough to spot the mistake.
import numpy as np
a = np.array([1, 2, 3])
b = np.array([10, 20])
try:
print(a + b)
except ValueError as err:
print("ValueError:", str(err).strip())
ValueError: operands could not be broadcast together with shapes (3,) (2,)
When not to reach for NumPy
NumPy is for numbers, all of the same type. That is the source of its speed and also its limit.
For a handful of values, a plain list is simpler and the speed difference is irrelevant. For rows mixing names, dates, and categories, an array is the wrong shape of tool entirely. That is what pandas is for, and it is the next lesson. Reach for NumPy when you have a lot of numbers and want to do math to all of them.
Convert a whole array of temperatures from Celsius to Fahrenheit in one expression. The formula is C × 9 / 5 + 32. Write it once, applied to the entire array, with no loop.
import numpy as np
celsius = np.array([0, 12, 21, 30, 37])
# TODO: convert every value at once, no loop
fahrenheit = celsius
print("celsius: ", celsius)
print("fahrenheit:", fahrenheit)
print("warmest F: ", fahrenheit.max())
Write the formula as if celsius were a single number: celsius * 9 / 5 + 32. NumPy applies it to every element for you. Note the result prints as decimals: dividing turns the whole array into floats.
import numpy as np
celsius = np.array([0, 12, 21, 30, 37])
fahrenheit = celsius * 9 / 5 + 32
print("celsius: ", celsius)
print("fahrenheit:", fahrenheit)
print("warmest F: ", fahrenheit.max())
celsius: [ 0 12 21 30 37]
fahrenheit: [32. 53.6 69.8 86. 98.6]
warmest F: 98.6
Common mistake: Expecting list math and array math to match
They look identical, and both are sequences of numbers.
On a list, * repeats and + joins. On an array they do arithmetic to every element. Convert with np.array(...) first.
Common mistake: Ignoring the shape in an error message
“could not be broadcast together” reads like something deep is wrong.
The two shapes are printed right there. Compare them, and print .shape on both arrays.
Common mistake: Using arrays for mixed text-and-number data
NumPy accepts it without complaint, so it looks supported.
Mixed types force everything to text and you lose the math. Use a pandas DataFrame for tables with names, dates, or categories.
Common mistake: Reaching for NumPy for five numbers
It is the new tool, so it feels like the better one.
The speed only matters in bulk. A short list plus sum() is clearer for small jobs.
What does vectorized mean?
You write the operation once and NumPy applies it across the whole array in compiled code.
What does an array's shape describe?
Six numbers in a row is (6,); three rows of four is (3, 4).
What does temps[temps > 15] give you?
The condition produces True/False, and using it as an index keeps the True positions, which is boolean indexing.
Mini exercise (medium)
A week of rainfall readings in millimeters. Using array operations rather than loops, find the total rainfall, how many days were completely dry, the wettest day's reading, and every reading above the week's average.
Take the wheel. Complete the code, hit Run, and check your output right here.
import numpy as np
rainfall = np.array([2.0, 0.0, 5.5, 1.2, 0.0, 8.1, 3.3])
total = 0 # TODO: total rainfall for the week
dry_days = 0 # TODO: how many days recorded exactly 0
wettest = 0 # TODO: the largest single reading
above_average = np.array([]) # TODO: every reading above the week's mean
print("total mm: ", round(float(total), 1))
print("dry days: ", int(dry_days))
print("wettest day mm:", round(float(wettest), 1))
print("above average: ", above_average)
.sum(), .max(), and .mean() are methods on the array. For dry days, (rainfall == 0) gives True/False and .sum() counts the Trues, because True counts as 1. For the last one, filter with rainfall[rainfall > rainfall.mean()].
import numpy as np
rainfall = np.array([2.0, 0.0, 5.5, 1.2, 0.0, 8.1, 3.3])
total = rainfall.sum()
dry_days = (rainfall == 0).sum()
wettest = rainfall.max()
above_average = rainfall[rainfall > rainfall.mean()]
print("total mm: ", round(float(total), 1))
print("dry days: ", int(dry_days))
print("wettest day mm:", round(float(wettest), 1))
print("above average: ", above_average)
total mm: 20.1
dry days: 2
wettest day mm: 8.1
above average: [5.5 8.1 3.3]
import numpy as np
assert round(float(total), 1) == 20.1, "sum() totals the whole array"
assert int(dry_days) == 2, "(rainfall == 0).sum() counts the True values"
assert round(float(wettest), 1) == 8.1, "max() gives the largest reading"
assert isinstance(above_average, np.ndarray), "filtering an array should give an array"
assert len(above_average) == 3, "three readings beat the mean"
print("✓ Looks good!")