0Pricing
Pandas & NumPy Academy · Lección

Pruebas t para comparar medias

Ejecute pruebas t de una muestra, de dos muestras independientes y de muestras emparejadas con scipy.stats, e interprete los intervalos de confianza.

Pruebas t para comparar medias es una lección gratuita de Pandas & NumPy Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Pandas & NumPy Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

¿Qué es una prueba t?

Una prueba t es una prueba de hipótesis que determina si la diferencia entre las medias de varios grupos es estadísticamente significativa o probablemente se debe al azar. Compara la diferencia observada con la variabilidad de los datos y produce un estadístico t y un valor p. Si p ≤ 0.05 (el umbral convencional), rechazamos la hipótesis nula de que las medias son iguales. Hay tres tipos habituales: prueba t de una muestra, prueba t de dos muestras independientes y prueba t para muestras emparejadas, cada uno destinado a diseños experimentales diferentes.

Prueba t de una muestra

La prueba t de una muestra comprueba si la media de una muestra difiere significativamente de una media poblacional conocida o hipotética. Por ejemplo: «¿Nuestro tiempo medio de entrega difiere significativamente del estándar del sector de 3 días?». Use scipy.stats.ttest_1samp(data, popmean). La hipótesis nula es H₀: mean = popmean. Un valor p pequeño lleva a rechazar H₀ y a concluir que la media muestral difiere del objetivo.

import numpy as np
from scipy import stats

np.random.seed(42)
# Delivery times in days (true mean is ~3.5, not 3)
delivery_times = np.random.normal(loc=3.5, scale=0.8, size=50)

# Test: is our mean significantly different from 3 days?
stat, p = stats.ttest_1samp(delivery_times, popmean=3.0)
print(f'Sample mean: {delivery_times.mean():.3f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Differs from 3?', 'Yes' if p < 0.05 else 'No')

Prueba t de dos muestras independientes

La prueba t de dos muestras independientes compara las medias de dos grupos independientes. Por ejemplo: «¿La variante de la prueba A/B produce mayores ingresos medios que el control?». Use scipy.stats.ttest_ind(group_a, group_b). El parámetro equal_var es importante: establezca equal_var=False (prueba t de Welch) cuando los dos grupos puedan tener varianzas diferentes, que es la opción predeterminada más segura para la mayoría de los datos reales.

import numpy as np
from scipy import stats

np.random.seed(0)
# A/B test revenue per session
control = np.random.normal(loc=25.0, scale=8.0, size=80)
variant = np.random.normal(loc=28.0, scale=9.0, size=80)

# Welch's t-test (does not assume equal variances)
stat, p = stats.ttest_ind(control, variant, equal_var=False)
print(f'Control mean: {control.mean():.2f}')
print(f'Variant mean: {variant.mean():.2f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Significant difference?', 'Yes' if p < 0.05 else 'No')

Pruebas unilaterales frente a bilaterales

De forma predeterminada, las pruebas t son bilaterales: comprueban si las medias difieren en cualquier dirección (mayor O menor). Si tiene una hipótesis direccional («la variante aumenta los ingresos»), use una prueba unilateral con el parámetro alternative: 'greater' o 'less'. Una prueba unilateral tiene mayor potencia para la dirección específica, pero no proporciona evidencia sobre la dirección contraria. Decida la dirección antes de consultar los datos para evitar el HARKing (formular hipótesis después de conocer los resultados).

import numpy as np
from scipy import stats

np.random.seed(1)
control = np.random.normal(25, 8, 100)
variant = np.random.normal(27, 8, 100)

# Two-tailed (default): does mean differ at all?
stat, p_two = stats.ttest_ind(control, variant, equal_var=False,
                              alternative='two-sided')
# One-tailed: is variant > control?
stat, p_one = stats.ttest_ind(control, variant, equal_var=False,
                              alternative='less')

print(f'Two-tailed p: {p_two:.4f}')
print(f'One-tailed p (variant greater): {p_one:.4f}')

Prueba t para muestras relacionadas

La prueba t para muestras relacionadas se utiliza cuando cada observación del grupo A tiene una pareja natural en el grupo B; por ejemplo, mediciones antes y después de un tratamiento en los mismos sujetos, o en las mismas tiendas durante dos meses diferentes. El emparejamiento controla la variabilidad entre sujetos, lo que hace que la prueba sea más potente que una prueba t independiente con los mismos datos. Use scipy.stats.ttest_rel(before, after). El orden de los elementos debe coincidir: before[i] y after[i] deben proceder del mismo sujeto.

import numpy as np
from scipy import stats

