Maths, statistics, and random¶
The built-in operators cover arithmetic; the standard library covers the rest. This notebook tours three modules you'll reach for constantly: math for mathematical functions, statistics for summary statistics, and random for generating random numbers. None needs installing — they ship with Python.
The math module¶
math is a thin, fast wrapper over your platform's C maths library. It works in float (for arbitrary precision use Decimal). Here are the groups you'll use most.
Roots, powers, and logs¶
import math
print(math.sqrt(16)) # 4.0 (always a float)
print(math.isqrt(17)) # 4 integer square root, floored, exact
print(math.hypot(3, 4)) # 5.0 sqrt(3**2 + 4**2)
print(math.exp(1)) # 2.718281828459045 e**1
print(math.log(math.e)) # 1.0 natural log
print(math.log(8, 2)) # 3.0 log base 2
print(math.log10(1000)) # 3.0
Rounding helpers¶
Unlike the built-in round, these always move in a fixed direction: floor down, ceil up, trunc toward zero. They return an int.
import math
print(math.floor(3.7), math.floor(-3.2)) # 3 -4 (toward negative infinity)
print(math.ceil(3.2), math.ceil(-3.7)) # 4 -3 (toward positive infinity)
print(math.trunc(3.7), math.trunc(-3.7)) # 3 -3 (toward zero)
Combinatorics and integer maths¶
Exact integer functions for counting problems and number theory — these stay in int, so they're exact however large the result.
import math
print(math.factorial(5)) # 120
print(math.comb(5, 2)) # 10 ways to choose 2 of 5 (order ignored)
print(math.perm(5, 2)) # 20 ways to arrange 2 of 5 (order matters)
print(math.gcd(12, 18)) # 6 greatest common divisor
print(math.lcm(4, 6)) # 12 lowest common multiple
print(math.prod([1, 2, 3, 4])) # 24 product of an iterable (like sum, but ×)
Trigonometry and constants¶
Trig functions work in radians. math provides the constants and the degrees/radians converters.
import math
print(math.pi, math.e, math.tau) # 3.141592653589793 2.718281828459045 6.283185307179586
print(math.degrees(math.pi)) # 180.0
print(math.radians(180)) # 3.141592653589793
print(round(math.sin(math.pi / 2), 10)) # 1.0
Testing special values¶
From the floating point notebook: test for nan and infinity with these, never with ==. isclose is the safe float comparison.
import math
print(math.isnan(float('nan'))) # True
print(math.isinf(math.inf)) # True
print(math.isclose(0.1 + 0.2, 0.3)) # True
The statistics module¶
For summary statistics without pulling in a third-party library, statistics covers the essentials and works with int, float, Decimal, and Fraction.
import statistics
data = [2, 4, 4, 4, 5, 5, 7, 9]
print(statistics.mean(data)) # 5
print(statistics.median(data)) # 4.5
print(statistics.mode(data)) # 4 (most common value)
print(statistics.pstdev(data)) # 2.0 population standard deviation
print(statistics.stdev(data)) # 2.138... sample standard deviation
pstdev/pvariance treat the data as the whole population; stdev/variance treat it as a sample (dividing by n−1). Reach for the sample versions when your data is a subset drawn from something larger — the usual case. There's also fmean (a faster float mean), median_low/median_high, and quantiles.
import statistics
data = [2, 4, 4, 4, 5, 5, 7, 9]
print(statistics.fmean(data)) # 5.0 — fast float mean
print(statistics.quantiles(data, n=4)) # the three quartile cut points
The random module¶
random generates pseudo-random numbers. "Pseudo" matters: the sequence is determined by a starting seed, so seeding makes runs reproducible — invaluable for tests and reproducible experiments.
import random
random.seed(42) # fix the seed -> same sequence every run
print(random.random()) # 0.6394267984578837 float in [0.0, 1.0)
print(random.uniform(1, 10)) # 1.2250967970040025 a float in [1, 10]
print(random.randint(1, 6)) # an int in [1, 6] inclusive — a dice roll
Choosing from a sequence¶
choice picks one item, choices picks several with replacement (and optional weights), sample picks several without replacement, and shuffle reorders a list in place.
import random
random.seed(42)
colours = ['red', 'green', 'blue', 'yellow']
print(random.choice(colours)) # one item
print(random.sample(colours, 2)) # two distinct items (no repeats)
print(random.choices(colours, k=3)) # three, repeats allowed
print(random.choices(colours, weights=[10, 1, 1, 1], k=5)) # 'red' favoured
deck = [1, 2, 3, 4, 5]
random.shuffle(deck) # shuffles in place
print(deck)
A normal distribution, and a security warning¶
random.gauss(mu, sigma) draws from a normal distribution. But the whole random module is not cryptographically secure — its output is predictable if an attacker learns the state. For passwords, tokens, or anything security-sensitive, use the secrets module instead.
import random, secrets
random.seed(42)
print(random.gauss(0, 1)) # a sample from a standard normal
# For security-sensitive randomness, NOT random:
print(secrets.token_hex(16)) # a secure random token
print(secrets.choice(['a', 'b', 'c'])) # a secure pick
Recap¶
math— roots, logs,floor/ceil/trunc, combinatorics (comb,perm,factorial,gcd,lcm,prod), trig (in radians), constants, and theisnan/isinf/isclosetests.statistics—mean,median,mode, and standard deviation/variance in population (pstdev) and sample (stdev) forms.random— seed for reproducibility;random,uniform,randint,choice,choices(with weights),sample,shuffle,gauss.- For security-sensitive randomness, use
secrets, neverrandom.
That's the toolkit. The Recipes put it to work — rounding correctly, handling money, and formatting numbers for display.