Saltar al contenido principal

Cómo obtener un valor p en Python

SciPy es la implementación de referencia contra la que se comprueban la mayoría de las demás herramientas, esta incluida. Aquí está cada función que produce un valor p, y la llamada al método que la mantiene exacta en la cola lejana.

8 min de lectura · Última revisión: 7 de agosto de 2026

La respuesta corta

Toda distribución de scipy.stats expone una función de supervivencia, sf, que es el área de la cola superior. Ese es el valor p de un contraste de cola derecha.

from scipy import stats

stats.norm.sf(2.5)              # z, unilateral
#> 0.006209665325776134

stats.t.sf(2.31, 27)            # t, unilateral
#> 0.014384284149660044

2 * stats.t.sf(abs(2.31), 27)   # t, bilateral
#> 0.028768568299320087

stats.chi2.sf(7.815, 3)         # chi-cuadrado, cola derecha
#> 0.04999390297488388

stats.f.sf(4.26, 2, 27)         # F, cola derecha
#> 0.02466186381480589

Chi-cuadrado y F siguen siendo de cola derecha. Duplicarlas no produce un contraste bilateral, produce un número sin significado.

Usa sf, nunca 1 - cdf

Estas dos líneas parecen intercambiables y no lo son:

stats.norm.sf(9)
#> 1.1285884059538324e-19

1 - stats.norm.cdf(9)
#> 0.0

cdf(9) está tan cerca de 1 que la doble precisión lo almacena como 1. La resta produce entonces exactamente cero, y un resultado que debería ser 1.13 × 10⁻¹⁹ se informa como imposible. sf calcula la cola sin formar nunca esa diferencia.

Esta es la fuente más habitual de valores p erróneos en el código de análisis, y es la razón de que este sitio escriba a mano su motor de distribuciones en lugar de restar de uno. La página de metodología publica las comparaciones contra estos mismos valores de SciPy.

Contrastes sobre datos en bruto

Si tienes observaciones en lugar de un estadístico, las funciones de contraste devuelven ambas cosas:

from scipy import stats

# Prueba t de Welch: hay que pedirla explícitamente
stats.ttest_ind(a, b, equal_var=False)

# Prueba t de Student (la opción predeterminada de SciPy)
stats.ttest_ind(a, b)

# Emparejada
stats.ttest_rel(before, after)

# Una muestra frente a un valor objetivo
stats.ttest_1samp(x, popmean=100)

# Chi-cuadrado sobre una tabla de contingencia
stats.chi2_contingency([[31, 19], [22, 28]])

# Correlación de Pearson
stats.pearsonr(x, y)

# ANOVA de un factor
stats.f_oneway(group1, group2, group3)

Fíjate en el valor predeterminado que pilla a la gente: ttest_ind supone varianzas iguales salvo que pases equal_var=False. R hace lo contrario. Si un resultado de SciPy no coincide con uno de R sobre los mismos datos, esta suele ser la razón. Welch es la opción más segura y es la que ejecuta esta calculadora.

chi2_contingency también aplica la corrección de continuidad de Yates a las tablas de 2 × 2 de forma predeterminada. Pasa correction=False para que coincida con una calculadora sin corrección.

Cómo leer los objetos de resultado

Las versiones modernas de SciPy devuelven tuplas con nombre, así que puedes extraer los campos por nombre en lugar de por posición:

res = stats.ttest_ind(a, b, equal_var=False)
res.pvalue
res.statistic
res.df

# Intervalo de confianza sobre la diferencia
res.confidence_interval(confidence_level=0.95)

La mayoría de las funciones de contraste aceptan además un argumento alternative —'two-sided', 'less' o 'greater'—, que es más limpio que dividir a mano un valor p bilateral y acierta la dirección por ti.

Cuando SciPy no basta

SciPy cubre los contrastes de hipótesis estándar. Para una salida de regresión con la tabla completa de coeficientes, usa statsmodels, que informa un valor p por coeficiente:

import statsmodels.api as sm

model = sm.OLS(y, sm.add_constant(X)).fit()
model.pvalues
model.summary()

Cómo darle formato para un artículo

Un float en bruto no es un valor p informable. El estilo APA suprime el cero a la izquierda y corta en tres decimales, así que 0.028768568 se convierte en p = .029 y cualquier valor por debajo de .001 se informa como p < .001 y no como p = .000, que nunca es cierto.

def apa(p):
    return "p < .001" if p < 0.001 else f"p = {p:.3f}".replace("0.", ".")

La guía de informe en estilo APA cubre el resto, y todas las calculadoras de aquí generan una cadena APA lista para usar junto al resultado.

Seguir leyendo

¿Listo para hacer los cálculos?

Nuestra calculadora muestra la distribución sombreada, el valor p exacto y una lectura en lenguaje claro de lo que respalda.

Abrir la calculadora de valor p

Preguntas frecuentes

¿Cuál es la diferencia entre sf y 1 - cdf en SciPy?

Matemáticamente ninguna; numéricamente muchísima. sf calcula la cola superior directamente, mientras que 1 - cdf calcula un número cercano a 1 y lo resta de 1, lo que pierde toda la precisión en cuanto la cola es pequeña. stats.norm.sf(9) devuelve 1.1285884059538324e-19 y 1 - stats.norm.cdf(9) devuelve exactamente 0.0. Usa siempre sf.

¿Cómo obtengo un p valor bilateral en SciPy?

Para una distribución simétrica, duplica la cola superior del estadístico en valor absoluto: 2 * stats.t.sf(abs(t), df). Con t = 2.31 y df = 27 eso da 0.028768568299320087. La mayoría de las funciones de contraste aceptan también alternative="two-sided", que hace la duplicación internamente y es menos propenso a errores. Nunca dupliques un valor p de chi-cuadrado o de F.

¿scipy ttest_ind usa Welch de forma predeterminada?

No. SciPy usa equal_var=True de forma predeterminada, que es la prueba t de Student suponiendo varianzas iguales. Tienes que pasar equal_var=False para obtener Welch. Esto es lo contrario que en R, cuyo t.test usa Welch de forma predeterminada, y es la explicación habitual cuando ambos producen valores p distintos sobre los mismos datos.

¿Por qué mi valor p se imprime como 0.0 en Python?

O bien usaste 1 - cdf en lugar de sf, que se desploma a cero en la cola lejana, o el valor verdadero ha desbordado por debajo el double representable más pequeño, en torno a 5e-324. El primer caso es un error que deberías corregir cambiando a sf. El segundo es un desbordamiento inferior genuino, y la forma honesta de informarlo es p < 1e-300 y no p = 0.

¿Cómo calculo un valor p a partir de una puntuación z en Python?

stats.norm.sf(z) para un contraste unilateral y 2 * stats.norm.sf(abs(z)) para uno bilateral. No intervienen grados de libertad porque la normal estándar no tiene. Si lo único que tienes a mano es una puntuación z, la calculadora de puntuación Z a valor p de este sitio hace el mismo cálculo sin abrir un intérprete.

¿De qué versión de SciPy salen estos valores?

De SciPy 1.17. Los valores de referencia publicados en la página de metodología son salida literal de esa versión, regenerada por un script incluido en el repositorio en lugar de transcrita a mano. Las funciones de distribución son numéricamente estables entre versiones recientes, así que las publicaciones anteriores de SciPy coinciden dentro del ruido de coma flotante.