W3docs

Distribuição Normal de Dados

Aprenda a distribuição normal (Gaussiana) em Python: PDF, CDF, Z-scores, regra empírica, testes de normalidade e quando usar transformações logarítmicas.

A distribuição normal (também chamada de distribuição Gaussiana) é a distribuição de probabilidade mais importante em estatística e aprendizado de máquina. Compreendê-la profundamente — não apenas reconhecer sua forma de sino, mas saber como medi-la, testá-la e trabalhar com ela em Python — oferece uma base sólida para preparação de dados, seleção de modelos e interpretação de resultados.

Este capítulo abrange:

  • O que é a distribuição normal e por que sua forma importa
  • A regra empírica (regra 68–95–99,7) e como verificá-la
  • Como gerar dados normalmente distribuídos com NumPy
  • Como avaliar a função de densidade de probabilidade (PDF) e a função de distribuição acumulada (CDF) com SciPy
  • Como calcular e interpretar Z-scores
  • Como testar se seus dados seguem uma distribuição normal
  • O que fazer quando os dados não são normais

O Que É a Distribuição Normal?

Uma distribuição de probabilidade descreve a probabilidade de cada valor de um conjunto de dados aparecer. A distribuição normal é uma distribuição contínua, simétrica e em forma de sino, definida por dois parâmetros:

  • Média (μ) — o centro do sino; onde fica o pico
  • Desvio padrão (σ) — o quão largo ou estreito é o sino; um σ maior dispersa os valores mais longe do centro

Como a curva é simétrica em torno da média, a média, mediana e moda são todas iguais para uma distribuição perfeitamente normal.

A fórmula para a função de densidade de probabilidade é:

f(x) = (1 / (σ√(2π))) × e^(−(x−μ)² / (2σ²))

Você não precisa aplicar essa fórmula manualmente — o stats.norm do SciPy cuida disso — mas é útil saber que a forma depende apenas de μ e σ.

A Regra Empírica (68–95–99,7)

Uma das propriedades mais práticas da distribuição normal é a regra empírica, também chamada de regra 68–95–99,7:

Intervalo em relação à médiaPercentagem de valores
Dentro de ±1 desvio padrão~68%
Dentro de ±2 desvios padrão~95%
Dentro de ±3 desvios padrão~99,7%

Isso significa que em um conjunto de dados normalmente distribuído, quase todos os valores estão a três desvios padrão da média. Valores além de ±3σ são genuinamente raros — aproximadamente 1 em cada 370 observações.

O exemplo a seguir simula 10 000 valores de uma distribuição normal padrão (μ=0, σ=1) e verifica a regra:

import numpy as np

rng = np.random.default_rng(seed=42)
mu, sigma = 0, 1
data = rng.normal(loc=mu, scale=sigma, size=10000)

w1 = np.mean(np.abs(data - mu) < 1 * sigma) * 100
w2 = np.mean(np.abs(data - mu) < 2 * sigma) * 100
w3 = np.mean(np.abs(data - mu) < 3 * sigma) * 100

print(f"Within 1 std: {w1:.1f}%  (expected ~68%)")
print(f"Within 2 std: {w2:.1f}%  (expected ~95%)")
print(f"Within 3 std: {w3:.1f}%  (expected ~99.7%)")

Saída:

Within 1 std: 67.8%  (expected ~68%)
Within 2 std: 95.4%  (expected ~95%)
Within 3 std: 99.7%  (expected ~99.7%)

A regra empírica é imediatamente útil: se a pontuação de um teste está mais de dois desvios padrão acima da média, ela está no topo de ~2,5% de todas as pontuações.

Gerando Dados Normalmente Distribuídos com NumPy

O gerador de números aleatórios do NumPy produz amostras que se aproximam de uma distribuição normal. O método rng.normal() aceita a média (loc), o desvio padrão (scale) e o número de amostras (size):

import numpy as np

rng = np.random.default_rng(seed=7)

