Módulo 6: Statistical Significance

La hipótesis nula y el p-value

Descripción

La lección anterior dejó una intuición clara pero informal: el ruido de muestreo, por sí solo, ya puede producir swings del tamaño del lift observado entre control y variant. Esta lección convierte esa intuición en dos herramientas precisas, calculables, que cualquiera puede reproducir con los mismos datos: la hipótesis nula (nullHypothesis), la versión formal de "no hay ningún efecto real, control y variant vienen de la misma tasa verdadera"; y el p-value (pValue), el número que mide qué tan sorprendente sería el resultado observado si esa hipótesis fuera cierta. Al final de esta lección vas a tener, por primera vez en el módulo, un z-score y un p-value calculados sobre los datos reales del A/B test de recommendations.

Conexión con el módulo. Esta lección construye el núcleo matemático que el resto del módulo va a refinar y empaquetar: la lección 4 corrige los malentendidos más comunes sobre el número que acabas de calcular aquí; la lección 5 agrega el intervalo de confianza como complemento; y la lección 6 toma exactamente esta misma lógica —hipótesis nula, proporción pooled, z-score, p-value— y la empaqueta en abTest(), la función verificada y reutilizable del módulo.

Una analogía: el jurado que presume inocencia

En un juicio, el jurado no empieza preguntando "¿es culpable?". Empieza asumiendo, por default, que el acusado es inocente — esa es la hipótesis de partida, y se mantiene hasta que la evidencia presentada sea lo bastante fuerte como para descartarla "más allá de toda duda razonable". El fiscal no tiene que probar la inocencia; tiene que acumular evidencia tan improbable bajo el supuesto de inocencia que sea razonable abandonar esa presunción.

La hipótesis nula funciona exactamente igual, con otro nombre. recommendations no tiene ningún efecto real — esa es la presunción de partida, la misma que en la analogía de la moneda de la lección 1: "la moneda es justa hasta que se demuestre lo contrario". El experimento no tiene que probar que hay un efecto; tiene que acumular una diferencia tan improbable bajo el supuesto de "no hay efecto" que sea razonable abandonar esa presunción. El p-value es, en esta analogía, el equivalente de "qué tan convincente es la evidencia del fiscal, medida en una escala de 0 a 1" — cuanto más chico, más incómoda le resulta la evidencia a la hipótesis de inocencia (o, en el caso de Mercado, a la hipótesis de "no hay efecto").

Y hay una asimetría importante, la misma que existe en un tribunal: no encontrar evidencia suficiente para condenar no es lo mismo que probar la inocencia. Un jurado que dice "no culpable" no está diciendo "definitivamente no lo hizo" — está diciendo "la evidencia presentada no alcanzó el umbral". Guarda esta idea; la lección 4 va a volver sobre ella con todo detalle, porque es uno de los errores más caros de toda la experimentación.

Ejemplo trabajado: el z-score y el p-value del A/B de Mercado

La hipótesis nula del experimento de recommendations se escribe así: nullHypothesis: la tasa de conversion verdadera de control y de variant es la MISMA. Bajo esa hipótesis, la mejor estimación de esa tasa común no es ni rateControl ni rateVariant por separado — es la proporción combinada de las dos muestras, la pooled proportion: si control y variant en el fondo son la misma cosa, la forma más honesta de estimar esa tasa compartida es juntar todas las conversiones y todas las visitas de ambos grupos en un solo cálculo.

Con esa tasa pooled se puede calcular el error estándar esperado de la diferencia entre dos muestras de esos tamaños —qué tanto deberían rebotar rateControl y rateVariant entre sí, solo por azar, si de verdad vinieran de la misma tasa—. Dividir la diferencia observada entre ese error estándar da el z-score: cuántos "errores estándar de ruido esperado" de distancia hay entre lo que se observó y lo que la hipótesis nula predice (una diferencia de cero).

// Primer calculo del z-score y el p-value sobre el A/B de Mercado (control vs variant).
// Todavia SIN empaquetar en abTest() ni sin el intervalo de confianza -- eso llega en L5/L6.
// Phi (CDF normal estandar) via la aproximacion de Abramowitz & Stegun 7.1.26 de la funcion
// error: Phi(x) = 0.5 * (1 + erf(x / sqrt(2))).
function erf(x) {
  const sign = x < 0 ? -1 : 1;
  x = Math.abs(x);
  const a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741,
        a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911;
  const t = 1 / (1 + p * x);
  const y = 1 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
  return sign * y;
}
function normalCdf(x) {
  return 0.5 * (1 + erf(x / Math.sqrt(2)));
}

// Datos del modulo 5: control 384/12000 (3.2%), variant 456/12000 (3.8%), lift +18.75%
const control = { n: 12000, conv: 384 };
const variant = { n: 12000, conv: 456 };

