---
title: "statistics em Python: média, mediana e desvio padrão"
url: "https://python.dev.br/blog/python-statistics-media-mediana-desvio-padrao/"
markdown_url: "https://python.dev.br/blog/python-statistics-media-mediana-desvio-padrao.MD"
description: "Aprenda o módulo statistics do Python para calcular média, mediana, moda, quantis, variância, desvio padrão, correlação e regressão sem instalar pandas."
date: "2026-09-05"
author: "Equipe Python Dev BR"
---

# statistics em Python: média, mediana e desvio padrão

Aprenda o módulo statistics do Python para calcular média, mediana, moda, quantis, variância, desvio padrão, correlação e regressão sem instalar pandas.


O módulo **`statistics` do Python** calcula média, mediana, moda, quantis, variância, desvio padrão, correlação e regressão linear sem instalar pandas ou NumPy. Ele é uma boa escolha para listas e outros iteráveis pequenos, scripts de automação, validações, CLIs e relatórios simples. Para tabelas grandes, agrupamentos e joins, pandas continua mais conveniente; para um cálculo descritivo direto, a biblioteca padrão costuma ser suficiente.

A recomendação prática é: use `fmean` para uma média em `float`, `median` quando valores extremos distorcem o centro, `quantiles` para faixas como quartis, `stdev` para uma amostra, `pstdev` para a população inteira e `correlation` apenas depois de conferir os dados. Este guia mostra as diferenças com um conjunto de pedidos fictícios de um pequeno e-commerce brasileiro.

## Exemplo inicial: resumo de valores de pedidos

Considere valores em reais, já convertidos para `float` apenas para simplificar o exemplo:

```python
from statistics import fmean, median, multimode

pedidos = [89.90, 120.00, 120.00, 149.90, 199.90, 249.90, 899.00]

print(f"Média: R$ {fmean(pedidos):.2f}")
print(f"Mediana: R$ {median(pedidos):.2f}")
print(f"Moda(s): {multimode(pedidos)}")
```

A média fica acima da mediana porque o pedido de R$ 899 puxa o resultado. Isso não torna a média errada: ela responde a uma pergunta diferente. A média distribui o total igualmente entre os pedidos; a mediana identifica o valor central depois da ordenação.

Em análises de ticket, salário, tempo de atendimento ou atraso de entrega, compare as duas medidas antes de resumir a realidade com um único número.

## API essencial do statistics

| Função | Pergunta que responde | Observação |
| --- | --- | --- |
| `mean(dados)` | Qual é a média aritmética? | Pode preservar `Decimal` e `Fraction` |
| `fmean(dados)` | Qual é a média em ponto flutuante? | Retorna `float` |
| `geometric_mean(dados)` | Qual é a média de fatores multiplicativos? | Exige valores positivos |
| `harmonic_mean(dados)` | Qual média usar para certas taxas? | Exige valores não negativos |
| `median(dados)` | Qual é o valor central? | Robusta a extremos |
| `median_low` / `median_high` | Qual elemento central real escolher? | Útil quando interpolar não faz sentido |
| `mode(dados)` | Qual é o valor mais frequente? | Devolve uma única moda |
| `multimode(dados)` | Quais são todos os valores mais frequentes? | Devolve uma lista |
| `quantiles(dados)` | Quais pontos dividem os dados em faixas? | Quartis, decis e percentis aproximados |
| `variance` / `stdev` | Qual é a dispersão da amostra? | Usam correção amostral |
| `pvariance` / `pstdev` | Qual é a dispersão da população? | Tratam os dados como conjunto completo |
| `correlation(x, y)` | As duas variáveis variam juntas linearmente? | Não demonstra causalidade |
| `linear_regression(x, y)` | Qual reta aproxima a relação? | Devolve inclinação e intercepto |

As funções levantam `statistics.StatisticsError` quando a entrada não atende aos requisitos, por exemplo ao calcular uma média de lista vazia ou um desvio padrão amostral com apenas um valor.

## mean ou fmean: qual usar?

As duas calculam média aritmética, mas têm uma diferença importante de tipos.

```python
from decimal import Decimal
from statistics import fmean, mean

valores = [Decimal("10.10"), Decimal("20.20"), Decimal("30.30")]

media_decimal = mean(valores)
media_float = fmean(valores)

print(media_decimal, type(media_decimal))  # Decimal('20.20')
print(media_float, type(media_float))      # 20.2 <class 'float'>
```