# Simulate IQ scores: mean=100, std=15
samples = rng.normal(loc=100, scale=15, size=1000)

print(f"Sample mean:  {samples.mean():.1f}")
print(f"Sample std:   {samples.std():.1f}")
print(f"Min:          {samples.min():.1f}")
print(f"Max:          {samples.max():.1f}")
print(f"Sample size:  {len(samples)}")

Saída:

Sample mean:  98.9
Sample std:   14.1
Min:          51.2
Max:          138.6
Sample size:  1000

A média e o desvio padrão da amostra são próximos — mas não exatamente — de 100 e 15, porque uma amostra finita tem variação aleatória natural. Com 10 000 amostras, as estimativas convergem mais para os parâmetros verdadeiros.

Por que usar uma semente? Passar seed=7 para default_rng torna os resultados reproduzíveis. Qualquer pessoa que execute o mesmo código obtém os mesmos números, o que é essencial para depuração e compartilhamento de resultados. A interface default_rng (introduzida no NumPy 1.17) é preferível à antiga np.random.normal() porque evita o uso de estado global compartilhado.

Avaliando a PDF e a CDF com SciPy

Função de Densidade de Probabilidade (PDF)

A PDF informa a probabilidade relativa de um valor. Um valor de PDF mais alto em x significa que x é mais provável de ocorrer. Para uma distribuição normal, o pico está na média:

from scipy import stats

mu, sigma = 0, 1

x_values = [-2, -1, 0, 1, 2]
for x in x_values:
    pdf = stats.norm.pdf(x, loc=mu, scale=sigma)
    print(f"  pdf({x:2d}) = {pdf:.4f}")

Saída:

  pdf(-2) = 0.0540
  pdf(-1) = 0.2420
  pdf( 0) = 0.3989
  pdf( 1) = 0.2420
  pdf( 2) = 0.0540

A PDF é simétrica: pdf(-1) é igual a pdf(1), e pdf(0) é o máximo (o pico da curva em sino). O próprio valor da PDF não é uma probabilidade — é uma densidade. Para obter uma probabilidade, você integra a PDF em um intervalo, que é o que a CDF faz.

Função de Distribuição Acumulada (CDF)

A CDF em um valor x fornece a probabilidade de uma observação extraída aleatoriamente ser menor ou igual a x. Use stats.norm.cdf() para responder a perguntas práticas de probabilidade:

from scipy import stats

mu, sigma = 170, 10  # adult heights in cm

# Probability that a randomly chosen person is shorter than 180 cm
p_below_180 = stats.norm.cdf(180, loc=mu, scale=sigma)
print(f"P(height < 180 cm) = {p_below_180:.4f}")

# Probability of being taller than 185 cm
p_above_185 = 1 - stats.norm.cdf(185, loc=mu, scale=sigma)
print(f"P(height > 185 cm) = {p_above_185:.4f}")

# Probability of falling between 160 cm and 180 cm
p_range = stats.norm.cdf(180, loc=mu, scale=sigma) - stats.norm.cdf(160, loc=mu, scale=sigma)
print(f"P(160 < height < 180 cm) = {p_range:.4f}")

Saída:

P(height < 180 cm) = 0.8413
P(height > 185 cm) = 0.0668
P(160 < height < 180 cm) = 0.6827

O terceiro resultado confirma a regra empírica: o intervalo μ±σ (160–180 cm) contém cerca de 68% das observações.

Z-Scores: Padronizando a Distribuição

Um Z-score (também chamado de pontuação padrão) mede quantos desvios padrão um valor está da média:

Z = (x − μ) / σ

Os Z-scores permitem comparar valores de distribuições com médias e desvios padrão diferentes em uma escala comum. Um Z-score de +2.0 significa que um valor está dois desvios padrão acima da média, independentemente das unidades originais.

import numpy as np
from scipy import stats

heights = np.array([171.3, 168.7, 176.4, 171.0, 164.6, 173.6, 183.0, 179.5])