np.random.seed(5)
# Blood pressure before and after medication (paired)
before = np.random.normal(130, 10, 30)
after = before - np.random.normal(5, 3, 30)  # Treatment reduces BP by ~5

stat, p = stats.ttest_rel(before, after)
print(f'Mean before: {before.mean():.2f}')
print(f'Mean after: {after.mean():.2f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Treatment effective?', 'Yes' if p < 0.05 else 'No')

Interpretación del estadístico t

El estadístico t mide a cuántos errores estándar del cero se encuentra la diferencia observada. Un estadístico t de 2 significa que la diferencia observada está 2 errores estándar por encima de lo esperado bajo la hipótesis nula. En una prueba bilateral con α=0.05 y una muestra grande, el valor crítico es aproximadamente ±1.96; por tanto, |t| > 1.96 implica p < 0.05. El valor p convierte el estadístico t en una probabilidad, lo que facilita su interpretación sin consultar tablas de la distribución t.

import numpy as np
from scipy import stats

# Demonstrate relationship between t-statistic and p-value
for t_val in [1.0, 1.96, 2.5, 3.0, 4.0]:
    # degrees of freedom = large sample -> t ~ normal
    p = 2 * stats.t.sf(abs(t_val), df=100)  # two-tailed
    print(f't = {t_val:4.2f} -> p = {p:.4f} '
          f'({'significant' if p < 0.05 else 'not significant'})')

Intervalos de confianza para la diferencia de medias

Un valor p por sí solo no indica la magnitud del efecto. Calcule siempre un intervalo de confianza para la diferencia de medias. Un IC del 95 % que incluye el cero es compatible con p > 0.05 (no significativo); uno que excluye el cero indica que la diferencia es significativa. El IC también indica si el tamaño del efecto es relevante en la práctica: una diferencia estadísticamente significativa de 0,01 $ en los ingresos puede ser irrelevante, mientras que una diferencia de 50 $ con p=0.06 aún puede ser importante para el negocio.

import numpy as np
from scipy import stats

np.random.seed(0)
control = np.random.normal(25, 8, 80)
variant = np.random.normal(28, 8, 80)

diff = variant.mean() - control.mean()
se = np.sqrt(control.var()/len(control) + variant.var()/len(variant))
df = len(control) + len(variant) - 2
t_crit = stats.t.ppf(0.975, df=df)
ci_low = diff - t_crit * se
ci_high = diff + t_crit * se

print(f'Mean difference: {diff:.2f}')
print(f'95% CI: [{ci_low:.2f}, {ci_high:.2f}]')
print('CI excludes zero:', ci_low > 0 or ci_high < 0)

Tamaño del efecto: d de Cohen

La significación estadística depende del tamaño de la muestra: con muestras suficientemente grandes, incluso las diferencias triviales se vuelven significativas. La d de Cohen mide la significación práctica (tamaño del efecto): la diferencia de medias dividida por la desviación estándar combinada. Como orientación: d < 0.2 es insignificante, 0.2–0.5 es pequeño, 0.5–0.8 es mediano y > 0.8 es grande. Informe siempre el tamaño del efecto junto con los valores p: un valor p significativo con d = 0.05 indica que la diferencia es real, pero probablemente no es relevante en la práctica.

import numpy as np
from scipy import stats

np.random.seed(0)
g1 = np.random.normal(25, 8, 200)
g2 = np.random.normal(28, 8, 200)

stat, p = stats.ttest_ind(g1, g2)

# Cohen's d
pooled_std = np.sqrt((g1.var() + g2.var()) / 2)
cohens_d = (g2.mean() - g1.mean()) / pooled_std

print(f'p-value: {p:.4f}')
print(f'Cohen\'s d: {cohens_d:.3f}')

if cohens_d < 0.2: print('Effect: negligible')
elif cohens_d < 0.5: print('Effect: small')
elif cohens_d < 0.8: print('Effect: medium')
else: print('Effect: large')

Supuestos de la prueba t

La prueba t independiente supone: (1) independencia: las observaciones dentro de cada grupo son independientes entre sí; (2) normalidad: los datos de cada grupo siguen aproximadamente una distribución normal (es robusta para n > 30 gracias al TCL); (3) en la prueba t de Student (equal_var=True), varianzas iguales. La prueba t de Welch (equal_var=False) relaja el supuesto 3 y es la opción preferida. Cuando la normalidad se incumple gravemente con muestras pequeñas (< 30), use en su lugar la prueba U de Mann-Whitney.

import numpy as np
from scipy import stats