const p1 = control.conv / control.n;
const p2 = variant.conv / variant.n;

// H0: no hay diferencia real -- p1 y p2 vienen de la MISMA tasa verdadera. Bajo H0, la
// mejor estimacion de esa tasa comun es la proporcion pooled (agrupada).
const pooled = (control.conv + variant.conv) / (control.n + variant.n);
const sePooled = Math.sqrt(pooled * (1 - pooled) * (1 / control.n + 1 / variant.n));
const z = (p2 - p1) / sePooled;

// p-value de DOS colas: la probabilidad de ver un |z| tan grande o mayor, si H0 fuera cierta.
const pValue = 2 * (1 - normalCdf(Math.abs(z)));

console.log('=== z-score y p-value del A/B de Mercado (control vs variant) ===\n');
console.log('rateControl = ' + control.conv + '/' + control.n + ' = ' + (p1 * 100).toFixed(2) + '%');
console.log('rateVariant = ' + variant.conv + '/' + variant.n + ' = ' + (p2 * 100).toFixed(2) + '%');
console.log('pooled      = ' + (pooled * 100).toFixed(4) + '%   (tasa comun asumida bajo H0)');
console.log('sePooled    = ' + sePooled.toFixed(6));
console.log('z           = ' + z.toFixed(4));
console.log('pValue      = ' + pValue.toFixed(4) + '  (dos colas)');
console.log('\n¿p < 0.05? ' + (pValue < 0.05 ? 'si' : 'no'));

Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:

=== z-score y p-value del A/B de Mercado (control vs variant) ===

rateControl = 384/12000 = 3.20%
rateVariant = 456/12000 = 3.80%
pooled      = 3.5000%   (tasa comun asumida bajo H0)
sePooled    = 0.002373
z           = 2.5289
pValue      = 0.0114  (dos colas)

¿p < 0.05? si

Lee el resultado con cuidado, palabra por palabra, porque la formulación exacta importa: si recommendations no tuviera ningún efecto real (la hipótesis nula), la probabilidad de observar una diferencia de conversión tan grande, o más grande, que la que se vio entre control y variant es de apenas 1.14%. No es imposible bajo la hipótesis nula —nunca lo es—, pero es poco frecuente: pasaría, por puro azar, aproximadamente 1 de cada 88 experimentos si de verdad no hubiera ningún efecto. Con el umbral convencional de alpha = 0.05 (5%), 1.14% cae por debajo — el resultado es estadísticamente significativo: hay evidencia suficiente para dudar de la hipótesis nula.

Por qué "dos colas"

El cálculo usó 2 * (1 - Phi(|z|)) en vez de solo 1 - Phi(z). La razón: antes de correr el experimento, el equipo de Mercado no sabía si recommendations iba a subir o bajar la conversión —era una apuesta razonable, pero no una certeza—. Un test de dos colas contempla las dos direcciones posibles: pregunta "¿qué tan sorprendente es una diferencia de este tamaño, en cualquier dirección?", no solo "¿qué tan sorprendente es una subida de este tamaño?". Eso es lo correcto cuando la pregunta original era honestamente abierta en ambos sentidos, que es el caso normal de un A/B test de producto — nadie lanza un experimento estando 100% seguro, de antemano, de que el resultado solo puede ir para un lado.

Errores comunes

Calcular el z-score con el error estándar de una sola muestra, no el pooled. Qué pasa: alguien calcula el error estándar de rateControl solo, o de rateVariant solo, y lo usa para el z-score, en vez de combinar ambas muestras bajo la hipótesis nula. Por qué pasa: parece más simple usar "el" error estándar de una tasa, sin pensar en que la hipótesis nula específicamente asume que ambas tasas son la misma, y esa suposición cambia cómo se calcula el error estándar correcto. Cómo detectarlo: si el cálculo del error estándar no usa pooled (la proporción combinada de conversiones y visitas de ambos grupos), el z-score resultante no corresponde al test de hipótesis nula estándar. Cómo corregirlo: para el z-score y el p-value, siempre usa la proporción pooled — es la fórmula estándar del z-test de dos proporciones, y es la que usa abTest() en la lección 6. (El intervalo de confianza de la lección 5, en cambio, sí usa el error estándar no-pooled — por una razón distinta, que esa lección explica.)

Pensar que el p-value mide el tamaño del efecto. Qué pasa: alguien compara dos experimentos, ve que uno tiene p=0.01 y el otro p=0.04, y concluye que el primero tiene "un efecto más grande" o "más importante" que el segundo. Por qué pasa: es tentador tratar al p-value como una escala de "qué tan fuerte es el efecto", como si fuera un termómetro. Cómo detectarlo: si alguien dice "esto es más significativo, entonces el impacto en el negocio es mayor", está mezclando dos preguntas distintas. Cómo corregirlo: el p-value mide confianza en que hay algún efecto, no el tamaño de ese efecto — dos experimentos con el mismo lift real pueden tener p-values muy distintos solo por diferencias en el tamaño de muestra, y dos experimentos con p-values parecidos pueden tener lifts de tamaños completamente distintos. El tamaño del efecto lo mide el lift (módulo 5) y, sobre todo, el intervalo de confianza (lección 5 de este módulo) — no el p-value por sí solo. Este error se retoma con más profundidad en la lección 4.