# Calculate Z-scores manually
mean = heights.mean()
std  = heights.std(ddof=1)   # ddof=1 for the sample standard deviation
z_manual = (heights - mean) / std
print("Z-scores (manual):", np.round(z_manual, 2))

# Or use scipy directly
z_scipy = stats.zscore(heights, ddof=1)
print("Z-scores (scipy): ", np.round(z_scipy, 2))

Saída:

Z-scores (manual): [-0.37 -0.81  0.49 -0.42 -1.5   0.01  1.59  1.01]
Z-scores (scipy):  [-0.37 -0.81  0.49 -0.42 -1.5   0.01  1.59  1.01]

Uma pessoa com altura de 183,0 cm tem um Z-score de +1.59, o que significa que está 1,59 desvios padrão acima da média. Em uma distribuição normal, isso a coloca aproximadamente nos 6% superiores da população.

Quando os Z-scores são úteis

  • Detecção de outliers: valores com |Z| > 3 são quase certamente outliers em uma distribuição normal.
  • Escalonamento de características: converter características em Z-scores (média zero, variância unitária) é chamado de padronização, e é exigido por algoritmos sensíveis à magnitude das características, como SVM e k-vizinhos mais próximos. Veja o capítulo sobre escalonamento de características para saber como aplicar isso com o StandardScaler do scikit-learn.
  • Comparar grandezas diferentes: se você quiser comparar a pontuação de matemática de um aluno (média 70, desvio padrão 10) com sua pontuação de leitura (média 50, desvio padrão 5), os Z-scores fornecem uma comparação justa.

Testando se os Dados Seguem uma Distribuição Normal

Antes de aplicar algoritmos que assumem normalidade, verifique a suposição. As duas abordagens mais comuns são o teste de Shapiro-Wilk (para amostras de até ~5 000) e uma rápida inspeção visual.

Teste de Shapiro-Wilk

O teste de Shapiro-Wilk retorna uma estatística W e um p-valor. Um p-valor acima de 0,05 significa que não há evidências suficientes para rejeitar a normalidade. Um p-valor em ou abaixo de 0,05 indica que é improvável que os dados sejam normais.

import numpy as np
from scipy import stats

rng = np.random.default_rng(seed=42)

# Normally distributed sample
normal_sample = rng.normal(loc=0, scale=1, size=50)
stat_n, p_n = stats.shapiro(normal_sample)
print(f"Normal sample  — W={stat_n:.3f}, p={p_n:.3f}")
if p_n > 0.05:
    print("  => Cannot reject normality.")
else:
    print("  => Reject normality.")

# Skewed sample (exponential distribution)
skewed_sample = rng.exponential(scale=1, size=50)
stat_s, p_s = stats.shapiro(skewed_sample)
print(f"Skewed sample  — W={stat_s:.3f}, p={p_s:.3f}")
if p_s > 0.05:
    print("  => Cannot reject normality.")
else:
    print("  => Reject normality.")

Saída:

Normal sample  — W=0.984, p=0.730
  => Cannot reject normality.
Skewed sample  — W=0.808, p=0.000
  => Reject normality.

A amostra normal é aprovada facilmente (p=0,730). A amostra exponencial falha decisivamente (p≈0).

Quando o teste de normalidade importa

Nem todo algoritmo precisa de características normalmente distribuídas. Modelos baseados em árvores (árvores de decisão, florestas aleatórias, gradient boosting) e redes neurais são completamente agnósticos em relação à distribuição. A normalidade é mais importante para:

  • Testes estatísticos paramétricos (teste t, ANOVA, correlação de Pearson) — os p-valores desses testes só são válidos se os dados forem aproximadamente normais.
  • Análise discriminante linear (LDA) — assume que cada classe é normalmente distribuída.
  • Naive Bayes Gaussiano — modela explicitamente cada característica como uma Gaussiana.
  • Intervalos de confiança e de previsão em regressão linear — derivados da normalidade dos resíduos.

Quando os Dados Não São Normais: Transformação Logarítmica

