18. Distribuciones y Probabilidades
Normal, binomial, Poisson y aplicaciones en análisis de datos
Domina distribuciones estadísticas (normal, binomial, Poisson) y cómo aplicarlas en análisis de datos reales.
Distribución Normal
La distribución más importante en estadística:
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import seaborn as sns
# Generar datos con distribución normal
mu = 100 # Media
sigma = 15 # Desviación estándar
datos = np.random.normal(mu, sigma, 10000)
# Visualizar
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.hist(datos, bins=50, density=True, alpha=0.7, color='steelblue', edgecolor='black')
x = np.linspace(mu - 4*sigma, mu + 4*sigma, 100)
plt.plot(x, stats.norm.pdf(x, mu, sigma), 'r-', linewidth=2, label='Curva normal')
plt.title('Distribución Normal (μ=100, σ=15)')
plt.xlabel('Valor')
plt.ylabel('Densidad')
plt.legend()
plt.subplot(1, 2, 2)
stats.probplot(datos, dist="norm", plot=plt)
plt.title('Q-Q Plot (Test de Normalidad)')
plt.tight_layout()
plt.show()
# Probabilidades
print(f"P(X < 85) = {stats.norm.cdf(85, mu, sigma):.4f}")
print(f"P(85 < X < 115) = {stats.norm.cdf(115, mu, sigma) - stats.norm.cdf(85, mu, sigma):.4f}")
print(f"P(X > 130) = {1 - stats.norm.cdf(130, mu, sigma):.4f}")
Teorema del Límite Central
# Demostrar que la media de muestras tiende a ser normal
medias_muestrales = []
for _ in range(1000):
muestra = np.random.exponential(scale=2, size=30) # Distribución NO normal
medias_muestrales.append(np.mean(muestra))
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
datos_expo = np.random.exponential(scale=2, size=1000)
plt.hist(datos_expo, bins=50, alpha=0.7, color='coral', edgecolor='black')
plt.title('Distribución Original (Exponencial)')
plt.xlabel('Valor')
plt.subplot(1, 2, 2)
plt.hist(medias_muestrales, bins=50, alpha=0.7, color='steelblue', edgecolor='black', density=True)
x = np.linspace(min(medias_muestrales), max(medias_muestrales), 100)
plt.plot(x, stats.norm.pdf(x, np.mean(medias_muestrales), np.std(medias_muestrales)), 'r-', linewidth=2)
plt.title('Distribución de Medias Muestrales (Normal!)')
plt.xlabel('Media')
plt.tight_layout()
plt.show()
Distribución Binomial
Para eventos con dos resultados posibles (éxito/fracaso):
# Ejemplo: Probabilidad de conversiones
n = 100 # Número de visitas
p = 0.05 # Probabilidad de conversión
# Generar datos
conversiones = stats.binom.rvs(n, p, size=1000)
# Visualizar
plt.figure(figsize=(10, 6))
unique, counts = np.unique(conversiones, return_counts=True)
plt.bar(unique, counts / len(conversiones), alpha=0.7, color='steelblue', edgecolor='black')
plt.title(f'Distribución Binomial (n={n}, p={p})')
plt.xlabel('Número de Conversiones')
plt.ylabel('Probabilidad')
plt.axvline(n*p, color='r', linestyle='--', linewidth=2, label=f'Media esperada: {n*p}')
plt.legend()
plt.show()
# Probabilidades
print(f"P(X = 5) = {stats.binom.pmf(5, n, p):.4f}")
print(f"P(X <= 3) = {stats.binom.cdf(3, n, p):.4f}")
print(f"P(X >= 7) = {1 - stats.binom.cdf(6, n, p):.4f}")
Distribución de Poisson
Para contar eventos en un intervalo de tiempo:
# Ejemplo: Visitas por hora a un sitio web
lambda_rate = 50 # Promedio de visitas por hora
# Generar datos
visitas = stats.poisson.rvs(lambda_rate, size=1000)
# Visualizar
plt.figure(figsize=(10, 6))
unique, counts = np.unique(visitas, return_counts=True)
plt.bar(unique, counts / len(visitas), alpha=0.7, color='coral', edgecolor='black')
plt.title(f'Distribución de Poisson (λ={lambda_rate})')
plt.xlabel('Número de Visitas por Hora')
plt.ylabel('Probabilidad')
plt.axvline(lambda_rate, color='r', linestyle='--', linewidth=2, label=f'λ = {lambda_rate}')
plt.legend()
plt.show()
# Probabilidades
print(f"P(X = 50) = {stats.poisson.pmf(50, lambda_rate):.4f}")
print(f"P(X < 40) = {stats.poisson.cdf(39, lambda_rate):.4f}")
print(f"P(X > 60) = {1 - stats.poisson.cdf(60, lambda_rate):.4f}")
Aplicación práctica: A/B Testing
# Simular resultados de A/B test
np.random.seed(42)
# Grupo A (control)
n_A = 1000
conversiones_A = 52
tasa_A = conversiones_A / n_A
# Grupo B (variante)
n_B = 1000
conversiones_B = 67
tasa_B = conversiones_B / n_B
print(f"Tasa de conversión A: {tasa_A*100:.2f}%")
print(f"Tasa de conversión B: {tasa_B*100:.2f}%")
print(f"Diferencia: {(tasa_B - tasa_A)*100:.2f} puntos porcentuales")
# Calcular intervalos de confianza (95%)
from statsmodels.stats.proportion import proportion_confint
ci_A = proportion_confint(conversiones_A, n_A, alpha=0.05, method='normal')
ci_B = proportion_confint(conversiones_B, n_B, alpha=0.05, method='normal')
print(f"\nIntervalo de confianza 95%:")
print(f" Grupo A: [{ci_A[0]*100:.2f}%, {ci_A[1]*100:.2f}%]")
print(f" Grupo B: [{ci_B[0]*100:.2f}%, {ci_B[1]*100:.2f}%]")
# Visualizar
plt.figure(figsize=(10, 6))
grupos = ['Grupo A\n(Control)', 'Grupo B\n(Variante)']
tasas = [tasa_A * 100, tasa_B * 100]
errores = [(tasa_A - ci_A[0])*100, (tasa_B - ci_B[0])*100]
plt.bar(grupos, tasas, yerr=errores, alpha=0.7, color=['steelblue', 'coral'],
edgecolor='black', capsize=10)
plt.ylabel('Tasa de Conversión (%)')
plt.title('Comparación de Conversión (A/B Test)')
plt.ylim(0, max(tasas) * 1.3)
for i, (g, t) in enumerate(zip(grupos, tasas)):
plt.text(i, t + 0.5, f'{t:.2f}%', ha='center', fontsize=12, fontweight='bold')
plt.tight_layout()
plt.show()
Resumen de la lección
✅ Distribución Normal: Más común en naturaleza, base del CLT ✅ Distribución Binomial: Eventos con dos resultados posibles ✅ Distribución de Poisson: Contar eventos en tiempo/espacio ✅ SciPy stats: Herramientas para trabajar con distribuciones ✅ Aplicaciones prácticas en A/B testing y análisis de datos
🎯 Próxima lección: 19. Correlaciones y Regresión Lineal
En la siguiente lección aprenderás a analizar relaciones entre variables. 🚀
¿Completaste esta lección?
Marca esta lección como completada. Tu progreso se guardará en tu navegador.