Capítulo 12 de 23 13 secciones 20 min

Compartir

La prueba de hipótesis y el valor p

Calculado a mano barajando etiquetas, para que veas exactamente qué es antes de que ninguna fórmula te lo esconda.

Una prueba de hipótesis supone que no pasa nada y calcula qué tan raro sería lo que viste bajo esa suposición. Ese "qué tan raro" es el valor p. Si es menor que 0,05 se considera que lo que viste no encaja con la suposición. Y lo que el valor p no dice, que es lo que a mí me costó entender: ni la probabilidad de que tu hipótesis sea cierta, ni el tamaño del efecto, ni si el hallazgo importa.

Llegamos al número más famoso y peor entendido de la estadística: el valor p 🏆

Y te lo voy a enseñar de una forma que casi nadie usa: calculándolo a mano, barajando cartas. Cuando lo veas así, ya no se te va a olvidar nunca, y las fórmulas de después van a ser un atajo y no una caja negra.

Y contéstate una, aunque sea a ojo: ¿qué crees que significa exactamente "p menor que 0,05"? Guarda tu respuesta y compárala al final. Casi nadie acierta la primera vez, y yo tampoco acerté 🎓

La pregunta, en su forma más honesta

En el capítulo 10 encontramos esto:

  • Horeca cierra el 63
  • 21% de sus ventas y Minimarket el 56
  • 93%

Una diferencia de 6,28 puntos.

La pregunta es la de siempre: ¿eso es real o es ruido?

Y la forma de contestarla es preciosa. Consiste en suponer que no hay ninguna diferencia y ver qué tan raro sería, en ese mundo, encontrar los 6,28 puntos que encontramos.

A esa suposición se le llama hipótesis nula. Es el abogado del diablo: parte de que no pasa nada, y le toca a tus datos convencerla de lo contrario 👩‍⚖️

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

URL = 'https://missyera.com/static/datasets/ventas-miss-yera.csv'


def carga_limpia(url):
    """La misma del capítulo 2."""
    v = pd.read_csv(url).drop_duplicates()
    v['ciudad'] = (v['ciudad'].str.strip().str.lower()
                   .str.normalize('NFKD')
                   .str.encode('ascii', 'ignore').str.decode('utf-8'))
    v['monto'] = pd.to_numeric(v['monto'].str.replace(',', '.'))
    for col in ['fecha', 'fecha_ultima_compra']:
        f = pd.to_datetime(v[col], format='%Y-%m-%d', errors='coerce')
        falta = f.isna() & v[col].notna()
        f[falta] = pd.to_datetime(v.loc[falta, col], format='%d/%m/%Y',
                                  errors='coerce')
        v[col] = f
    return v


v = carga_limpia(URL)

horeca = v.loc[v['segmento'] == 'Horeca', 'compro'].values
minimarket = v.loc[v['segmento'] == 'Minimarket', 'compro'].values
observada = horeca.mean() - minimarket.mean()

print('Horeca:     %.4f (%d ventas)' % (horeca.mean(), len(horeca)))
print('Minimarket: %.4f (%d ventas)' % (minimarket.mean(), len(minimarket)))
print('diferencia: %.6f' % observada)
Horeca:     0.6321 (742 ventas)
Minimarket: 0.5693 (801 ventas)
diferencia: 0.062787
Histograma de las 10.000 diferencias que salen al barajar las etiquetas de los dos segmentos, con las colas resaltadas y una línea vertical en la diferencia observada de 0,0628, que cae dentro de la cola derecha.
Eso resaltado en los extremos es el valor p: la proporción de mundos sin efecto que producen una diferencia tan grande como la observada. Aquí son 155 de 10.000, o sea 0,0155.

Barajando las etiquetas

Aquí viene lo bonito 🃏

Si la hipótesis nula fuera cierta, o sea si el segmento no influyera en nada, entonces las etiquetas "Horeca" y "Minimarket" estarían puestas al azar sobre esas 1.543 ventas.

Así que vamos a hacer justo eso: mezclar las etiquetas mil veces y ver qué diferencias salen en un mundo donde no pasa nada.

generador = np.random.default_rng(7)
todas = np.concatenate([horeca, minimarket])
n_horeca = len(horeca)

