Mean, median, and standard deviation appear in reports, monitoring, surveys, quality control, grades, performance metrics, and experiments. For small analyses and dependency-free scripts, the Python statistics module provides ready-made functions for central tendency, spread, relationships between variables, normal distributions, and kernel density estimation.
This guide explains how to choose mean or median, distinguish samples from populations, calculate quantiles, correlation, and regression, remove NaN values, and use NormalDist. It complements our guides about Python lists, collections, Decimal, fractions, and floating-point numbers.
When to use statistics
The module targets scientific-calculator and graphing-level analysis. It is not intended to replace NumPy, SciPy, pandas, or professional statistical packages. It works well when the dataset fits in memory, calculations are univariate or bivariate, and staying in the standard library is valuable.
from statistics import mean, median, stdev
data = [12, 15, 14, 18, 20, 13]
print(mean(data))
print(median(data))
print(stdev(data))Functions accept iterables and, where documented, support int, float, Decimal, and Fraction. Avoid mixed numeric types because behavior can be implementation-dependent.
Arithmetic mean with mean()
mean() sums the values and divides by the count.
from statistics import mean
grades = [7.5, 8.0, 9.0, 6.5]
print(mean(grades)) # 7.75The mean uses every observation, but it is sensitive to outliers. One extremely high salary can raise a group’s mean and stop representing a typical person.
fmean() and weighted averages
fmean() converts input to floats and is generally faster. Since Python 3.11, it supports weights.
from statistics import fmean
grades = [85, 92, 83, 91]
weights = [0.20, 0.20, 0.30, 0.30]
print(fmean(grades, weights)) # 87.6Weights must match the data length and have a positive sum. Document whether a weight represents importance, frequency, duration, or exposure.
Geometric mean
geometric_mean() is appropriate for multiplicative factors, compound growth, and relative indices.
from statistics import geometric_mean
factors = [1.10, 0.95, 1.20]
print(geometric_mean(factors))Values must be positive. Do not use an arithmetic mean directly for successive growth rates; first convert rates to multiplicative factors.
Harmonic mean
harmonic_mean() is useful for rates, especially when distance or quantity is fixed.
from statistics import harmonic_mean
average_speed = harmonic_mean([40, 60])
print(average_speed) # 48Traveling equal distances at 40 and 60 km/h does not produce 50 km/h because the slower segment consumes more time.
Median and outliers
median() returns the middle of sorted data and is more robust to extreme values.
from statistics import mean, median
salaries = [2500, 2700, 2800, 3000, 30000]
print(mean(salaries))
print(median(salaries))With an even number of points, the standard median averages the two center values. For ordinal or discrete data, use median_low() or median_high() to return an observed value.
Grouped median
median_grouped() estimates a median when each value represents the midpoint of a fixed-width class.
from statistics import median_grouped
ages = [25] * 10 + [35] * 30 + [45] * 15
print(median_grouped(ages, interval=10))The function assumes that data points are separated by exact multiples of the interval. It does not verify the precondition.
Mode and multimode
mode() returns the first most frequent value. multimode() returns all tied modes.
from statistics import mode, multimode
colors = ["blue", "green", "blue", "red", "green"]
print(mode(colors))
print(multimode(colors))Mode works with nominal data as well as numbers. Elements must be hashable.
Quantiles, quartiles, and percentiles
quantiles() divides data into equal-probability intervals. With n=4, it returns quartile cut points; with 10, deciles; with 100, percentiles.
from statistics import quantiles
data = [12, 15, 18, 20, 22, 25, 30, 35, 40, 45]
print(quantiles(data, n=4))The default exclusive method treats data as a sample from a population that may contain more extreme values. The inclusive method treats the observed minimum and maximum as population endpoints. Record the chosen convention.
Variance and standard deviation
Variance measures squared spread; standard deviation returns to the original unit. For a sample, use variance() and stdev(). For the full population, use pvariance() and pstdev().
from statistics import variance, stdev, pvariance, pstdev
data = [10, 12, 13, 15, 20]
print(variance(data))
print(stdev(data))
print(pvariance(data))
print(pstdev(data))Sample variance uses Bessel’s correction and divides by N-1. The distinction matters most with small datasets.
Passing a precomputed mean
If the correct mean is already available, pass it to avoid recomputation.
from statistics import mean, variance
m = mean(data)
v = variance(data, m)The function does not verify that the supplied value is the actual mean. Passing an arbitrary value produces an invalid result.
Remove NaN before analysis
NaN has unusual comparison semantics and can break sorting, median, mode, and quantiles.
from itertools import filterfalse
from math import isnan
from statistics import median
data = [20.7, float("nan"), 19.2, 18.3, float("nan"), 14.4]
clean = list(filterfalse(isnan, data))
print(median(clean))The official statistics documentation recommends stripping NaN before sorting and counting functions. A real project must also decide whether missing values should be dropped, imputed, or rejected.
Keep numeric types consistent
mean() and spread functions can preserve Decimal or Fraction values.
from decimal import Decimal
from fractions import Fraction
from statistics import mean
print(mean([Decimal("0.5"), Decimal("0.75")]))
print(mean([Fraction(1, 3), Fraction(2, 3)]))Convert the entire dataset to one coherent type before analysis.
Covariance
covariance() measures how two variables vary together.
from statistics import covariance
hours = [1, 2, 3, 4, 5]
grades = [55, 60, 68, 76, 85]
print(covariance(hours, grades))A positive value indicates joint increase; a negative value indicates opposite movement. Magnitude depends on units and is not directly comparable across differently scaled datasets.
Pearson correlation
correlation() with the default linear method returns Pearson’s coefficient between -1 and 1.
from statistics import correlation
print(correlation(hours, grades))Correlation measures linear association, not causality. Outliers can dominate the coefficient, and a strong curved relationship can have weak linear correlation.
Spearman rank correlation
Since Python 3.12, method="ranked" calculates Spearman correlation.
print(correlation(hours, grades, method="ranked"))Spearman measures monotonic association and suits ordinal data or continuous relationships that are not proportional. Ties receive averaged ranks.
Simple linear regression
linear_regression() estimates slope and intercept using ordinary least squares.
from statistics import linear_regression
model = linear_regression(hours, grades)
predicted = model.slope * 6 + model.intercept
print(model)
print(predicted)Simple regression does not prove causality or automatically validate residual independence, equal variance, and model form. Inspect residuals before important decisions.
Proportional regression
With proportional=True, the fitted line is forced through the origin.
model = linear_regression(x, y, proportional=True)Use this only when the domain justifies a zero intercept. An unjustified constraint distorts the slope.
NormalDist
NormalDist packages a normal distribution’s mean and standard deviation.
from statistics import NormalDist
distribution = NormalDist(mu=100, sigma=15)
print(distribution.cdf(115))
print(distribution.inv_cdf(0.95))
print(distribution.zscore(130))cdf() returns cumulative probability, inv_cdf() finds a quantile, and zscore() reports the number of standard deviations from the mean.
Estimate a normal distribution from samples
sample = [98, 101, 99, 105, 102, 97]
dist = NormalDist.from_samples(sample)
print(dist.mean, dist.stdev)This assumes a normal model is meaningful. Check histograms, skewness, outliers, and the process that generated the observations.
Reproducible random samples
samples() generates simulated values. Supply a seed for reproducible tests.
values = NormalDist(0, 1).samples(5, seed=42)
print(values)This is for statistical simulation, not cryptographic security.
Kernel density estimation
Since Python 3.13, kde() creates a smoothed density function from a sample.
from statistics import kde
sample = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2]
density = kde(sample, h=1.5)
print(density(0))The bandwidth h matters more than the exact kernel shape. Small bandwidths emphasize local detail and noise; large values create a smoother distribution.
kde_random()
kde_random() returns a function that draws values from the estimated density.
from statistics import kde_random
generator = kde_random(sample, h=1.5, seed=123)
print([generator() for _ in range(5)])Use it for exploratory simulation while remembering that output quality depends on the original sample and bandwidth.
StatisticsError
Empty inputs, variance with too few values, and incompatible data can raise StatisticsError.
from statistics import StatisticsError, mean
try:
mean([])
except StatisticsError:
print("Not enough data")Validate data length and quality before automated calculations.
statistics versus NumPy and pandas
Use statistics for small iterables, Decimal/Fraction support, and dependency-free scripts. Use NumPy for large arrays and vectorized multidimensional operations. Use pandas for tables, columns, missing values, and grouped analysis.
Results can differ because of degrees of freedom and quantile conventions. Document parameters when migrating.
Numerical precision
For difficult floating-point sums, the official math documentation provides math.fsum(), which reduces accumulated precision loss. Statistics functions use careful algorithms, but ill-conditioned inputs still deserve scrutiny.
Common mistakes
- Using the mean on outlier-dominated data.
- Confusing a sample with the entire population.
- Leaving NaN values in the input.
- Mixing numeric types.
- Interpreting correlation as causation.
- Running regression without checking residuals.
- Reporting quartiles without the method.
- Choosing KDE bandwidth arbitrarily.
Best practices
- Define the question before selecting a metric.
- Clean and validate data.
- Report sample size and missing values.
- Show median alongside mean for skewed data.
- Choose sample or population functions correctly.
- Document quantile and correlation methods.
- Visualize observations and residuals.
- Move to specialized libraries when scale grows.
Conclusion
The Python statistics module provides a solid standard-library foundation for descriptive analysis and simple relationships. It includes several means, robust central measures, spread, quantiles, correlation, regression, normal distributions, and KDE.
The quality of an analysis depends more on metric choice and data preparation than on the function call. By distinguishing sample and population, removing NaN values, handling outliers, and documenting conventions, you can turn lists of numbers into reproducible analysis without drawing misleading statistical conclusions.