Use `fmean` quando `float` for a representação esperada, como em telemetria, notas, latências e indicadores aproximados. Use `mean` com `Decimal` quando a aritmética decimal precisar ser preservada.

Para valores monetários, não construa `Decimal` a partir de um `float` já arredondado de forma binária. Prefira strings, inteiros em centavos ou valores vindos de uma fonte que preserve a representação decimal. O guia de [`Decimal` para dinheiro](/blog/python-decimal-dinheiro-calculos-precisos/) explica esse cuidado em detalhes.

### Média ponderada

Em algumas versões modernas do Python, `fmean` aceita pesos. Para manter o exemplo portável e deixar a fórmula explícita, você também pode calcular a média ponderada assim:

```python
from statistics import fmean

notas = [7.0, 8.5, 9.0]
pesos = [2, 3, 5]

media_ponderada = sum(
    nota * peso for nota, peso in zip(notas, pesos, strict=True)
) / sum(pesos)

print(fmean(notas))          # média simples
print(media_ponderada)       # média ponderada
```

O parâmetro `strict=True` faz `zip` levantar erro se as listas tiverem tamanhos diferentes. Isso evita ignorar silenciosamente uma nota ou um peso.

## Média, mediana e outliers

Um outlier é um valor muito distante da maior parte dos dados. Ele pode ser erro, fraude, caso raro legítimo ou apenas parte natural da distribuição. Não remova extremos automaticamente.

```python
from statistics import fmean, median

minutos_atendimento = [4, 5, 5, 6, 7, 8, 42]

print(fmean(minutos_atendimento))   # 11.0
print(median(minutos_atendimento))  # 6
```

Dizer que o atendimento “leva 11 minutos” esconde que seis de sete casos terminaram em até oito minutos. Dizer apenas que a mediana é seis também esconde um atendimento de 42 minutos que talvez mereça investigação.

Um relatório mais honesto poderia informar:

- mediana de 6 minutos;
- média de 11 minutos;
- maior tempo de 42 minutos;
- 6 de 7 atendimentos concluídos em até 8 minutos.

A estatística ajuda a descrever; o contexto decide o que precisa de ação.

### median_low e median_high

Quando há quantidade par de itens, `median` interpola os dois centrais. Para categorias ordenadas ou valores indivisíveis, talvez você queira um elemento que exista na amostra:

```python
from statistics import median, median_high, median_low

prazos_dias = [1, 2, 3, 8]

print(median(prazos_dias))       # 2.5
print(median_low(prazos_dias))   # 2
print(median_high(prazos_dias))  # 3
```

Use a variante de forma consciente e documente a regra. Escolher sempre o menor ou o maior centro pode influenciar indicadores operacionais.

## mode e multimode: encontrando frequências

`mode` devolve um valor mais frequente. `multimode` devolve todos os valores empatados na frequência máxima, na ordem em que aparecem pela primeira vez.

```python
from statistics import mode, multimode

formas_pagamento = [
    "pix",
    "cartao",
    "pix",
    "boleto",
    "cartao",
]

print(mode(formas_pagamento))       # pix
print(multimode(formas_pagamento))  # ['pix', 'cartao']
```

Se empate for relevante, prefira `multimode`. Uma única moda pode induzir o relatório a declarar um vencedor onde existem duas categorias igualmente frequentes.

Para contagens completas, `collections.Counter` é mais informativo:

```python
from collections import Counter

contagem = Counter(formas_pagamento)
print(contagem.most_common())
```

O módulo `statistics` resume; `Counter` permite inspecionar toda a distribuição de categorias.

## Quantis, quartis e percentis

Quantis dividem dados ordenados em intervalos. Com `n=4`, você obtém três pontos que delimitam quatro grupos; com `n=10`, nove pontos para decis; com `n=100`, 99 pontos associados a percentis.

```python
from statistics import quantiles

tempos_ms = [82, 90, 95, 98, 105, 110, 118, 130, 145, 190, 260, 410]

q1, q2, q3 = quantiles(tempos_ms, n=4, method="inclusive")

print(f"Q1: {q1:.1f} ms")
print(f"Q2: {q2:.1f} ms")
print(f"Q3: {q3:.1f} ms")
```

O argumento `method` muda a forma de estimar os limites:

- `exclusive` é o padrão e trata a amostra como parte de uma população maior;
- `inclusive` inclui os extremos como percentis 0 e 100 do conjunto observado.