Um remédio comum para dados com assimetria à direita (por exemplo, renda, preços de imóveis, valores de transações) é a transformação logarítmica, que comprime valores grandes e expande valores pequenos. O resultado frequentemente se aproxima de uma distribuição normal:

import numpy as np
from scipy import stats

rng = np.random.default_rng(seed=7)

# Simulate log-normally distributed incomes (a realistic pattern)
incomes = rng.lognormal(mean=10.5, sigma=0.5, size=1000)
log_incomes = np.log(incomes)

sk_before = stats.skew(incomes)
sk_after  = stats.skew(log_incomes)

stat_before, p_before = stats.shapiro(incomes[:50])
stat_after,  p_after  = stats.shapiro(log_incomes[:50])

print(f"Before transform — skewness: {sk_before:.2f},  Shapiro p={p_before:.3f}")
print(f"After log transform — skewness: {sk_after:.2f}, Shapiro p={p_after:.3f}")

Saída:

Before transform — skewness: 1.44,  Shapiro p=0.000
After log transform — skewness: 0.01, Shapiro p=0.940

Após a transformação logarítmica, a assimetria cai de 1,44 para quase zero, e o p-valor do Shapiro-Wilk sobe de 0,000 para 0,940 — os dados transformados passam facilmente no teste de normalidade.

Atenção:

  • A transformação logarítmica exige que todos os valores sejam estritamente positivos. Se seus dados contêm zeros, use np.log(x + 1) (transformação log-mais-um).
  • Se seus dados têm assimetria à esquerda (cauda longa à esquerda), tente uma transformação quadrática ou de reflexão.
  • Sempre aplique a mesma transformação tanto ao conjunto de treinamento quanto a quaisquer novos dados no momento da inferência.

Por Que a Distribuição Normal Importa no Aprendizado de Máquina

A distribuição normal aparece em todos os lugares no ML, frequentemente de forma implícita:

  • Teorema Central do Limite: a média de uma grande amostra aleatória é aproximadamente normalmente distribuída, independentemente da distribuição original. Isso fundamenta intervalos de confiança e testes de hipóteses sobre métricas de modelos.
  • Análise de resíduos: os resíduos de uma regressão linear bem ajustada devem ser aproximadamente normais. Desvios indicam má especificação do modelo.
  • Inicialização de pesos em redes neurais: esquemas como a inicialização Xavier e He extraem pesos iniciais de distribuições normais para evitar gradientes que desaparecem ou explodem.
  • Processos Gaussianos: uma família de modelos probabilísticos que colocam uma distribuição normal sobre funções, usados em otimização Bayesiana e quantificação de incerteza.
  • Detecção de outliers: em muitos domínios, dados além de ±3σ da média são sinalizados como anomalias.

Entender onde a distribuição normal é assumida — e quando essa suposição se rompe — ajuda a evitar erros silenciosos de modelagem.

Resumo

  • A distribuição normal é definida por sua média (μ) e desvio padrão (σ); sua curva em forma de sino é simétrica em torno da média.
  • A regra empírica afirma que 68%, 95% e 99,7% dos valores estão dentro de 1, 2 e 3 desvios padrão da média.
  • Use numpy.random.default_rng().normal() para gerar amostras normalmente distribuídas e scipy.stats.norm.pdf() / .cdf() para avaliar probabilidades.
  • Os Z-scores padronizam valores para uma escala comum; são essenciais para detecção de outliers e escalonamento de características.
  • Use scipy.stats.shapiro() para testar formalmente a normalidade — mas lembre-se de que muitos algoritmos modernos de ML não a exigem.
  • Quando os dados têm assimetria à direita, uma transformação logarítmica frequentemente os torna aproximadamente normais.

Para uma comparação mais ampla dos tipos de distribuição (uniforme, assimétrica, multimodal), consulte o capítulo sobre Distribuição de Dados. Para saber como medir dispersão e centro, veja Desvio Padrão e Média, Mediana e Moda.

Was this page helpful?