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

    Fraction charts representing exact rational numbers in Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python fractions: Exact Rational Numbers

    Learn Python fractions for exact rational arithmetic, automatic reduction, limit_denominator, formatting, and safe conversions.

    Ler mais

    Tempo de leitura: 5 minutos
    31/07/2026
    Calculator and documents representing precise Decimal calculations in Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python Decimal: Precise Calculations

    Learn Python Decimal for exact calculations, money, quantize, rounding modes, contexts, and validation without float errors.

    Ler mais

    Tempo de leitura: 6 minutos
    30/07/2026
    Digital code representing unique and sortable UUID identifiers in Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python uuid: Unique and Sortable IDs

    Learn Python uuid versions 4, 5, 6, and 7, validation, database storage, sortable IDs, and essential security practices.

    Ler mais

    Tempo de leitura: 7 minutos
    30/07/2026
    Organized files representing secure temporary storage in Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python tempfile: Secure Temporary Files

    Learn Python tempfile to create secure temporary files and directories with automatic cleanup across Windows and Unix.

    Ler mais

    Tempo de leitura: 7 minutos
    29/07/2026
    Clocks representing international time zones with Python zoneinfo
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python zoneinfo: Time Zones Done Right

    Learn Python zoneinfo to convert time zones, handle daylight saving, fold, UTC, and tzdata without scheduling mistakes.

    Ler mais

    Tempo de leitura: 6 minutos
    29/07/2026
    Duplicate document icon representing shallow and deep copies in Python
    Libraries and Modules
    Foto de perfil de Leandro Hirt da Academify

    Python copy: Shallow and Deep Copies

    Learn Python copy to create shallow and deep copies, replace fields, and avoid sharing nested mutable objects by mistake.

    Ler mais

    Tempo de leitura: 6 minutos
    28/07/2026