Não existe um método universalmente correto para toda situação. Ferramentas diferentes podem produzir percentis ligeiramente diferentes. Ao comparar seu resultado com Excel, banco de dados, pandas ou plataforma de observabilidade, confira a definição usada por cada sistema.

Para uma lista curta, percentil 99 transmite uma precisão que os dados não sustentam. Em relatórios pequenos, mediana, quartis, mínimo e máximo costumam ser mais interpretáveis.

## Variância e desvio padrão: amostra ou população?

O desvio padrão mede a dispersão dos valores em torno da média. Um resultado pequeno indica valores mais concentrados; um resultado grande indica maior variação. Ele usa a mesma unidade dos dados, ao contrário da variância, que fica na unidade ao quadrado.

A decisão principal é se seus dados representam **toda a população de interesse** ou apenas **uma amostra**.

```python
from statistics import pstdev, pvariance, stdev, variance

entregas_dias = [1, 2, 2, 3, 4, 6]

print(f"Variância amostral: {variance(entregas_dias):.2f}")
print(f"Desvio amostral: {stdev(entregas_dias):.2f}")
print(f"Variância populacional: {pvariance(entregas_dias):.2f}")
print(f"Desvio populacional: {pstdev(entregas_dias):.2f}")
```

Use `stdev` e `variance` quando os registros são uma amostra usada para estimar um universo maior. Use `pstdev` e `pvariance` quando os valores fornecidos são o conjunto completo que você quer descrever — por exemplo, todos os chamados encerrados pelo time naquele dia, se a pergunta estiver limitada exatamente àquele dia.

Não escolha a função pelo número “mais bonito”. Escreva no relatório qual universo está sendo analisado.

### Coeficiente de variação

Para comparar dispersões de métricas com escalas diferentes, às vezes se usa o coeficiente de variação:

```python
from statistics import fmean, stdev

valores = [91, 95, 98, 103, 113]
media = fmean(valores)
cv = stdev(valores) / media * 100

print(f"Coeficiente de variação: {cv:.1f}%")
```

A interpretação exige cautela quando a média está próxima de zero ou os valores podem ser negativos. Nesse cenário, o coeficiente pode ficar instável ou pouco significativo.

## Correlação não é causalidade

`correlation` calcula a correlação de Pearson por padrão, medindo associação linear entre duas sequências. O resultado vai de -1 a 1:

- perto de 1: associação linear positiva forte;
- perto de -1: associação linear negativa forte;
- perto de 0: pouca associação linear.

```python
from statistics import correlation

investimento_reais = [100, 150, 200, 250, 300, 350]
pedidos = [8, 11, 13, 17, 18, 22]

r = correlation(investimento_reais, pedidos)
print(f"Correlação: {r:.3f}")
```

Uma correlação alta não prova que aumentar investimento causou os pedidos. Sazonalidade, promoções, preço, canal e outros fatores podem influenciar as duas variáveis. Antes de interpretar:

1. confira se as listas estão alinhadas pelo mesmo período;
2. trate valores ausentes sem deslocar pares;
3. visualize os pontos;
4. procure outliers;
5. verifique se a relação parece linear;
6. evite linguagem causal sem um desenho de estudo adequado.

Em Python moderno, `correlation` também pode oferecer método de Spearman conforme a versão utilizada. Se seu projeto precisa funcionar em versões variadas, confira a documentação da versão mínima declarada no `pyproject.toml` e cubra a chamada com testes.

## Regressão linear simples

`linear_regression` ajusta uma reta do tipo `y = slope * x + intercept`:

```python
from statistics import linear_regression

horas_estudo = [1, 2, 3, 4, 5, 6]
notas = [52, 58, 67, 71, 79, 86]

modelo = linear_regression(horas_estudo, notas)

print(f"Inclinação: {modelo.slope:.2f}")
print(f"Intercepto: {modelo.intercept:.2f}")

previsao_4h30 = modelo.slope * 4.5 + modelo.intercept
print(f"Estimativa para 4,5 horas: {previsao_4h30:.1f}")
```

O exemplo demonstra a API, não uma regra sobre aprendizado. Uma reta baseada em poucos pontos não controla qualidade do estudo, dificuldade da prova ou conhecimento prévio. Também é arriscado extrapolar muito além do intervalo observado: um modelo treinado entre uma e seis horas não justifica previsões para cinquenta horas.

Para modelagem, validação, múltiplas variáveis e métricas de erro, use bibliotecas apropriadas, como scikit-learn ou statsmodels. O `statistics.linear_regression` atende bem a verificações e análises lineares pequenas.