diferencias = []
for _ in range(10000):
    generador.shuffle(todas)
    diferencias.append(todas[:n_horeca].mean() - todas[n_horeca:].mean())

diferencias = np.array(diferencias)

print('diferencias al azar: media %+.6f | desviacion %.6f'
      % (diferencias.mean(), diferencias.std()))
print('la mayor que salio por azar: %.6f' % np.abs(diferencias).max())
print('la nuestra:                  %.6f' % abs(observada))
diferencias al azar: media +0.000433 | desviacion 0.025086
la mayor que salio por azar: 0.092982
la nuestra:                  0.062787

Fíjate en lo que acabamos de fabricar: el mundo donde no pasa nada. En ese mundo las diferencias entre los dos grupos se mueven alrededor de cero con una desviación de 0,025 🌍

Y ahora el valor p, que es literalmente contar:

p = np.mean(np.abs(diferencias) >= abs(observada))

print('de 10000 mundos sin efecto, cuantos dieron una diferencia asi de grande o mas:')
print('   %d' % (np.abs(diferencias) >= abs(observada)).sum())
print('valor p = %.4f' % p)
de 10000 mundos sin efecto, cuantos dieron una diferencia asi de grande o mas:
   155
valor p = 0.0155

Ese es el valor p. Y ya está. 🎉

Se lee así: si el segmento no influyera en nada, solo el 1,55% de las veces vería una diferencia tan grande como la que vi.

No hay más misterio. Un valor p es la proporción de mundos-sin-efecto que producen algo tan llamativo como lo tuyo.

Y ahora la fórmula, que es el atajo

Lo que acabamos de hacer se llama prueba de permutación y tarda unos segundos. La prueba t hace lo mismo con una fórmula, en una línea:

t=x¯1x¯2s12n1+s22n2

la diferencia entre los dos grupos medida en errores estándar, o sea cuántas veces cabe el ruido dentro de lo que encontraste

resultado = stats.ttest_ind(horeca, minimarket, equal_var=False)

print('permutacion (barajando): p = %.4f' % p)
print('prueba t (formula):      p = %.4f' % resultado.pvalue)
permutacion (barajando): p = 0.0155
prueba t (formula):      p = 0.0118

0,0155 y 0,0118. Prácticamente lo mismo 🤝

La fórmula supone que las diferencias al azar siguen una campana, y por eso va más rápido. La permutación no supone nada: se fabrica el mundo sin efecto de verdad. Cuando las dos coinciden, como aquí, es que la suposición de la fórmula era razonable.

Yo uso la fórmula por comodidad y la permutación cuando el resultado es importante o los datos son raros. Y las dos veces sé lo que estoy calculando, que es de lo que iba este capítulo 😌

El contraste: cuando de verdad no hay nada

Hagamos lo mismo con dos ciudades, que llevamos nueve capítulos viendo que no separan nada:

trujillo = v.loc[v['ciudad'] == 'trujillo', 'compro'].values
piura = v.loc[v['ciudad'] == 'piura', 'compro'].values

print('trujillo %.4f | piura %.4f | diferencia %.4f'
      % (trujillo.mean(), piura.mean(), trujillo.mean() - piura.mean()))
print('prueba t: p = %.4f'
      % stats.ttest_ind(trujillo, piura, equal_var=False).pvalue)
trujillo 0.5924 | piura 0.5692 | diferencia 0.0232
prueba t: p = 0.4634

p = 0,4634. O sea que casi la mitad de los mundos sin efecto producirían una diferencia así 🤷

Y ojo con cómo se dice esto, porque es donde se resbala todo el mundo. No se dice "queda demostrado que las ciudades cierran igual". Se dice "no tengo evidencia de que sean distintas".

La diferencia importa: puede que Trujillo cierre mejor de verdad y que 471 ventas no alcancen para verlo. Ausencia de evidencia no es evidencia de ausencia 🕵️

Las dos distribuciones, la de la hipótesis nula y la del efecto real, solapadas, con la línea del valor crítico entre ellas. A la derecha de la línea bajo la nula está alfa, a la izquierda bajo la alternativa está beta, y el resto es la potencia.
Aquí se ve por qué no se pueden bajar los dos errores a la vez: mueve la línea a la derecha y el área magenta de falsos positivos encoge, pero la gris de falsos negativos crece. Es la misma imagen del umbral de un modelo, con otros nombres.

