Python statistics: Data Analysis Guide

Published on: July 31, 2026
Reading time: 6 minutes
Chart dashboard representing statistical data analysis in Python

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.75

The 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.6

Weights 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)  # 48

Traveling 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.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Digital file icon representing MIME types with Python mimetypes
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python mimetypes: MIME Types

    Learn Python mimetypes to identify MIME types, extensions, and encodings safely in uploads, downloads, email, and web APIs.

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026
    Binary search and sorted lists with Python bisect
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python bisect: Search Sorted Lists

    Learn Python bisect for binary search, ordered insertion, duplicate ranges, thresholds, and safe sorted-list design.

    Ler mais

    Tempo de leitura: 4 minutos
    08/08/2026
    Code and packaged files with Python importlib.resources
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python importlib.resources: Practical Guide

    Learn Python importlib.resources to access packaged files safely across wheels, installed applications, and custom import loaders.

    Ler mais

    Tempo de leitura: 5 minutos
    07/08/2026
    Keyboard and data flow representing multiple-file processing with Python fileinput
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python fileinput: Read Multiple Files

    Learn Python fileinput to read multiple files or stdin, track line numbers, open compressed logs, and rewrite content safely with

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026
    Code editor with numbered lines representing the Python linecache module
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python linecache: Read Lines by Number

    Learn Python linecache to read source lines by number, manage cached files, refresh changed code, and support traceback and import

    Ler mais

    Tempo de leitura: 7 minutos
    07/08/2026
    Binary data representing internal serialization with Python marshal
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python marshal: Internal Serialization

    Learn Python marshal to serialize internal types, control format versions, and reject code objects when they are unnecessary.

    Ler mais

    Tempo de leitura: 6 minutos
    06/08/2026