## Limpando os dados antes do cálculo

As funções estatísticas não substituem validação. Dados vazios, texto inesperado, valores ausentes e `NaN` podem quebrar a execução ou produzir resultados difíceis de interpretar.

```python
import math
from statistics import StatisticsError, fmean, median


def resumir(valores: list[float | None]) -> dict[str, float | int]:
    limpos = [
        valor
        for valor in valores
        if valor is not None and math.isfinite(valor)
    ]

    if not limpos:
        raise ValueError("Nenhum valor numérico finito foi informado")

    return {
        "quantidade": len(limpos),
        "media": fmean(limpos),
        "mediana": median(limpos),
        "minimo": min(limpos),
        "maximo": max(limpos),
    }


try:
    resumo = resumir([10.0, None, 12.5, float("nan"), 15.0])
except (ValueError, StatisticsError) as erro:
    print(f"Não foi possível calcular: {erro}")
else:
    print(resumo)
```

Não remova `None`, infinito ou `NaN` sem registrar quantos valores foram descartados. A ausência pode revelar falha de coleta. Em um relatório de produção, retorne também `quantidade_recebida`, `quantidade_valida` e `quantidade_descartada`.

## Exemplo completo: resumo de um CSV de vendas

Este script usa apenas a biblioteca padrão. Ele lê um CSV, valida a coluna `valor`, calcula estatísticas e gera uma saída textual.

```python
import csv
from decimal import Decimal, InvalidOperation
from pathlib import Path
from statistics import StatisticsError, mean, median, quantiles, stdev


def carregar_valores(caminho: Path) -> tuple[list[Decimal], list[str]]:
    valores: list[Decimal] = []
    erros: list[str] = []

    with caminho.open(encoding="utf-8", newline="") as arquivo:
        leitor = csv.DictReader(arquivo)

        if "valor" not in (leitor.fieldnames or []):
            raise ValueError("O CSV precisa ter a coluna 'valor'")

        for numero_linha, linha in enumerate(leitor, start=2):
            texto = (linha.get("valor") or "").strip().replace(",", ".")

            try:
                valor = Decimal(texto)
            except InvalidOperation:
                erros.append(f"linha {numero_linha}: valor inválido")
                continue

            if valor < 0:
                erros.append(f"linha {numero_linha}: valor negativo")
                continue

            valores.append(valor)

    return valores, erros


def gerar_resumo(valores: list[Decimal]) -> dict[str, Decimal | int]:
    if len(valores) < 2:
        raise ValueError("São necessários pelo menos dois valores válidos")

    q1, _, q3 = quantiles(valores, n=4, method="inclusive")

    return {
        "quantidade": len(valores),
        "media": mean(valores),
        "mediana": median(valores),
        "desvio_amostral": stdev(valores),
        "q1": q1,
        "q3": q3,
        "minimo": min(valores),
        "maximo": max(valores),
    }


try:
    valores, erros = carregar_valores(Path("vendas.csv"))
    resumo = gerar_resumo(valores)
except (OSError, ValueError, StatisticsError) as erro:
    raise SystemExit(f"Falha ao gerar relatório: {erro}") from erro

print("Resumo das vendas")
for nome, valor in resumo.items():
    print(f"- {nome}: {valor}")

print(f"- linhas rejeitadas: {len(erros)}")
for erro in erros:
    print(f"  - {erro}")
```

Em uma aplicação real, confirme o separador decimal e o formato de origem. Substituir vírgula por ponto funciona em um CSV controlado, mas não interpreta corretamente todos os formatos monetários brasileiros, como `1.234,56`. Defina um contrato de entrada em vez de tentar adivinhar qualquer representação.

Se o arquivo exige filtros, agrupamentos por loja e junções, migre para [pandas](/blog/introducao-ao-pandas/). Para dominar o formato antes disso, veja [leitura e escrita de CSV com Python](/blog/python-csv-leitura-escrita-arquivos/).

## Testando os cálculos

Testes evitam regressões em regras de resumo:

```python
from decimal import Decimal

import pytest


def test_gerar_resumo():
    valores = [
        Decimal("10.00"),
        Decimal("20.00"),
        Decimal("30.00"),
        Decimal("40.00"),
    ]

    resumo = gerar_resumo(valores)

    assert resumo["quantidade"] == 4
    assert resumo["media"] == Decimal("25.00")
    assert resumo["mediana"] == Decimal("25.00")
    assert resumo["minimo"] == Decimal("10.00")
    assert resumo["maximo"] == Decimal("40.00")


def test_exige_dois_valores():
    with pytest.raises(ValueError, match="pelo menos dois"):
        gerar_resumo([Decimal("10.00")])
```