El 0,05, que no tiene nada de mágico

Se corta en 0,05 por costumbre. Lo propuso Ronald Fisher en los años veinte como una guía razonable y se quedó para siempre.

Lo que sí es concreto es qué te cuesta ese corte. Vamos a verlo con datos donde sabemos que no hay ningún efecto porque los fabrico yo:

falsos = 0
for _ in range(1000):
    a = generador.normal(size=50)
    b = generador.normal(size=50)
    if stats.ttest_ind(a, b).pvalue < 0.05:
        falsos += 1

print('comparaciones sin ningun efecto: 1000')
print('salieron significativas:         %d' % falsos)
print('o sea el %.1f%%' % (falsos / 10))
comparaciones sin ningun efecto: 1000
salieron significativas:         57
o sea el 5.7%

El 5,7%, o sea el 5% prometido 🎯

Eso es lo que significa alfa = 0,05: estás aceptando equivocarte una de cada veinte veces cuando no hay nada. No es un defecto, es el precio que eliges pagar.

Y de ahí sale la tabla de los dos errores, que conviene tener clara:

No hay efectoSí hay efecto
Dices que sí hayError tipo I (falsa alarma), pasa el 5%Correcto 🎉
Dices que no hayCorrectoError tipo II (te lo perdiste)

Bajar alfa a 0,01 reduce las falsas alarmas y aumenta las veces que te pierdes algo real. No hay forma de reducir los dos a la vez sin conseguir más datos. Eso es el capítulo 14 💪

Las cuatro cosas que un valor p NO dice

Esta lista vale el capítulo entero. Cada una la he oído en una reunión 🙃

  • "p = 0,0155 quiere decir que hay 98,45% de probabilidad de que la diferencia sea real." No. El p se calcula suponiendo que no hay diferencia; no puede decirte la probabilidad de que la haya.
  • "p = 0,0155 quiere decir que hay 1,55% de probabilidad de que sea casualidad." Tampoco, y es la más común. Es la probabilidad de ver estos datos si fuera casualidad, que es al revés. Es el mismo error de dar la vuelta a una condicional del capítulo 7.
  • "p chiquito quiere decir efecto grande." No. El p mezcla tamaño del efecto y cantidad de datos. Con muchísimas filas, un efecto ridículo sale con p diminuto.
  • "p = 0,06 quiere decir que no hay nada." No. 0,049 y 0,051 son prácticamente el mismo resultado, y el corte en 0,05 es una convención.

Si te llevas una sola frase del capítulo, que sea esta: significativo no quiere decir importante 📌

El error del capítulo

Quiero comparar la satisfacción entre dos segmentos:

sa = v.loc[v['segmento'] == 'Horeca', 'satisfaccion']
sb = v.loc[v['segmento'] == 'Bodega', 'satisfaccion']

print(stats.ttest_ind(sa, sb))
TtestResult(statistic=np.float64(nan), pvalue=np.float64(nan), df=np.float64(nan))

nan por todas partes, sin error y sin aviso 🫥

La causa son los 231 nulos de satisfaccion. Cualquier cuenta que toque un nan devuelve nan, y como el resultado parece un resultado, se puede copiar a una diapositiva tal cual.

Un p de nan no es "no significativo": es "no calculé nada".

print(stats.ttest_ind(sa, sb, nan_policy='omit'))
TtestResult(statistic=np.float64(0.9507468810912626), pvalue=np.float64(0.341903692047498), df=np.float64(1345.0))

Ahora sí: p = 0,3419, o sea que la satisfacción no distingue esos dos segmentos. Que es lo que ya sospechábamos desde el capítulo 3.

Y el error que sí te frena, por si te lo preguntas:

stats.ttest_ind(sa.dropna(), sb.dropna(), alternative='mayor')
ValueError: `alternative` must be 'less', 'greater', or 'two-sided'.

Ese está en inglés y hay que escribirlo greater. Lo pongo porque la comparación es justa: el parámetro mal escrito te para, y los datos mal preparados no 😤

