7 min de leitura
NumPy #9 - Distribuições de Probabilidade (Parte 1)
Como embaralhar e permutar arrays com numpy.random, visualizar distribuições com Seaborn, e gerar dados seguindo as distribuições Normal, Binomial, Poisson e Uniforme.
Lá no post #4, você viu o básico de numpy.random: randint(), rand() e choice(). Isso é suficiente pra sortear números soltos, mas o numpy.random vai bem mais fundo — ele sabe gerar dados que seguem distribuições de probabilidade específicas, o tipo de coisa usada o tempo todo pra simular dados realistas antes de testar um modelo. Como é bastante conteúdo, a gente divide em duas partes: hoje, embaralhamento, uma introdução à visualização com Seaborn, e as quatro distribuições mais comuns.
Embaralhando e permutando arrays
random.shuffle()
np.random.shuffle() embaralha um array no próprio lugar (in place) — ele modifica o array original e não devolve nada.
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
np.random.shuffle(arr)
print(arr)
A saída é (a ordem muda a cada execução):
[3 1 5 2 4]
random.permutation()
np.random.permutation() faz a mesma coisa, mas devolve um array novo embaralhado, deixando o original intacto.
arr = np.array([1, 2, 3, 4, 5])
embaralhado = np.random.permutation(arr)
print(embaralhado)
print(arr)
A saída é:
[4 2 5 1 3]
[1 2 3 4 5]
Repare que arr continua na ordem original — só embaralhado mudou. É a mesma diferença de comportamento que apareceu no post de copy vs view: uma função muta o array, a outra devolve uma cópia modificada.
Visualizando distribuições com Seaborn
Antes de gerar dados de distribuições diferentes, ajuda enxergar o formato de cada uma — e olhar uma lista de números não ajuda muito nisso. O Seaborn, uma biblioteca de visualização construída em cima do Matplotlib, tem uma função feita sob medida pra isso.
pip install seaborn
import matplotlib.pyplot as plt
import seaborn as sns
dados = np.random.normal(size=1000)
sns.displot(dados, kind="kde")
plt.show()
sns.displot(dados, kind="kde") desenha a curva de densidade dos dados — mostra em quais faixas de valor os dados se concentram mais ou menos, sem precisar de um histograma com barras. A partir daqui, todo exemplo de distribuição pode ser visualizado trocando só o dados por outra chamada de numpy.random.
Nota: o Seaborn não é NumPy — é uma biblioteca separada, focada em visualização estatística. Ela não faz parte do escopo desta série, mas aparece aqui porque é praticamente o par perfeito do
numpy.randompra enxergar o que cada distribuição representa.
Distribuição Normal (Gaussiana)
A distribuição Normal é a clássica “curva de sino” — a maioria dos valores fica perto da média, e a frequência cai conforme o valor se afasta dela pros dois lados. É a distribuição mais comum na natureza (altura de pessoas, erros de medição, etc.).
dados = np.random.normal(loc=0, scale=1, size=(2, 4))
print(dados)
A saída é (os valores mudam a cada execução):
[[ 0.49 -1.23 0.87 0.02]
[-0.65 1.14 -0.08 0.33]]
loc define a média da distribuição (o centro da curva), scale define o desvio padrão (o quão “espalhados” os valores ficam), e size define o formato do array de saída — os três parâmetros que você vai ver se repetir, com nomes um pouco diferentes, em quase toda distribuição do NumPy.
Exemplo prático — simulando notas de uma prova
Combinando a distribuição Normal com a indexação booleana vista no post #3, dá pra simular um cenário realista: as notas de 1000 alunos numa prova, com média 70 e desvio padrão 10, e então contar quantos ficaram acima de 90.
notas = np.random.normal(loc=70, scale=10, size=1000)
acima_de_90 = notas[notas > 90]
print(len(acima_de_90))
A saída é (o número exato muda a cada execução, mas fica sempre perto disso):
23
Isso bate com a teoria: em uma distribuição Normal, valores acima de 2 desvios padrão da média (aqui, acima de 70 + 2*10 = 90) representam só uma fatia pequena do total — por volta de 2 a 3% em 1000 amostras.
Distribuição Binomial
A Binomial é discreta (só gera inteiros, diferente da Normal, que é contínua) e modela o número de sucessos em um número fixo de tentativas com probabilidade fixa — o exemplo clássico é contar quantas caras saem em 10 lançamentos de moeda.
resultado = np.random.binomial(n=10, p=0.5, size=10)
print(resultado)
A saída é:
[6 4 5 7 5 3 6 5 4 8]
n é o número de tentativas em cada “rodada” (10 lançamentos de moeda), p é a probabilidade de sucesso em cada tentativa (0.5 = moeda honesta), e size é quantas rodadas simular. Cada valor do array é quantos sucessos saíram naquela rodada.
Distribuição de Poisson
A Poisson também é discreta, e modela quantas vezes um evento acontece em um intervalo fixo de tempo (ou espaço) — por exemplo, quantos clientes chegam a uma loja por hora.
resultado = np.random.poisson(lam=2, size=10)
print(resultado)
A saída é:
[1 3 2 0 4 2 1 2 3 1]
lam (de lambda) é a taxa média de ocorrência esperada no intervalo — com lam=2, o valor mais comum de aparecer é próximo de 2, mas valores mais altos ou mais baixos também aparecem, só que com menos frequência.
Distribuição Uniforme
A Uniforme é a mais simples das quatro: todo valor dentro de um intervalo tem exatamente a mesma chance de aparecer — nenhuma região é mais “concentrada” que outra.
resultado = np.random.uniform(low=0, high=10, size=(2, 3))
print(resultado)
A saída é:
[[3.42 7.91 0.55]
[9.14 2.08 5.67]]
low e high definem o intervalo (por padrão, 0.0 e 1.0). Se você plotar essa distribuição com sns.displot(), ao contrário da curva em sino da Normal, o gráfico fica praticamente “achatado” — sem pico central, porque nenhum valor é mais provável que outro.
Quatro distribuições já dão uma boa base, mas o numpy.random tem mais sete que vale conhecer, algumas específicas de áreas como processamento de sinais e testes estatísticos. É isso que fecha a série no próximo (e último) post.
Fonte adaptada: NumPy Random Permutations, NumPy Seaborn Module, Normal (Gaussian) Distribution, Binomial Distribution, Poisson Distribution, Uniform Distribution
Comentários