Evite afirmar igualdade exata entre `float`s calculados quando o resultado envolve aproximação. Use `pytest.approx` nesses casos. Para estruturar a suíte, consulte o guia de [testes com pytest](/guias/testes-com-pytest/).

## Quando usar statistics, pandas ou NumPy

| Cenário | Escolha recomendada |
| --- | --- |
| Lista pequena dentro de um script | `statistics` |
| CLI sem dependências externas | `statistics` |
| Regra estatística em teste unitário | `statistics` |
| CSV com filtros e agrupamentos | pandas |
| Join entre várias tabelas | pandas |
| Vetores grandes e operações numéricas | NumPy |
| Modelagem estatística e inferência | statsmodels ou SciPy |
| Machine learning com validação | scikit-learn |

Começar com `statistics` reduz dependências e deixa a regra explícita. Migrar depois não é fracasso: é uma resposta ao crescimento da estrutura, do volume ou da complexidade.

## Erros comuns

- **Calcular média de uma lista vazia** sem validar a entrada.
- **Usar média para dados muito assimétricos** sem mostrar mediana ou distribuição.
- **Confundir amostra e população** ao escolher `stdev` ou `pstdev`.
- **Apagar outliers automaticamente** sem investigar sua origem.
- **Misturar `float` e dinheiro** quando `Decimal` seria mais apropriado.
- **Calcular percentis em amostras minúsculas** e comunicar falsa precisão.
- **Interpretar correlação como causa**.
- **Desalinhar pares de dados** ao remover ausentes de `x` e `y` separadamente.
- **Ignorar a versão mínima do Python** ao usar recursos recentes da API.
- **Comparar resultados entre ferramentas** sem conferir o método de quantis.

## Checklist para um resumo confiável

- [ ] Defini a população ou amostra que quero descrever.
- [ ] Validei entradas vazias, ausentes, infinitas e inválidas.
- [ ] Registrei quantas linhas foram rejeitadas.
- [ ] Comparei média e mediana quando há possibilidade de extremos.
- [ ] Escolhi `stdev` ou `pstdev` de acordo com o desenho da análise.
- [ ] Documentei o método usado para quantis.
- [ ] Mantive pares alinhados em correlação e regressão.
- [ ] Evitei linguagem causal baseada apenas em correlação.
- [ ] Usei `Decimal` quando a precisão monetária exige isso.
- [ ] Escrevi testes para entradas normais e casos-limite.

## Perguntas frequentes

### Preciso instalar o módulo statistics com pip?

Não. Ele faz parte da biblioteca padrão. Importe apenas as funções necessárias: `from statistics import mean, median, stdev`.

### Qual é a diferença entre mean e fmean?

`mean` pode preservar tipos como `Decimal` e `Fraction`. `fmean` produz `float` e é uma escolha direta quando ponto flutuante atende à análise.

### Quando usar média ou mediana?

Use média para representar a distribuição proporcional do total e mediana para identificar o centro com menor influência de extremos. Em salários, tickets e tempos, apresentar ambas costuma ser melhor.

### Qual é a diferença entre stdev e pstdev?

`stdev` estima o desvio padrão de uma população a partir de uma amostra. `pstdev` descreve o conjunto fornecido como população completa.

### statistics substitui pandas e NumPy?

Não. Ele resolve cálculos descritivos em iteráveis simples com ótima legibilidade e zero dependências. Pandas e NumPy atendem volumes, estruturas e operações mais complexas.

## Próximo passo

Pegue um conjunto pequeno que você já usa — tempos de execução, notas, pedidos fictícios ou métricas de um projeto — e calcule quantidade, média, mediana, quartis, mínimo, máximo e desvio padrão. Depois escreva duas frases: o que os números mostram e o que eles não permitem concluir.

Se o objetivo é entrar em analytics, transforme o exercício em um repositório reproduzível e siga o guia de [estágio em dados com Python](/carreira/estagio-dados-python/). Para evoluir de listas para tabelas, continue pela [introdução ao pandas](/blog/introducao-ao-pandas/) e pela trilha de [analista de dados com Python](/carreira/python-analista-de-dados/). A biblioteca padrão é um ótimo ponto de partida porque obriga você a entender a medida antes de esconder a lógica atrás de um dataframe.