Y dos más, el primero de los que más se repiten

stats.ttest_ind(horeca, minimarket, alternative='mayor')
ValueError: `alternative` must be 'less', 'greater', or 'two-sided'.

Las opciones van en inglés y son esas tres. Y lo importante no es la palabra: es que elegir una cola en vez de dos es una decisión estadística, no de escritura. Con una cola encuentras significativo más fácil, así que se decide antes de mirar los datos o no vale.

stats.ttest_ind(horeca)
TypeError: ttest_ind() missing 1 required positional argument: 'b'

Una prueba de dos muestras necesita dos muestras. Sale cuando uno tiene los grupos en una tabla y se olvida de partirla, y el mensaje es de los claros: falta b.

Practica 💪

1. Prueba tú la diferencia grande

Compara Mayorista contra Bodega, que son los dos extremos. ¿Qué valor p sale?

may = v.loc[v['segmento'] == 'Mayorista', 'compro'].values
bod = v.loc[v['segmento'] == 'Bodega', 'compro'].values

r = stats.ttest_ind(may, bod, equal_var=False)
print('mayorista %.4f | bodega %.4f | diferencia %.4f'
      % (may.mean(), bod.mean(), may.mean() - bod.mean()))
print('t = %.4f' % r.statistic)
print('p = %.3e' % r.pvalue)
mayorista 0.7240 | bodega 0.3748 | diferencia 0.3492
t = 14.2705
p = 2.726e-43

p = 2,7 por 10 elevado a -43. O sea un cero, una coma y cuarenta y dos ceros más antes del primer dígito 😅

Cuando un valor p sale así de chico, ya no aporta nada leerlo como probabilidad. Lo único que dice es "esto no es casualidad, ni de lejos".

Y aquí es donde hay que cambiar de pregunta. Ya sabemos que la diferencia es real; lo que hay que reportar es cuánto: 34,92 puntos de diferencia en la tasa de cierre. Ese número sí sirve para decidir algo.

Reportar "p menor que 0,001" y quedarse ahí es de las cosas que más frenan una reunión 🛑

2. El mismo efecto con menos datos

Coge la diferencia de Horeca contra Minimarket, que salió con p = 0,0118, y repítela usando solo 100 ventas de cada uno.

for tamano in [100, 200, 400, 742]:
    a = generador.choice(horeca, min(tamano, len(horeca)), replace=False)
    b = generador.choice(minimarket, min(tamano, len(minimarket)), replace=False)
    r = stats.ttest_ind(a, b, equal_var=False)
    print('con %3d de cada uno -> diferencia %+.4f | p = %.4f'
          % (tamano, a.mean() - b.mean(), r.pvalue))
con 100 de cada uno -> diferencia +0.0000 | p = 1.0000
con 200 de cada uno -> diferencia +0.1050 | p = 0.0339
con 400 de cada uno -> diferencia +0.0650 | p = 0.0610
con 742 de cada uno -> diferencia +0.0580 | p = 0.0225

Mira qué inestable 😬

El mismo efecto que con toda la muestra daba 0,0118 aquí sale 1,0000, 0,0339, 0,0610 y 0,0225 según con cuántas filas y cuáles te toque.

Y fíjate en el detalle más incómodo, el primero: con 100 de cada uno la diferencia observada fue exactamente cero, y el p salió 1,0000. Con esa muestra habrías concluido que los dos segmentos cierran igual, y te habrías equivocado.

Mira también el tercero: 400 de cada uno, diferencia +0,0650 (más grande que la real) y p = 0,0610, o sea justo por encima del corte. Ahí habrías dicho "no hay nada" teniendo el efecto delante.

Esto es lo que hay detrás de la mitad de los estudios que no se replican. Con muestras chicas, el valor p es casi un sorteo 🎰

3. La prueba de una sola muestra

¿El monto promedio de este archivo es distinto de 800 soles? Es la otra prueba clásica: comparar contra un número fijo.

for referencia in [800, 750, 803]:
    r = stats.ttest_1samp(v['monto'], referencia)
    print('contra %d soles -> t = %+7.4f | p = %.4f' % (referencia, r.statistic, r.pvalue))
