Capítulo 8

Confianza y Tests de Hipótesis

Cómo cuantificar la incertidumbre de las estimaciones y tomar decisiones basadas en datos.

8.1 Intervalos de Confianza

El rango plausible del parámetro

Un intervalo de confianza al (1α)(1 - \alpha) es un rango que cubre el valor verdadero con probabilidad 1α1 - \alpha:

Xˉ±zα/2σn\bar{X} \pm z_{\alpha/2} \cdot \frac{\sigma}{\sqrt{n}}

Interpretación correcta

Si construimos 100 intervalos IC al 95%, ~95 contendrán el valor verdadero. No es "95% de probabilidad de que el parámetro esté en el intervalo".

t-student vs Gaussiana

Cuando σ es desconocido y n es pequeño, usamos la distribución t de Student con n-1 grados de libertad (colas más anchas).

python
import numpy as np
from scipy import stats

datos = np.array([1.2, 0.8, 1.5, 0.9, 1.1])
n = len(datos)
media = datos.mean()
se = stats.sem(datos)  # error estándar = std / sqrt(n)

# IC 95% usando t-student
alpha = 0.05
t_crit = stats.t.ppf(1 - alpha/2, df=n-1)
ic_inf = media - t_crit * se
ic_sup = media + t_crit * se

print(f"Media: {media:.3f}")
print(f"IC 95%: [{ic_inf:.3f}, {ic_sup:.3f}]")
print(f"Interpretación: con 95% de confianza, la media poblacional")
print(f"está entre {ic_inf:.3f} y {ic_sup:.3f}")

8.2 Bootstrapping

Cuando la teoría no alcanza

El bootstrap es un método no paramétrico para estimar la distribución de un estadístico mediante remuestreo con reemplazo de los datos originales.

Algoritmo

  1. Muestrear n observaciones con reemplazo de los datos
  2. Calcular el estadístico de interés
  3. Repetir B veces (ej: 10000)
  4. Usar los percentiles 2.5% y 97.5% como IC

Ventajas

  • • No asume distribución normal
  • • Funciona para estadísticos complejos (mediana, correlación, etc.)
  • • Solo requiere poder computar el estadístico
python
import numpy as np

datos = np.array([1.2, 0.8, 1.5, 0.9, 1.1, 3.2, 0.7])
n = len(datos)
B = 10000

# Bootstrap para la mediana
boot_medians = []
for _ in range(B):
    muestra = np.random.choice(datos, size=n, replace=True)
    boot_medians.append(np.median(muestra))

boot_medians = np.array(boot_medians)
ic_inf = np.percentile(boot_medians, 2.5)
ic_sup = np.percentile(boot_medians, 97.5)

print(f"Mediana muestral: {np.median(datos):.3f}")
print(f"IC 95% (bootstrap): [{ic_inf:.3f}, {ic_sup:.3f}]")

8.3 Tests de Hipótesis

¿El efecto es real o es ruido?

Marco general:

  • H0H_0 (hipótesis nula): no hay efecto, no hay diferencia
  • H1H_1 (hipótesis alternativa): hay efecto, hay diferencia
  • p-valor: probabilidad de observar los datos (o algo más extremo) si H0H_0 es cierta

Error Tipo I

Rechazar H0H_0 cuando es verdadera (falso positivo). Probabilidad = α (nivel de significancia, típicamente 0.05).

Error Tipo II

No rechazar H0H_0 cuando es falsa (falso negativo). Probabilidad = β. Potencia = 1 - β.

p-valor

p < α → rechazamos H0H_0(resultado "significativo"). p ≥ α → no podemos rechazar H0H_0.

python
from scipy import stats
import numpy as np

# T-test: ¿la media es diferente de 0?
datos = np.array([0.5, 0.8, -0.2, 0.6, 1.1, 0.3])

t_stat, p_valor = stats.ttest_1samp(datos, popmean=0)

print(f"Estadístico t: {t_stat:.3f}")
print(f"p-valor: {p_valor:.4f}")

alpha = 0.05
if p_valor < alpha:
    print("Rechazamos H₀: la media es significativamente distinta de 0")
else:
    print("No podemos rechazar H₀")

# T-test de dos muestras independientes
grupo_a = np.array([2.1, 2.5, 1.9, 2.3, 2.7])
grupo_b = np.array([1.8, 1.5, 2.0, 1.6, 1.7])

t_stat2, p_valor2 = stats.ttest_ind(grupo_a, grupo_b)
print(f"
Test de dos muestras: p = {p_valor2:.4f}")

8.4 Curvas ROC y PR

Evaluando clasificadores

La curva ROC (Receiver Operating Characteristic) grafica la tasa de verdaderos positivos vs tasa de falsos positivos al variar el umbral de clasificación.

Métricas clave

  • TPR (Sensibilidad): TP / (TP + FN)
  • FPR (1 - Especificidad): FP / (FP + TN)
  • AUC: área bajo la curva ROC (0.5 = aleatorio, 1 = perfecto)
  • Precisión: TP / (TP + FP)
  • Recall: = TPR

Cuándo usar ROC vs PR

ROC: clases balanceadas. PR (Precision-Recall): clases desbalanceadas (la curva PR es más sensible a falsos positivos cuando la clase positiva es rara).

python
from sklearn.metrics import roc_curve, auc, precision_recall_curve
import numpy as np

# Scores de un clasificador y etiquetas reales
y_true = np.array([0, 0, 1, 1, 0, 1, 0, 1, 1, 0])
y_scores = np.array([0.1, 0.2, 0.8, 0.6, 0.3, 0.9, 0.4, 0.7, 0.5, 0.2])

# ROC
fpr, tpr, thresholds = roc_curve(y_true, y_scores)
roc_auc = auc(fpr, tpr)
print(f"AUC: {roc_auc:.3f}")

# Precision-Recall
precision, recall, _ = precision_recall_curve(y_true, y_scores)

# Interpretación del AUC
if roc_auc >= 0.9:
    print("Clasificador excelente")
elif roc_auc >= 0.8:
    print("Clasificador bueno")
elif roc_auc >= 0.7:
    print("Clasificador aceptable")
else:
    print("Clasificador deficiente")