Python math and statistics
1Math and Statistics: overview
Python's built-in operators, the math module and the statistics module handle everyday calculations such as roots, rounding, logarithms, averages and spread, while Decimal and Fraction give exact answers where ordinary floats are only approximate.
Before this lesson: Python Operators, Python Modules and Imports
2Math and Statistics: diagram
3Math and Statistics: explained step by step
Python can do arithmetic the moment you open it, but real programs soon need more: the long side of a triangle, the number of pages for 47 items, the average mark of a class, an invoice that adds up to the exact cent. This lesson covers the tools for each, and the places where plain decimal numbers (floats) let you down.
Built-in helpers: no import needed
Python already gives you abs() to drop the sign, round() to round, and divmod(a, b), which returns the quotient and the remainder in one call. That turns 135 minutes into hours and minutes.
print(abs(-7.5))
print(round(3.14159, 2))
hours, minutes = divmod(135, 60)
print(hours, "h", minutes, "min")The math module
import math unlocks the functions of a scientific calculator. You call each one as math.name(...), so a reader can see where it came from.
import math
print(math.sqrt(144))
print(math.pi, math.e)
print(math.hypot(3, 4))
print(math.log10(1000), math.log2(8), math.log(math.e))
print(math.factorial(5), math.comb(5, 2), math.perm(5, 2))
print(math.gcd(12, 18), math.lcm(4, 6))sqrt(x)is the square root. It always returns a float.hypot(a, b)is the long side of a right-angled triangle,sqrt(a*a + b*b).log(x)is the natural logarithm,log10andlog2use base 10 and 2, andlog(x, base)takes any base.factorial(n)isn!.comb(n, k)counts the ways to choosekthings out ofnwhen order does not matter, andperm(n, k)when it does.gcdis the greatest common divisor andlcmthe lowest common multiple.
For powers, write 2 ** 3; math.pow(2, 3) always returns a float.
Angles are in radians
sin, cos and tan expect radians, not degrees. Convert with math.radians() on the way in and math.degrees() on the way out.
import math
print(math.sin(90))
print(math.sin(math.radians(90)))
print(round(math.cos(math.radians(60)), 2))
print(math.degrees(math.pi))The first line treats 90 as 90 radians, a different angle altogether.
Four ways to get a whole number
They treat the decimal part differently, and negative numbers show it best:
math.floor(x)always goes down, toward negative infinity.math.ceil(x)always goes up. Use it for "how many boxes or pages do I need?".math.trunc(x)(the same asint(x)) cuts the decimals off, which means toward zero.round(x)picks the nearest whole number. An exact half goes to the even neighbour, soround(2.5)is 2 andround(3.5)is 4. This is banker's rounding: always rounding halves up would push long columns of figures upward, and going to the even side balances that out. A negative second argument rounds to tens or hundreds, soround(1234, -2)is 1200.
import math
for x in (2.5, 3.5, -2.5):
print(x, math.floor(x), math.ceil(x), math.trunc(x), round(x))
print(round(3.14159, 2), round(1234, -2))
print(math.ceil(47 / 10))Floats are close, not exact
Computers store a float in binary, and a value such as 0.1 has no exact binary form. Tiny errors appear, and they surface when you compare: 0.1 + 0.2 == 0.3 is False. Use math.isclose() for floats.
import math
total = 0.1 + 0.2
print(total == 0.3, total)
print(math.isclose(total, 0.3))
print(math.isclose(0, 1e-12))
print(math.isclose(0, 1e-12, abs_tol=1e-9))By default isclose allows a small difference relative to the size of the numbers. Next to zero any relative difference is huge, so when one side may be 0 add abs_tol.
The statistics module
To summarise a list of numbers, use statistics instead of writing the formulas yourself.
import statistics
marks = [62, 70, 70, 75, 81, 90, 98]
print(statistics.mean(marks))
print(statistics.median(marks), statistics.mode(marks))
print(statistics.multimode([1, 1, 2, 2, 3]))
print(round(statistics.stdev(marks), 2), round(statistics.pstdev(marks), 2))
print(statistics.quantiles(marks, n=4))meanis the total divided by the count.medianis the middle value once sorted, or the average of the two middle values when the count is even.modereturns the most common value.multimodereturns a list of all values that tie.stdevmeasures how spread out the data is. Use it when your list is a sample of something bigger, andpstdevwhen it is the whole population.varianceis the standard deviation squared.quantiles(data, n=4)returns the three cut points that split the data into quarters.
The mean is easy to drag around, and the median is not: one huge value changes the mean a lot and the median hardly at all.
import statistics
salaries = [30, 32, 35, 36, 400]
print(statistics.mean(salaries), statistics.median(salaries))Exact numbers: Decimal and Fraction
For money, binary floats are the wrong tool. Decimal stores decimal digits exactly, so build it from a string. quantize() rounds to a chosen number of places, and you choose the rounding rule. Fraction keeps a ratio exact, so one third plus one sixth really is one half.
from decimal import Decimal, ROUND_HALF_UP
from fractions import Fraction
print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))
print(Decimal(0.1))
print(round(2.675, 2), Decimal("2.675").quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))
print(Fraction(1, 3) + Fraction(1, 6))Decimal(0.1) copies the float's error along with it. And round(2.675, 2) gives 2.67 because the float behind 2.675 sits a hair below it.
When the input is bad
A math function raises ValueError: math domain error when the maths has no answer, such as the square root of a negative number. The statistics functions raise StatisticsError (a kind of ValueError) when there is not enough data: mean([]), or stdev of a single value. Check the data first, or catch the error.
import math
import statistics
for value in (16, -4):
try:
print(math.sqrt(value))
except ValueError as error:
print("Cannot take the root of", value, "->", error)
try:
statistics.mean([])
except statistics.StatisticsError as error:
print("No data:", error)Which tool, when?
Operators and built-ins cover everyday sums. Use math for roots, logarithms, angles and rounding direction, statistics to summarise a list, Decimal for money and Fraction for exact ratios. For millions of numbers, switch to NumPy.
Quick reference: Math and Statistics
- Python Division (/) vs Floor Division (//) — `/` is **true division** and always returns a float (`7 / 2` is `3.5`), while `//` is **floor division** and …
- How to Convert a Float to an Int in Python — `int(3.9)` returns `3` because it cuts off the decimal part. Use `round(3.9)` to get `4`, or `math.floor()` a…
- Python Program to Calculate the Average of a List — The average of a list is the sum divided by the count: `sum(numbers) / len(numbers)`.
- Python Program to Find Factorial of a Number — The factorial of n (written n!) is the product of all whole numbers from 1 to n, and in Python you can comput…
- Python Program to Find GCD of Two Numbers — The GCD (greatest common divisor, also called HCF) of two numbers is the largest number that divides both exa…
- Python sum() Function — `sum(iterable, start=0)` adds up all the items of an iterable and returns the total; an empty iterable gives …
- Python abs() Function — `abs(x)` returns the absolute value of a number: its distance from zero, so `abs(-7)` is `7`.
- Python round() Function — `round(number, ndigits=None)` rounds a number to `ndigits` decimal places, or to the nearest integer when `nd…
4Math and Statistics: syntax
import math
math.sqrt(x) math.hypot(a, b) math.pi math.e
math.floor(x) math.ceil(x) math.trunc(x) round(x, digits)
math.isclose(a, b, rel_tol=1e-09, abs_tol=0.0)
math.gcd(a, b) math.lcm(a, b) math.factorial(n) math.comb(n, k) math.perm(n, k)
math.log(x) math.log10(x) math.log2(x) math.radians(deg) math.degrees(rad)
divmod(a, b) # (a // b, a % b)
import statistics
statistics.mean(data) statistics.median(data) statistics.mode(data)
statistics.multimode(data) statistics.stdev(data) statistics.pstdev(data)
statistics.variance(data) statistics.quantiles(data, n=4)
from decimal import Decimal, ROUND_HALF_UP
Decimal("19.99").quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
from fractions import Fraction
Fraction(1, 3) + Fraction(1, 6) # Fraction(1, 2)
5Math and Statistics: run the example
Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.
import math
import statistics
marks = [72, 85, 90, 66, 85, 78]
average = statistics.mean(marks)
middle = statistics.median(marks)
spread = statistics.stdev(marks)
print("Average:", round(average, 1))
print("Median:", middle)
print("Most common:", statistics.mode(marks))
print("Spread:", round(spread, 2))
top_third = math.ceil(len(marks) / 3)
best = sorted(marks, reverse=True)[:top_third]
print("Top", top_third, "marks:", best)Expected outputAverage: 79.3
Median: 81.5
Most common: 85
Spread: 9.07
Top 2 marks: [90, 85]
7Math and Statistics: how the example works
Hover or tap a step to highlight its lines in the example above.
- Line 1-2Two standard-library modules:
mathforceil, andstatisticsfor the averages. - Line 4Six exam marks in a list. Notice that 85 appears twice.
- Line 6
mean()adds the marks (476) and divides by how many there are (6), which gives 79.33... - Line 7
median()sorts the marks and, because the count is even, averages the two middle ones: 78 and 85 give 81.5. - Line 8
stdev()measures how far the marks are from the mean, treating the list as a sample of a bigger group. - Line 9
round(average, 1)only changes what is displayed. The variableaveragekeeps its full precision. - Line 10-11The median is printed as it is.
mode()returns 85, the only value that occurs twice. - Line 12The standard deviation is rounded to two decimals so the output stays tidy.
- Line 14
math.ceil(6 / 3)is 2. Rounding up guarantees that nobody who belongs in the top third is left out when the class size does not divide evenly. - Line 15
sorted(..., reverse=True)makes a new list from highest to lowest, and the slice[:top_third]keeps the first two items. - Line 16Prints the count and the winning marks.
8Math and Statistics: common mistakes
Taking the square root of a negative number
✗ Wrong
import math
profit = -4
print(math.sqrt(profit))✓ Right
import math
profit = -4
if profit >= 0:
print(math.sqrt(profit))
else:
print("No real square root for", profit)A real square root does not exist for a negative number, so math.sqrt refuses. Check the value first, or take abs() if the sign does not matter.
Comparing floats with ==
✗ Wrong
total = 0.1 + 0.2
if total == 0.3:
print("Exact match")
else:
print("No match:", total)✓ Right
import math
total = 0.1 + 0.2
if math.isclose(total, 0.3):
print("Exact match")
else:
print("No match:", total)Floats carry tiny binary rounding errors. Compare them with math.isclose() instead of ==.
Expecting round(2.5) to give 3
✗ Wrong
tip = 2.5
print(round(tip))✓ Right
from decimal import Decimal, ROUND_HALF_UP
tip = Decimal("2.5")
print(tip.quantize(Decimal("1"), rounding=ROUND_HALF_UP))round() sends an exact half to the nearest even number. If you need the school rule (halves go up), use Decimal with ROUND_HALF_UP.
Passing degrees to sin() and cos()
✗ Wrong
import math
print(math.sin(90))✓ Right
import math
print(math.sin(math.radians(90)))The trigonometric functions work in radians. Convert degrees with math.radians() first.
Averaging an empty list
✗ Wrong
import statistics
scores = []
print(statistics.mean(scores))✓ Right
import statistics
scores = []
if scores:
print(statistics.mean(scores))
else:
print("No scores yet")There is nothing to average. Check that the list has data before calling mean(), or catch the error and report that there are no scores yet.
9Math and Statistics: exercises
Write your answer, then press Check. The checks run in your browser.
1. Circle area and ladder length
Write code Easy ✓ CompletedComplete two functions with the math module:
circle_area(radius)returns the area of a circle (pitimes the radius squared), rounded to 2 decimal places.ladder_length(wall_height, distance)returns the length of a ladder that standsdistancemetres from a wall and touches itwall_heightmetres up. Usemath.hypot, and round the result to 2 decimal places.
Both functions must return their answer.
import math
def circle_area(radius):
pass
def ladder_length(wall_height, distance):
pass
print(circle_area(2))
print(ladder_length(4, 3))
import math
def circle_area(radius):
return round(math.pi * radius ** 2, 2)
def ladder_length(wall_height, distance):
return round(math.hypot(wall_height, distance), 2)
print(circle_area(2))
print(ladder_length(4, 3))
2. Predict the rounding
Predict the output Easy ✓ CompletedRead the code and type exactly what it prints, line by line. Then press Check to run it and compare.
import math
print(round(2.5), round(3.5), round(0.5))
print(math.floor(-2.5), math.ceil(-2.5), math.trunc(-2.5))
print(divmod(17, 5))
print(0.1 + 0.2 == 0.3, math.isclose(0.1 + 0.2, 0.3))
2 4 0
-3 -2 -2
(3, 2)
False True
2 more exercises → 6 more examples
Questions learners ask
Why does 0.1 + 0.2 give 0.30000000000000004 in Python?
Python floats are stored in binary, and numbers like 0.1 and 0.2 cannot be written exactly in binary, just as 1/3 cannot be written exactly in decimal. Each one is stored a tiny bit off, and the error shows when you add them. Every language that uses standard floats does this. Round for display, compare with math.isclose(), and use Decimal for money.
Why does round(2.5) return 2 in Python?
Python 3 uses banker's rounding: an exact half goes to the nearest even whole number, so round(2.5) is 2 and round(3.5) is 4. Over many numbers this avoids always pushing totals upward. If you need halves to go up, use Decimal("2.5").quantize(Decimal("1"), rounding=ROUND_HALF_UP). Note that round(2.675, 2) gives 2.67 for a different reason: the float is stored slightly below 2.675.
What is the difference between statistics.stdev and statistics.pstdev?
stdev is the sample standard deviation: it divides by n - 1, and you use it when your list is only a sample taken from a larger group. pstdev is the population standard deviation: it divides by n, and you use it when the list contains every value you care about. For the same data, stdev is always slightly larger, and it needs at least two values.
Further reading
10Next lesson
Up nextPython random ModuleThe random module draws pseudo-random numbers and picks from a seeded generator, so you can simulate dice, shuffle and …Related:
Python OperatorsPython Modules and ImportsPython random ModuleNumPy Basics
← Python Modules and Imports · All Functions and Modules lessons · Practise in the IDE