contra 800 soles -> t = +0.2740 | p = 0.7841
contra 750 soles -> t = +3.9158 | p = 0.0001
contra 803 soles -> t = +0.0554 | p = 0.9558

Contra 800 el p es 0,7841: no puedo afirmar que el promedio sea distinto de 800. Contra 750 el p es 0,0001: sí puedo 📊

Y date cuenta de lo que esto es en realidad: la misma información que el intervalo de confianza del capítulo 10, que iba de 776,84 a 830,68.

800 está dentro del intervalo, así que no se rechaza. 750 está fuera, así que se rechaza. Toda prueba de hipótesis se puede leer como "¿está este número dentro del intervalo?", y esa forma me gusta más porque de paso te dice el tamaño 💚

4. Cuando la fórmula y la permutación no coinciden

Compara el monto (no la tasa de compra) entre Horeca y Minimarket con las dos formas. Los montos tienen cola, a ver si aguanta.

ma = v.loc[v['segmento'] == 'Horeca', 'monto'].values
mb = v.loc[v['segmento'] == 'Minimarket', 'monto'].values
obs = ma.mean() - mb.mean()

juntos = np.concatenate([ma, mb])
difs = []
for _ in range(10000):
    generador.shuffle(juntos)
    difs.append(juntos[:len(ma)].mean() - juntos[len(ma):].mean())
difs = np.array(difs)

print('diferencia observada: %.2f soles' % obs)
print('permutacion: p = %.4f' % np.mean(np.abs(difs) >= abs(obs)))
print('prueba t:    p = %.3e' % stats.ttest_ind(ma, mb, equal_var=False).pvalue)
diferencia observada: 340.26 soles
permutacion: p = 0.0000
prueba t:    p = 2.535e-143

Las dos dicen lo mismo: imposible que sea casualidad 🎯

La permutación dice 0,0000 porque de 10.000 mundos sin efecto, ninguno produjo una diferencia de 340 soles. Ese es su límite: no puede darte un p más chico que 1 entre 10.000.

La fórmula sí puede, y dice 2,5 por 10 elevado a -143. Ese número no significa nada práctico, pero muestra bien la diferencia entre las dos herramientas: la permutación es honesta hasta donde llega, la fórmula extrapola.

Cuando necesites un p de verdad chiquito, la fórmula. Cuando necesites no suponer nada, barajar 🃏

5. Fabrica un falso positivo

Añade cinco columnas de basura al azar y prueba cada una contra compro. ¿Alguna sale significativa?

for i in range(5):
    basura = generador.normal(size=len(v))
    grupo_a = basura[v['compro'] == 1]
    grupo_b = basura[v['compro'] == 0]
    r = stats.ttest_ind(grupo_a, grupo_b)
    marca = '  <-- SIGNIFICATIVA' if r.pvalue < 0.05 else ''
    print('columna de basura %d -> p = %.4f%s' % (i + 1, r.pvalue, marca))
columna de basura 1 -> p = 0.0700
columna de basura 2 -> p = 0.5952
columna de basura 3 -> p = 0.9215
columna de basura 4 -> p = 0.1748
columna de basura 5 -> p = 0.1566

Esta vez ninguna, y tiene sentido: con cinco pruebas al 5%, la probabilidad de que salte alguna es del 22,6% 🎲

Prueba a subir el número a 50 y verás salir dos o tres. Son columnas de ruido puro, generadas por mí, sin ninguna relación posible con la compra. Y aun así algunas van a "salir significativas".

Eso es exactamente lo que pasa cuando alguien cruza treinta variables buscando "qué influye en las ventas". Encuentra cosas. Y algunas son esto 🗑️

Tiene nombre, se llama comparaciones múltiples, y tiene su sección en el capítulo 17.

6. La prueba que no supone nada de la forma

La prueba t compara medias y supone campana. Prueba la alternativa que compara posiciones sin suponer forma: Mann-Whitney.

print('comparando montos de Horeca contra Minimarket')
print('  medias:   %.2f contra %.2f' % (ma.mean(), mb.mean()))
print('  medianas: %.2f contra %.2f' % (np.median(ma), np.median(mb)))
print()
print('prueba t (medias):        p = %.3e'
      % stats.ttest_ind(ma, mb, equal_var=False).pvalue)
