Averages and spread
Mean, median and standard deviation, built by hand, and which one a question needs.
Why it matters
Data rarely arrives as one number. Delivery times, order values, a model's scores on a test: they come as lists, and you need to say what's typical and how much the values vary. The mean, the median and the standard deviation are the three numbers people reach for, and each one has a trap. One huge order can drag the "average" order value far from any real order, and two products can share an average and behave nothing alike. This lesson builds each one by hand in Python, so you know exactly what a library means when it reports them.
What you'll be able to do
- Compute a mean and a median by hand in Python, and say which one a situation needs
- Explain how one extreme value pulls the mean but not the median
- Measure spread with the range and the standard deviation
- Read a result like "82% ± 3" and spot a suspicious average
What it covers
- The mean: share it out equally
- The median: the middle value
- The median of an even count
- Percentiles: the median's family
- Mean or median?
- Spread: how far from the middle?
- Standard deviation by hand
- Reading spread in results
- Averages in AI
orders = [20, 25, 30, 25, 400] print(sum(orders) / len(orders))
Show the answer
100.0
The five orders add up to 500, and 500 / 5 = 100.0. One $400 order pulls the average far above every other order. That's the first trap of averages, and this lesson's starting point.
The mean: share it out equally
The mean is what most people call "the average": add everything up and divide by how many there are. Think of it as sharing out equally. If five friends' restaurant bills add up to $100, the mean is what each would pay if they split it evenly: $20.
bills = [18, 25, 12, 30, 15] mean = sum(bills) / len(bills) print(mean)
The mean has a useful property: the mean times the count gives back the total. That makes it the right choice when you care about totals, like revenue per customer to plan next month's revenue. Its weakness is that every value pulls on it, and extreme values pull hardest. One $400 order among $25 ones moves the mean a long way, as the opening question showed. And an empty list has no mean at all: sum([]) / len([]) divides by zero, so code that averages data it didn't check first can crash on a quiet day.
Every order but one is the typical size. Drag the big one and watch the mean chase it while the median stays put.
The median: the middle value
The median is the middle value once the list is sorted. Half the values are below it and half above. It doesn't care how extreme the extremes are: make the biggest order $400 or $4 million, and the median doesn't move.
times = [12, 3, 7, 30, 5] in_order = sorted(times) print(in_order) middle = len(in_order) // 2 print(in_order[middle])
With an odd count there's one middle value: for five values it's at position 2, which is len // 2. With an even count there are two middle values, and the median is the mean of those two. That's why house prices and salaries are usually reported as medians: a few mansions or one very rich founder can't distort them.
times = sorted([12, 3, 7, 30, 5]) print(times[len(times) // 2])
Show the answer
7
Sorted, the list is [3, 5, 7, 12, 30]. There are 5 values, so the middle position is 5 // 2 = 2, which holds 7. The 30 doesn't drag it up.
The median of an even count
Six parcels took these delivery times in hours: 34, 12, 27, 19, 45 and 22. With an even count, the median is the mean of the two middle values. Set in_order to the sorted list and median to the median.
hours = [34, 12, 27, 19, 45, 22] in_order = hours # sort it median = 0 # the mean of the two middle values print(in_order, median)
Percentiles: the median's family
The median is one of a family called percentiles. The 90th percentile is the value that 90% of the data falls below; the median is the 50th. Percentiles describe the slow end of response times well: "half our replies arrive within 2 hours, and 95% within 6" says far more than a single average.
Show the answer
95% of replies arrived within 6 hours
The 95th percentile is the value that 95% of the data falls below, so all but the slowest 5% of replies came within 6 hours.
Mean or median?
Neither is "the right average"; they answer different questions. The mean answers "if it were shared out equally, how much each?", which is what you need for totals and budgets. The median answers "what's a typical one like?", which is what you need when a few extreme values would mislead.
When the two are close, the data is fairly balanced. When the mean is far above the median, a few large values are pulling it up; that's called a skewed distribution, and it's common in money, waiting times and website traffic. Reporting both, side by side, tells a reader more than either alone. (A third average, the mode, is the most common value. It's handy for categories, like the most popular shirt size.)
Spread: how far from the middle?
Two shops both average 40 minutes to deliver. At the first, every delivery takes 38 to 42 minutes. At the second, some take 10 and some take 70. Same mean, very different experience. The spread says how far values typically sit from the middle.
The simplest measure is the range, max minus min, but one odd value can make it huge. The usual measure is the standard deviation: roughly, the typical distance from the mean. You find each value's distance from the mean, square the distances (so negatives don't cancel), average the squares, and take the square root to get back to the original units:
values = [38, 40, 42, 40] mean = sum(values) / len(values) total = 0 for v in values: total = total + (v - mean) ** 2 std = (total / len(values)) ** 0.5 print(mean, round(std, 2))
The average of the squared distances, before the square root, is called the variance. (Statisticians often divide by one less than the count when the list is a sample from a bigger group; the idea is the same.)
Show the answer
B: its ratings sit further from the average
A's ratings are all exactly the mean, so its standard deviation is 0. B's are each 1 star away, so its standard deviation is 1. Same mean, different spread.
Standard deviation by hand
Five test runs scored 4, 8, 6, 5 and 7. Set mean, then variance (the average of the squared distances from the mean), then std (its square root), with std rounded to 2 decimal places. Use a loop to add up the squared distances.
scores = [4, 8, 6, 5, 7] mean = 0 variance = 0 std = 0 print(mean, variance, std)
Reading spread in results
For data shaped like a bell, a handy rule: about two thirds of the values sit within one standard deviation of the mean, and about 95% within two. So a report like "delivery takes 40 ± 5 minutes" says most deliveries take 35 to 45 minutes, and almost all take 30 to 50. A value three standard deviations out is rare enough to investigate.
The same notation shows up in AI results: "82% ± 3" means a model scored 82% on average across several runs, with a standard deviation of 3 points. If a new model scores 84% ± 3, the gap is smaller than the run-to-run wobble, and you can't yet claim it's better.
monday = 41 / 50 * 100 tuesday = 45 / 50 * 100 print((monday + tuesday) / 2)
Show the answer
86.0
The two days scored 82% and 90%, and their mean is 86.0. Averaging rates is fine here because both days had the same 50 questions; with different-sized groups you'd combine the counts first.
Averages in AI
These three numbers are everywhere in the course. An eval's accuracy is a mean of 1s and 0s. A model's loss is a mean of its errors over a batch of examples. Results are reported as mean ± standard deviation across runs. Response times are reported as a median, plus a slow percentile such as the 95th, because a mean hides the slow tail. And in Phase 4, normalization layers subtract a mean and divide by a standard deviation inside the network itself.
Python's statistics module has mean(), median() and stdev() built in, and you'll meet it in the modules lesson. Having built each one by hand, you'll know exactly what they compute, and when one of them is the wrong number for the question.
scores = [0.8, 0.9, 0.7] print(round(sum(scores) / len(scores), 2))
Show the answer
0.8
The scores add up to 2.4 (give or take a tiny float error), and 2.4 / 3 = 0.8. round(..., 2) tidies the float noise away.
Show the answer
A few replies took far longer than the rest
Half the replies came within 2 hours, but a few very slow ones pulled the mean up to 9. The gap between mean and median is a sign of skew.
Practice what you just read
- 3 code examples you can edit and run
- a practice exercise that checks your code
- a 6-question quiz
- 5 review cards for spaced repetition
Try a quiz question
Most homes in a town sell for about $300,000, but three mansions sold for $9 million each. Which number best describes a typical home?