Lesson 3 of 4
Do element-wise maths on arrays, summarise rows and columns with axis, and filter data with Boolean conditions.
About 30 minutes
axisWhen you combine two arrays of the same shape with an operator, NumPy pairs up the elements in matching positions: the first with the first, the second with the second, and so on. When you combine an array with a single number, that number is used with every element. (NumPy calls this broadcasting: the single value is broadcast across the array.)
import numpy as npforce = np.array([1000.0, 2000.0, 3000.0, 4000.0]) # Narea = 20.0e-6 # m² (20 mm²)original_length = 50.0 # mmextension = np.array([0.012, 0.024, 0.036, 0.049]) # mmstress = force / area / 1e6 # MPastrain = extension / original_lengthmodulus = stress / strain / 1000 # GPaprint("Stress (MPa):", stress)print("Strain: ", strain)print("Modulus (GPa):", np.round(modulus, 1))Shapes must match
ValueError saying the shapes could not be broadcast together.NumPy's mathematical functions, sometimes called universal functions, apply to every element. Use them instead of the math module whenever you work with arrays: math.exp() only accepts a single number, while np.exp() accepts a whole array.
| Function | Calculates |
|---|---|
np.sqrt(a) | Square root |
np.exp(a) | eˣ |
np.log(a), np.log10(a) | Natural and base-10 logarithm |
np.sin(a), np.cos(a) | Trigonometry (radians) |
np.abs(a) | Absolute value |
np.round(a, n) | Round to n decimal places |
Diffusion follows an Arrhenius law, D = D₀ exp(−Q / RT). Calculate the diffusion coefficient of carbon in iron across a range of temperatures.
import numpy as npd0 = 2.3e-5 # m²/sq = 148_000 # J/mol, activation energyr = 8.314 # J/(mol·K)temperatures = np.array([800, 900, 1000, 1100, 1200]) # Kdiffusivity = d0 * np.exp(-q / (r * temperatures))for temperature, d in zip(temperatures, diffusivity): print(f"{temperature} K: {d:.2e} m²/s")zip() pairs up items from two sequences so a for loop can unpack them together. Notice how strongly diffusion depends on temperature: about 1,700 times faster at 1200 K than at 800 K.
Functions such as np.mean() and np.max() reduce a whole array to one number by default. For a 2D table you usually want one result per row or per column. The axis argument chooses the direction: axis=0 works down the columns (one result per column), and axis=1 works across the rows (one result per row).
import numpy as np# Rows: three samples. Columns: hardness at 4 positions.hardness = np.array([ [152, 160, 158, 149], [201, 198, 205, 199], [175, 172, 178, 176],])print("Mean of each sample: ", hardness.mean(axis=1))print("Mean at each position:", np.round(hardness.mean(axis=0), 1))print("Overall mean:", round(hardness.mean(), 1))Comparing an array with a value gives an array of True/False values, one per element. This is called a Boolean mask. Putting a mask inside square brackets selects only the elements where the mask is True. This is the NumPy way to filter data, with no loop needed.
import numpy as npdensities = np.array([2.70, 4.51, 7.87, 8.96, 19.32, 1.74])names = np.array(["Al", "Ti", "Fe", "Cu", "Au", "Mg"])is_light = densities < 5print(is_light)print(densities[is_light])print(names[is_light])print("Number of light metals:", np.sum(is_light))The same mask can select from any array of the same length, so a mask built from densities picks the matching names. np.sum() of a mask counts the True values, because True counts as 1 and False as 0.
Combine conditions with & (and) and | (or), putting each condition in brackets. Python's and and or don't work element by element on arrays.
import numpy as npreadings = np.array([8.94, 8.97, 9.31, 8.95, 8.60, 8.96])valid = (readings > 8.9) & (readings < 9.0)print(readings[valid])print(f"Mean of valid readings: {readings[valid].mean():.3f}")Choose an answer and press Check answer. You can retry as often as you like.