print('Mann-Whitney (posicion):  p = %.3e'
      % stats.mannwhitneyu(ma, mb).pvalue)
comparando montos de Horeca contra Minimarket
  medias:   755.18 contra 414.92
  medianas: 743.00 contra 413.74

prueba t (medias):        p = 2.535e-143
Mann-Whitney (posicion):  p = 1.022e-142

Las dos dicen que sí, y con una contundencia casi idéntica: 10 elevado a -143 contra 10 elevado a -142 💪

Lo que hace es olvidarse de los valores y quedarse con el orden: pone las 1.543 ventas en fila y mira si las de un grupo tienden a estar más arriba. Como no usa los montos, ningún atípico la despeina.

Mi criterio práctico, por si te sirve: si las dos coinciden, reporta la t que todo el mundo entiende. Si discrepan, quédate con Mann-Whitney y mira qué está haciendo la cola 👀

7. La misma diferencia con cuatro tamaños

Compara horeca contra minimarket con 20, 60, 200 y 600 por grupo, y mira cómo cambia el valor p.

ge = np.random.default_rng(1)
for n in [20, 60, 200, 600]:
    a2 = ge.choice(horeca, n, replace=False)
    b2 = ge.choice(minimarket, n, replace=False)
    print('con %3d por grupo: diferencia %8.2f   p = %.4f'
          % (n, a2.mean() - b2.mean(), stats.ttest_ind(a2, b2).pvalue))
con  20 por grupo: diferencia     0.10   p = 0.5393
con  60 por grupo: diferencia     0.02   p = 0.8556
con 200 por grupo: diferencia     0.07   p = 0.1808
con 600 por grupo: diferencia     0.09   p = 0.0014

La diferencia se queda en 0,1 y el valor p se desploma 😬

Mira las dos columnas juntas: el efecto es prácticamente el mismo en las cuatro filas, y el p pasa de 0,54 a 0,0014. Lo único que cambió fue cuánta gente miramos.

El valor p mide evidencia, no tamaño. Con muestras grandes, diferencias que no le importan a nadie salen significativas, y por eso el capítulo 14 existe 📉

8. Barajando etiquetas quinientas veces

Mezcla los dos grupos al azar quinientas veces y cuenta cuántas dan un valor p por debajo de 0,05.

g3 = np.random.default_rng(5)
todos = np.concatenate([horeca, minimarket])
falsos = 0
for _ in range(500):
    mezcla = g3.permutation(todos)
    corte = len(horeca)
    falsos += stats.ttest_ind(mezcla[:corte], mezcla[corte:]).pvalue < 0.05
print('de 500 particiones al azar, salen significativas: %d' % falsos)
print('o sea el %.1f%%, y lo esperado es el 5%%' % (falsos / 500 * 100))
de 500 particiones al azar, salen significativas: 15
o sea el 3.0%, y lo esperado es el 5%

El 3,0% donde se esperaba el 5% 💚

Aquí no hay ninguna diferencia real: las etiquetas están barajadas, así que los dos grupos son el mismo grupo partido al azar. Y aun así quince de las quinientas salen significativas. Eso es lo que promete el 0,05: equivocarse cinco veces de cada cien cuando no hay nada.

Que salga 3,0 y no 5,0 es el azar de hacer solo 500 pruebas. Corre la celda con otra semilla y vas a ver el número bailar alrededor del cinco, que es justo la lección 🎲

La misma diferencia con dos conclusiones opuestas

Y ahora la trampa del capítulo, que es la que más decisiones malas ha producido de todo el libro 🔬

La trampa

Comparas dos canales, sale p mayor que 0,05, y escribes en el informe que no hay diferencia entre ellos.

# los dos canales enteros
# WhatsApp 0.655  vs  Marketplace 0.461
# n = 719 y 763      p = 9.4e-14

# la MISMA comparación con 60 y 60
# 0.650  vs  0.483
# p = 0.0973
Qué está mal

La misma diferencia de verdad, y dos conclusiones opuestas 🔬 Lo único que cambió fue cuántas filas miré.

