Descriptive statistics summarise a set of numbers: where the values are centred (mean, median, mode), how spread out they are (range, variance, standard deviation), and how they are distributed (percentiles). Python's built-in statistics module calculates all of these without installing anything:
from statistics import mean, median, stdev
minutes = [10, 12, 14, 16, 48]
print(mean(minutes), median(minutes), round(stdev(minutes), 2)) # 20 14 15.81The numbers are the easy part. This lesson also covers choosing the right summary, the difference between sample and population formulas, how the way data was collected limits what it can tell you, and how to report statistics so readers don't draw the wrong conclusion. The examples analyse how long repairs take at a bicycle workshop.
Location summaries
The three common measures of centre answer slightly different questions:
| Measure | Function | Meaning | For [10, 12, 14, 16, 48] |
|---|---|---|---|
| mean | mean() | sum divided by count | 20 |
| median | median() | middle value when sorted | 14 |
| mode | mode() / multimode() | most frequent value | every value once |
The mean uses every value, so one unusual observation pulls it strongly: the 48-minute repair lifts the mean to 20 even though four of five repairs took 16 minutes or less. The median only cares about the middle position, so it barely moves. When data is skewed, as durations, prices and incomes often are, the median usually describes a "typical" case better, and reporting both reveals the skew.
With an even number of values, median() averages the two middle ones; median_low() and median_high() return an actual data value instead. mode() suits categories such as the most common repair type; multimode() returns every value tied for most frequent.
Variation
Spread tells you how consistent the values are. Two workshops can both average 20 minutes, one with every repair between 18 and 22, the other ranging from 5 to 60.
- Range:
max(x) - min(x); simple, but decided entirely by the two extremes. - Variance: the average squared distance from the mean.
- Standard deviation: the square root of the variance, in the same units as the data (minutes), so it's easier to interpret.
from statistics import stdev, pstdev, variance
minutes = [10, 12, 14, 16, 48]
print(round(stdev(minutes), 2), round(pstdev(minutes), 2)) # 15.81 14.14stdev() and variance() are sample statistics: they divide by n − 1, which corrects for estimating the spread of a larger process from a sample. pstdev() and pvariance() are population statistics: they divide by n, appropriate when you have every value of interest. With these five repairs as a sample of all repairs, stdev() is the right choice. The difference shrinks as n grows, but matters for small datasets.
Sampling
Every statistic describes the data you have, which may not represent what you want to know about. Ask how the observations were collected:
- Were repairs timed only on quiet weekday mornings? Then weekend rush times are missing.
- Did mechanics only record repairs that went smoothly? Then difficult jobs are under-represented.
- Are five repairs enough to say anything general at all?
Data collected because it was convenient can differ systematically from the whole process, and no formula corrects that. Larger samples reduce random noise but not systematic bias.
Write down the selection: the period, which repairs were included or excluded, and how many observations there are. A statistic quoted without its sample size and collection method is hard for anyone to judge. When the question needs a fair sample, choose observations randomly, for example with random.sample(all_job_ids, 30), rather than taking whichever were easiest to record.
Interpretation
A number on its own isn't a finding. Report statistics with:
- units: "median repair time 14 minutes", not "median 14";
- the count: "n = 5 repairs";
- centre and spread together: "median 14 min, ranging from 10 to 48";
- known limitations: "one repair needed a part ordered in; it accounts for the 48".
from statistics import median, quantiles
minutes = [10, 12, 14, 16, 48, 11, 13, 15, 17, 19]
q1, q2, q3 = quantiles(minutes, n=4)
print(f"n={len(minutes)}, median {median(minutes)} min, middle half {q1}-{q3} min")Be careful with relationships. If repairs on older bikes take longer, the data shows an association; it doesn't prove that age causes the delay. Older bikes might simply be booked for bigger jobs. statistics.correlation() (Python 3.10+) measures how closely two variables move together, but deciding why needs knowledge of the process, not just the numbers.
Independent practice and a worked check
Task: summarise repair durations with units, sample size, location and spread, explaining an unusually long observation.
from statistics import mean, median, stdev
minutes = [22, 18, 25, 20, 19, 75, 21]
note = "The 75-minute repair waited for a replacement derailleur."
print(f"n = {len(minutes)} repairs")
print(f"median {median(minutes)} min, mean {mean(minutes):.1f} min")
print(f"sample sd {stdev(minutes):.1f} min, range {min(minutes)}-{max(minutes)} min")
without = [m for m in minutes if m != 75]
print(f"without the outlier: median {median(without)} min, mean {mean(without):.1f} min")
print(note)The median moves from 21 to 20.5 minutes when the outlier is set aside, while the mean drops from 28.6 to 20.8, showing which summary is robust. The outlier is reported and explained, not silently deleted. For the worked check, the statistic generally less affected by one extreme duration is the median.
Reference and next practice
The statistics module documentation lists every function with examples. Continue with data visualization and charts and distributions and percentiles. Practise with the Python exercises and Python quiz.
Statistics with Python Summary
- Location summaries: Mean summarizes the arithmetic average while median identifies the middle sorted value.
- Variation: Variance and standard deviation summarize spread using squared deviations.
- Sampling: A collected sample may differ from the full process being studied.
- Interpretation: A numerical summary needs units, context and an explanation of its limitations.
Statistics with Python Examples
Each example shows the code, the output it produces and what each part does.
Statistics with Python in a small program
from statistics import mean, median, stdev
minutes = [10, 12, 14, 16, 48]
print(mean(minutes))
print(median(minutes))
print(round(stdev(minutes), 2))20
14
15.81Line-by-line explanation
- Line 3The mean, 100 / 5, is pulled up by the 48-minute repair.
- Line 4The median is the middle sorted value, 14, unaffected by how large the outlier is.
- Line 5The sample standard deviation, rounded to two decimal places.
A complete repair-time summary
from statistics import mean, median, stdev, multimode
repairs = {
"puncture": [10, 12, 9, 11, 14],
"brakes": [25, 30, 28, 26, 61],
"gears": [35, 40, 38, 42, 36],
}
all_minutes = [m for times in repairs.values() for m in times]
print(f"All repairs: n={len(all_minutes)}, mean {mean(all_minutes):.1f}, median {median(all_minutes)} min")
for kind, times in repairs.items():
spread = stdev(times)
print(f"{kind:<9} n={len(times)} median {median(times):>4} min sd {spread:5.1f} range {min(times)}-{max(times)}")
outliers = {kind: [m for m in times if m > 2 * median(times)] for kind, times in repairs.items()}
print("Longer than twice their type's median:", {k: v for k, v in outliers.items() if v})
print("Most common durations:", multimode(all_minutes))All repairs: n=15, mean 27.8, median 28 min
puncture n=5 median 11 min sd 1.9 range 9-14
brakes n=5 median 28 min sd 15.2 range 25-61
gears n=5 median 38 min sd 2.9 range 35-42
Longer than twice their type's median: {'brakes': [61]}
Most common durations: [10, 12, 9, 11, 14, 25, 30, 28, 26, 61, 35, 40, 38, 42, 36]Line-by-line explanation
- Lines 11 to 13Summaries per repair type reveal that only brakes vary a lot.
- Lines 15 to 16A simple screening rule flags repairs over twice the median for their type; 61 minutes stands out among brake jobs. Flagged values are for investigating, not deleting.
- Line 17Every value appears once, so
multimode()returns them all: there is no meaningful mode here.
Common Statistics with Python Mistakes
Reporting only the mean of skewed data
print(f"Repairs take {mean(minutes)} minutes") # 20: no repair took close to 20print(f"Median {median(minutes)} min (n={len(minutes)}); mean {mean(minutes)} min "
f"raised by one {max(minutes)}-minute repair")Using the population formula for a sample
spread = pstdev(sample_of_repairs) # underestimates the spread of all repairsspread = stdev(sample_of_repairs) # n - 1 denominator for a sampleStatistics with Python in Real Projects
Statistics in everyday decisions
Service teams quote median response times because a few extreme tickets would distort the mean. Web performance is reported as percentiles, such as "95th percentile page load 1.8 s", because averages hide the slowest experiences. Quality control watches standard deviation: a production line whose average is perfect but whose spread is growing is about to produce faulty parts.
For larger datasets, pandas offers describe(), median(), quantile() and std() on whole columns, and NumPy and SciPy add more advanced statistics. The judgement about what to measure, from which sample, and how to report it stays the same.
Statistics with Python FAQ
What is the difference between mean and median?
The mean adds all values and divides by the count, so extreme values pull it. The median is the middle value when sorted, so it's resistant to outliers. For skewed data such as times, prices or incomes, the median usually represents a typical value better.
What is the difference between stdev and pstdev in Python?
stdev() is the sample standard deviation, dividing by n − 1; use it when your data is a sample of a larger process. pstdev() is the population standard deviation, dividing by n; use it when you have every value of interest.
Do I need NumPy for statistics in Python?
Not for basic descriptive statistics: the built-in statistics module covers mean, median, mode, variance, standard deviation, quantiles and correlation. NumPy and pandas are faster for large datasets and many columns.
How do I find outliers in Python?
A common rule flags values more than 1.5 times the interquartile range beyond the first or third quartile, computed with statistics.quantiles(data, n=4). Treat flagged values as questions to investigate, not automatic deletions.
Why does mode() return the first value when nothing repeats?
Since Python 3.8, mode() returns the first of several equally common values instead of raising an error. Use multimode() to see every tied value, which shows when there's no single meaningful mode.
Statistics with Python Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Which statistic is generally less affected by one extreme duration?
Which review concern is valid for statistics with python?
Statistics with Python Quiz
2 questions to check what you learned.
Which statistic is generally less affected by one extreme duration?
- Mean
- Median
- Python ignores the distinction
Which statement correctly describes sampling?
- A collected sample may differ from the full process being studied.
- Only the example output matters, not the rule
- Every operation has the same behavior