Summarising Numbers
Overview
Totals, averages and extremes, and what each one hides.
What You Will Learn in This Lesson
By the end of this lesson, you will be able to:
- Compute a count, total, mean, minimum and maximum from a column.
- Handle the empty column before it raises
ZeroDivisionError. - Find the middle value with a median, and say when it beats the mean.
- Report an average together with its spread.
Why This Matters
A summary is the answer most analyses are asked for, and a single average is the most-quoted and most-misleading number in any report. Knowing what each summary throws away — the mean hides spread, one outlier drags it, an empty column breaks it — is what lets you choose the right one and report it honestly. pandas’ describe() in unit 4 is these same numbers in one call.
Step 1: The summaries you reach for first
Five built-ins answer most questions about a column of numbers.
Count, total, extremes
len, sum, min and max work directly on a list of numbers.
scores = [92, 88, 79, 55]
print("count:", len(scores))
print("total:", sum(scores))
print("lowest:", min(scores), "highest:", max(scores))
The mean, written out
The average is the total divided by the count. Writing it out once, rather than reaching for a library, makes the next problem obvious.
scores = [92, 88, 79, 55]
mean = sum(scores) / len(scores)
print(round(mean, 1))
Step 2: The empty column
Real data sometimes has nothing in it: a filter kept no rows, a class had no scores yet.
Dividing by zero
len([]) is 0, so the mean of an empty list raises ZeroDivisionError. min and max raise ValueError.
scores = []
try:
print(sum(scores) / len(scores))
except ZeroDivisionError as e:
print("mean failed:", e)
Decide the answer before the division
Check for an empty list first and return something deliberate — 0, or None if “no answer” should look different from “scored zero”.
def mean(values):
if not values:
return None
return sum(values) / len(values)
print(mean([92, 88]))
print(mean([]))
Mini Practice #1: Guard the extremes
Try It YourselfRun it: the empty list raises ValueError, caught and printed below. Change lowest so it returns None for an empty list, and the second line prints None instead.
Step 3: What an average hides
Very different data can share an average. Two small additions make a summary much harder to misread.
Same mean, different class
Everyone scoring 50, and half scoring 0 while half score 100, both average 50. Reporting the lowest and highest next to the mean shows the difference at the cost of one line.
a = [50, 50, 50, 50]
b = [0, 0, 100, 100]
print(sum(a) / len(a), min(a), max(a))
print(sum(b) / len(b), min(b), max(b))
One outlier moves the mean
A single typo of 900 instead of 90 drags the mean far from any real score. The median — the middle value once sorted — barely moves. The statistics module has both.
import statistics
scores = [88, 90, 92, 900]
print("mean:", statistics.mean(scores))
print("median:", statistics.median(scores))
Finding the median by hand
Sort, then take the middle value — or the mean of the two middle values when the count is even.
scores = sorted([79, 92, 88, 55])
n = len(scores)
mid = n // 2
if n % 2:
print(scores[mid])
else:
print((scores[mid - 1] + scores[mid]) / 2)
Key idea: Neither summary is “correct”. The mean uses every value, which is its strength and its weakness. When a mean and a median disagree a lot, that disagreement is itself worth reporting.
Mini Practice #2: Mean or median?
Try It YourselfPrint the mean and the median of each list. For which list do they disagree, and why?
Predict, then check: One outlier, two summaries
A slow response appears beside three ordinary ones. Predict which summary changes most when 100 becomes 1000.
import statistics
times = [2, 3, 3, 100]
print(statistics.mean(times))
print(statistics.median(times))
Show the output and explanation
27 3.0
The mean uses every value, so the 100 affects it strongly. The median uses the middle two values after sorting: (3 + 3) / 2. Report what the numbers measure and the sample size; neither summary alone describes the whole distribution.
Try one change: Replace 100 with 1000, then compare both summaries. Explain which is more useful for a typical response time.
Go deeper: Compare a typical value with an extreme
The mean uses every value, so a single extreme can move it substantially. The median is the middle value after sorting, or the average of the two middle values for an even count. Compare both when you want to describe a typical observation.
Trace the example
Predict the output before opening the explanation.
from statistics import median
waits = [2, 3, 3, 4, 28]
print(sum(waits) / len(waits))
print(median(waits))
Show the output and trace
8.0 3
The total waiting time is 40 across 5 observations, giving a mean of 8. The sorted middle value is 3. Four of the five waits are below the mean, so reporting only 8 hides the shape of this tiny dataset.
A mistake to watch for
An extreme value is not automatically an error: the 28-minute wait might be real. Investigate its meaning before removing it. Check for an empty list before computing a mean or median, and preserve precision until final display.
Transfer challenge
Replace 28 with 8. Recalculate the mean and median, then explain which summary changed and why.
Change the example below to solve the challenge. Predict the result, run your code, and compare. This practice is ungraded.
Compare with a worked solution
from statistics import median
waits = [2, 3, 3, 4, 8]
print(sum(waits) / len(waits))
print(median(waits))
Run both versions. Explain any difference in their output, then change one input and predict the new result.
Step 4: When to Use It
Mean with its range
The default report for a column of numbers: the average, and the lowest and highest beside it so the spread is visible.
print(mean, min(scores), max(scores))
Median when values are skewed
Incomes, response times, anything with a long tail or a possible typo. The median ignores a single extreme value.
When Not to Use It
Do not report a mean on its own, and do not divide before checking the column has anything in it. An empty column should give a deliberate answer, not a ZeroDivisionError.
End-of-Lesson Exercises
Exercise 1: A safe average
Write a function average(scores) that returns the mean, and returns 0 for an empty list rather than raising.
The empty case has to be handled before the division, not after it — len([]) is 0 and dividing by it raises.
Exercise 2: The median, by hand
Write median(values) that returns the middle value of the list once sorted, or the mean of the two middle values when there is an even number of them. Return None for an empty list. Do not change the list you were given, and do not import statistics.
s = sorted(values) makes a sorted copy. With n = len(s) and mid = n // 2, an odd count gives s[mid]; an even count gives (s[mid - 1] + s[mid]) / 2.