Un p alto no dice "son iguales". Dice "con estos datos no se ve", que es otra cosa completamente distinta. Con 60 filas por grupo, una diferencia de 17 puntos, que es enorme, se queda sin fuerza para demostrarse.

Y esto se convierte en decisiones malas todo el tiempo: se prueba algo con poca gente, sale p alto, y se archiva como que no funciona. No se demostró que no funcione. No se demostró nada.

Cuando te salga un p alto, mira dos cosas antes de escribir la conclusión: el tamaño de la diferencia y su intervalo de confianza. Si el intervalo va de -2% a +20%, lo honesto es decir que no sabes, no que da igual.

Comprueba que lo tienes

Un valor p de 0,0155, ¿qué significa exactamente?

  • Que si no hubiera efecto, el 1,55% de las veces vería algo así de grande
  • Que hay un 1,55% de probabilidad de que sea casualidad
  • Que hay un 98,45% de probabilidad de que la diferencia sea real
  • Que el efecto es pequeño

Lo que te llevas

  • 🃏 Un valor p es contar: de 10.000 mundos sin efecto, ¿cuántos producen algo tan grande como lo tuyo? Aquí 155, o sea p = 0,0155.
  • ⚡ La fórmula es el atajo de eso. La permutación dio 0,0155 y la prueba t 0,0118.
  • 🚫 Un p alto no demuestra que no haya diferencia. Ausencia de evidencia no es evidencia de ausencia.
  • 🎯 Alfa = 0,05 significa equivocarte 1 de cada 20 veces cuando no hay nada. Con 1.000 comparaciones vacías salieron 57 significativas.
  • ❌ El p no es la probabilidad de que sea casualidad, ni el tamaño del efecto, ni una medida de importancia.
  • 🎰 Con muestras chicas el p es un sorteo: el mismo efecto dio 0,2000, 0,3626, 0,0392 y 0,1263 según qué 100 filas tocaran.
  • 👻 Un p de nan no es "no significativo", es "no calculé nada". Y llega sin avisar en cuanto hay nulos.

Y si de todo el capítulo te llevas una sola frase, que sea esta:

El valor p mide evidencia, no importancia. Y no dice nada de la probabilidad de que tengas razón.

Y sacar los dos grupos que vas a comparar casi nunca es un filtro de pandas en la vida real: es una consulta contra la base. Eso está en el libro de SQL 🗃️

Qué viene ahora

Ya sabes qué es un valor p. En el capítulo 13 vemos las cuatro pruebas concretas que cubren el 90% de los casos reales, y sobre todo cómo elegir cuál toca, que es donde se atasca todo el mundo 🧰

Preguntas frecuentes

¿Qué es la hipótesis nula?

La afirmación aburrida: que no pasa nada, que las dos cosas son iguales, que la diferencia es azar. La prueba se monta para ver si hay evidencia suficiente para descartarla.

¿Qué es el valor p?

La probabilidad de ver una diferencia como la tuya, o mayor, si la hipótesis nula fuera cierta. No es la probabilidad de que tu hipótesis sea verdad, y esa confusión es la más común de la estadística.

¿Qué significa que el valor p sea menor a 0,05?

Que un resultado así sería raro si no pasara nada, así que se descarta la hipótesis nula. El 0,05 es una convención y no una ley de la naturaleza.

¿Qué es el error tipo I y el tipo II?

El tipo I es decir que hay diferencia cuando no la hay. El tipo II es no verla cuando sí la hay. Bajar uno sube el otro, y elegir cuál te duele más es una decisión de negocio.

Practica este capítulo 📓

Todo el código de arriba en un cuaderno que corre de principio a fin, y los ejercicios con una celda vacía para que los hagas tú. Se abre en Google Colab de un clic y no hay que instalar nada. Donde veas %%revisa, escribe tu respuesta y el cuaderno te dice si te salió.

¿Prefieres trabajar en tu máquina? Bájate el cuaderno de práctica o el de soluciones. Todos están también en github.com/soymissyera/MissYeraEjercicios.

¿Le sirve a alguien que conoces?

Pásale el libro. Es gratis, está entero y no pide registro 🐣

Instagram y TikTok no dejan compartir enlaces desde la web: esos dos copian la URL para que la pegues en tu historia.

¿Tienes alguna duda o consulta?