Home› Tutorials› Standard Library
Standard LibraryPython's statistics Module: Mean, Median, and Variance Without NumPy
statisticsships in the standard library and needs no install — reach for it before adding a NumPy dependency for a one-off average.variance()/stdev()assume the data is a sample and divide by n−1;pvariance()/pstdev()assume the full population and divide by n — picking the wrong pair silently skews the result.median()averages the two middle values for even-length data;median_low()/median_high()instead pick an actual data point, which matters when the values aren't numeric-only.- For large datasets or anything beyond simple descriptive stats, NumPy and pandas are dramatically faster and more capable —
statisticsis for correctness and convenience on modest data, not performance.
mean, median, and mode
The three headline measures of central tendency are each a single function call:
import statistics as stats
data = [4, 8, 6, 5, 3, 8, 9, 8]
stats.mean(data) # 6.375
stats.median(data) # 6.5
stats.mode(data) # 8 -- the most common value
mean() returns a plain average; for even-length data, median() returns the mean of the two middle values, which can be a value that never actually appears in the data (as with 6.5 above). mode() returns the single most common value and, since Python 3.8, returns the first one encountered if there's a tie, rather than raising an error the way earlier versions did — a behaviour change worth knowing if code targets multiple Python versions.
Sample vs population: variance and stdev
This is the distinction most people get wrong the first time, because both pairs of functions look interchangeable at a glance:
stats.variance(data) # sample variance, divides by (n - 1)
stats.pvariance(data) # population variance, divides by n
stats.stdev(data) # sample standard deviation
stats.pstdev(data) # population standard deviation
The rule: if your data is the entire population you care about (every student in one specific class, say), use the population versions. If your data is a sample drawn from a larger population you're trying to draw conclusions about (a survey of 200 people standing in for a country), use the sample versions — dividing by n−1 instead of n corrects for the fact that a sample's variance tends to slightly underestimate the true population variance. Using the wrong pair doesn't raise an error; it just quietly produces a number that's biased, which is worse than crashing because nothing flags it.
multimode and quantiles
Two additions from more recent Python versions cover cases mode() and manual bucketing don't handle well:
stats.multimode([1, 1, 2, 2, 3]) # [1, 2] -- all values tied for most common
stats.quantiles(data, n=4) # quartile boundaries
multimode() returns every value tied for the highest frequency, in first-encountered order, instead of arbitrarily picking one the way mode() does. quantiles(data, n=4) splits the distribution into four equal-probability groups and returns the three cut points between them (quartiles); passing n=100 gives percentile cut points instead. The method keyword argument controls which of several standard interpolation conventions is used, which matters if results need to match a specific textbook or another tool's definition exactly.
Working with Decimal and Fraction input
Unlike hand-rolled averaging with sum(data) / len(data), statistics functions preserve the input type where it makes sense:
from decimal import Decimal
stats.mean([Decimal("1.1"), Decimal("2.2"), Decimal("3.3")]) # Decimal('2.2')
Feeding in Decimal values keeps the result as a Decimal rather than silently converting to float and reintroducing the rounding error Decimal was chosen to avoid in the first place — genuinely useful for financial or accounting figures where every digit needs to be exact.
When statistics isn't enough
statistics is deliberately modest in scope: descriptive statistics on in-memory Python sequences, computed correctly, with no dependencies. It's not built for speed — each function walks the data in pure Python, so on datasets in the millions of rows, NumPy's vectorised, compiled implementations are an order of magnitude faster or more. It also doesn't cover correlation, regression, hypothesis testing, or anything resembling a dataframe. The statistics module documentation is explicit that this is a calculator-replacement tool for small-to-modest datasets, not a data-science library — reach for pandas or NumPy once the dataset or the analysis outgrows that description.