np.random.seed(0)
g1 = np.random.normal(10, 2, 30)
g2 = np.random.normal(11, 5, 30)  # Very different variance!

# Levene test for equal variances
stat_l, p_l = stats.levene(g1, g2)
print(f'Levene test p: {p_l:.4f}')
if p_l < 0.05:
    print('Unequal variances -> use Welch\'s (equal_var=False)')
    stat, p = stats.ttest_ind(g1, g2, equal_var=False)
else:
    print('Equal variances OK -> Student\'s t-test')
    stat, p = stats.ttest_ind(g1, g2, equal_var=True)
print(f'Result: t={stat:.3f}, p={p:.4f}')

Pruebas t sobre Series de Pandas

En la práctica, los datos que desea analizar se encuentran en una columna de un DataFrame de Pandas. Puede pasar directamente una Series de Pandas a las funciones de scipy.stats; funcionan perfectamente tanto con Series como con arrays de NumPy. Un flujo de trabajo habitual consiste en filtrar el DataFrame para obtener la Series de cada grupo, ejecutar la prueba t y almacenar los resultados en un DataFrame de resumen. Este patrón se puede ampliar para probar muchos pares de columnas o grupos dentro de un bucle.

import pandas as pd
import numpy as np
from scipy import stats

np.random.seed(0)
df = pd.DataFrame({
    'group': ['A']*60 + ['B']*60,
    'revenue': np.concatenate([
        np.random.normal(100, 20, 60),
        np.random.normal(110, 22, 60)
    ])
})

grp_a = df.loc[df['group'] == 'A', 'revenue']
grp_b = df.loc[df['group'] == 'B', 'revenue']

stat, p = stats.ttest_ind(grp_a, grp_b, equal_var=False)
print(f'A mean: {grp_a.mean():.2f}, B mean: {grp_b.mean():.2f}')
print(f'p-value: {p:.4f}')

Problema de las comparaciones múltiples

Ejecutar muchas pruebas t simultáneamente aumenta la probabilidad de obtener un falso positivo. Si ejecuta 20 pruebas t con α=0.05, espera obtener 1 falso positivo por azar. Este es el problema de las comparaciones múltiples. Aplique la corrección de Bonferroni: divida α por el número de pruebas (p_threshold = 0.05 / n_tests). Para obtener mayor potencia con menos conservadurismo, use la corrección de la tasa de falsos descubrimientos de Benjamini-Hochberg (FDR), disponible en statsmodels. Corrija siempre las comparaciones múltiples en las pruebas A/B con muchas métricas.

import numpy as np
from scipy import stats

np.random.seed(0)
n_tests = 10
results = []
for i in range(n_tests):
    g1 = np.random.normal(0, 1, 50)
    g2 = np.random.normal(0.1, 1, 50)  # Tiny true effect
    _, p = stats.ttest_ind(g1, g2)
    results.append(p)

print('p-values:', [round(p, 3) for p in results])
bonferroni_thresh = 0.05 / n_tests
print(f'Bonferroni threshold: {bonferroni_thresh}')
print('Significant after Bonferroni:', sum(p < bonferroni_thresh for p in results))

Comprobación rápida

Compruebe su comprensión de los conceptos de análisis de datos de esta lección.

Resumen de la lección

En esta lección ha aprendido que ttest_1samp compara la media de una muestra con un valor de referencia, ttest_ind (la prueba de Welch con equal_var=False) compara dos grupos independientes y ttest_rel trabaja con mediciones relacionadas. Informe siempre la d de Cohen junto con el valor p para distinguir la significación estadística de la práctica. A continuación, comprobaremos las relaciones entre variables categóricas con la prueba de chi-cuadrado.

Preguntas frecuentes

¿La lección «Pruebas t para comparar medias» es gratis?

Sí — el texto completo de «Pruebas t para comparar medias» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Pandas & NumPy Academy, actualiza a CoddyKit PRO. El curso de Pandas & NumPy Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Pruebas t para comparar medias»?

Ejecute pruebas t de una muestra, de dos muestras independientes y de muestras emparejadas con scipy.stats, e interprete los intervalos de confianza. Practicas Pandas & NumPy Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Pandas & NumPy Academy?

No se requiere experiencia previa. Pandas & NumPy Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Pruebas t para comparar medias»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Pandas & NumPy Academy?

Sí. Cada lección de Pandas & NumPy Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Estadística descriptiva y pruebas de normalidad
  2. Pruebas t para comparar medias
  3. Prueba de chi cuadrado de independencia
  4. ANOVA y pruebas post hoc
← Volver a Pandas & NumPy Academy