Ejercicios

Ejercicio 1 — Calcula la proporción pooled a mano. Con control = {n: 12000, conv: 384} y variant = {n: 12000, conv: 456}, calcula a mano el valor de pooled antes de correr el código. Verifica tu resultado contra la salida de la lección.

Ver solución

pooled = (384 + 456) / (12000 + 12000) = 840 / 24000 = 0.035 = 3.5%. Coincide exactamente con el pooled = 3.5000% de la salida — tiene sentido, porque 3.5% está justo entre el 3.2% de control y el 3.8% de variant (y, como ambos grupos tienen el mismo tamaño, el pooled es simplemente el promedio de las dos tasas).

Ejercicio 2 — Interpreta el p-value con tus propias palabras. Sin copiar la frase de la lección, escribe en una oración qué significa pValue = 0.0114 para el experimento de Mercado. Evita las palabras "probabilidad de que la hipótesis nula sea verdadera" — esa frase, aunque suena parecida, es incorrecta (la lección 4 explica por qué).

Ver solución

Una formulación correcta: "Si recommendations no tuviera ningún efecto real sobre la conversión, ver una diferencia tan grande —o más grande— que la observada entre control y variant pasaría, por puro azar de muestreo, apenas un 1.14% de las veces." La frase describe la probabilidad del dato observado (o algo más extremo) bajo la hipótesis nula, no la probabilidad de que la hipótesis nula sea cierta o falsa — esa distinción exacta es el tema completo de la próxima lección.

Ejercicio 3 — Predicción con criterio. Si el experimento de Mercado hubiera corrido con solo 1,200 usuarios por variante (una décima parte), en vez de 12,000, manteniendo las mismas tasas de conversión (3.2% y 3.8%), ¿esperarías que el z-score fuera más grande, más chico, o igual? ¿Y el p-value?

Ver solución

El z-score sería más chico (más cerca de 0), y el p-value sería más grande (menos significativo). La razón: sePooled depende de 1/n1 + 1/n2 — con muestras diez veces más chicas, ese término crece, el error estándar crece, y dividir la misma diferencia (p2 - p1) entre un error estándar más grande da un z-score más chico. Un z-score más chico está más cerca del centro de la distribución normal, así que normalCdf se acerca más a 0.5 y el p-value (2 * (1 - normalCdf(|z|))) crece. Esta es la misma idea de la lección 2 —menos muestra, más ruido relativo— expresada ahora en la fórmula exacta del z-test; el módulo 7 la retoma para calcular, al revés, cuánta muestra hace falta para un lift dado.

Resumen y siguiente paso

En esta lección calculaste, por primera vez en el módulo, el z-score (2.5289) y el p-value (0.0114) del A/B test real de recommendations, usando la hipótesis nula formal —"control y variant vienen de la misma tasa verdadera"— y la proporción pooled para estimar el error estándar bajo esa hipótesis. Con alpha = 0.05 como umbral convencional, el resultado es estadísticamente significativo: 0.0114 < 0.05.

Antes de avanzar deberías poder: explicar, con la analogía del jurado, qué representa la hipótesis nula; calcular a mano la proporción pooled dados dos grupos; y decir con precisión qué significa un p-value de 0.0114, sin caer en la frase incorrecta de "probabilidad de que la hipótesis nula sea verdadera".

Esa frase incorrecta —y otras dos igual de comunes y igual de caras— son exactamente el tema de la lección 4: qué no significa el p-value, antes de seguir construyendo sobre él.

Recursos

  • Wikipedia, "p-value" — en.wikipedia.org/wiki/P-value. Define el p-value exactamente como esta lección lo usó: "la probabilidad de obtener resultados de prueba al menos tan extremos como el resultado observado, bajo el supuesto de que la hipótesis nula es correcta". En inglés.
  • Wikipedia, "Z-test" — en.wikipedia.org/wiki/Z-test, sección "Comparing the proportions of two binomials". La fórmula exacta del z-test de dos proporciones que implementa el código de esta lección, con la misma estructura de error estándar pooled. En inglés.
  • Ron Kohavi, Diane Tang y Ya Xu, Trustworthy Online Controlled Experimentsexperimentguide.com. El capítulo sobre pruebas de hipótesis desarrolla con más profundidad la lógica de "asumir H0, medir qué tan sorprendente es el dato" que esta lección presentó con la analogía del jurado. En inglés.