Ya tienes el titular y ya elegiste qué mostrar. Ahora te van a preguntar.
Y la buena noticia es que las preguntas son casi siempre las mismas seis. La mala es que cada una necesita una cuenta que no está en tu cuaderno todavía 🌸
import pandas as pd import numpy as np from math import ceil URL = 'https://missyera.com/static/datasets/ventas-miss-yera.csv' df = pd.read_csv(URL) df['monto'] = pd.to_numeric(df['monto'], errors='coerce') d = df[['canal', 'compro', 'segmento', 'ciudad', 'monto']].dropna( subset=['canal', 'compro']) sub = d[d['canal'].isin(['WhatsApp', 'Web'])] w = sub[sub['canal'] == 'WhatsApp']['compro'] b = sub[sub['canal'] == 'Web']['compro'] dif = float(w.mean() - b.mean()) se = float(np.sqrt(w.var(ddof=1) / len(w) + b.var(ddof=1) / len(b))) print('diferencia:', round(dif, 4), '| error estandar:', round(se, 4))
diferencia: 0.0694 | error estandar: 0.0248
1. "¿Y qué tan seguro estás?"
print('entre', round((dif - 1.96 * se) * 100, 1), 'y', round((dif + 1.96 * se) * 100, 1), 'puntos')
entre 2.1 y 11.8 puntos
Es la pregunta más frecuente y la más fácil, porque ya la contestaste en el capítulo 10.
Lo que hay que resistir es la tentación de contestar "el p-valor es 0,0053". Nadie sabe qué hacer con eso. "Entre 2 y 12 puntos" lo entiende todo el mundo y dice exactamente lo mismo, con la ventaja de que trae el tamaño dentro 🧾
2. "¿Y si no hacemos nada?"
print('la web ya convierte:', round(float(b.mean()), 4)) print('con 1.000 clientes ya compran', round(float(b.mean()) * 1000))
la web ya convierte: 0.583 con 1.000 clientes ya compran 583
Esta es la que más análisis hunde, y casi nadie la lleva preparada.
La web ya convierte 583 de cada 1.000. Tu propuesta no trae 652 ventas nuevas: trae 69 más de las que ya había. Sin la línea base al lado, el número grande parece todo el mérito del proyecto.
Se llama línea base y es la respuesta a "¿comparado con qué?". Cuando alguien te presente una mejora sin decirte la suya, esa es la pregunta que hay que hacer.
3. "¿Cuánta plata es?"
ticket = float(d['monto'].dropna().mean()) print('centro :', round(dif * 1000 * ticket), 'soles') print('borde bajo:', round((dif - 1.96 * se) * 1000 * ticket), 'soles')
centro : 56086 soles borde bajo: 16733 soles
Los dos números, siempre. Si llevas solo el 56.086, alguien va a poner eso en un presupuesto y tú vas a ser la responsable de que no llegue.
Y la frase que hay que decir en voz alta: "esto asume 1.000 clientes y el ticket medio actual, y las dos cosas pueden cambiar".
4. "¿Funciona para todos?"
for seg in sorted(sub['segmento'].dropna().unique()): s = sub[sub['segmento'] == seg] a = s[s['canal'] == 'WhatsApp']['compro'] c = s[s['canal'] == 'Web']['compro'] print(seg.ljust(11), 'dif', round(float(a.mean() - c.mean()), 3))
Bodega dif 0.063 Horeca dif 0.016 Mayorista dif 0.075 Minimarket dif 0.123
No. Horeca casi no se mueve, y eso es lo que trabajamos en el capítulo 20.
Si esta pregunta te la hacen y no la tienes hecha, la reunión se acaba ahí: "vuelve cuando lo hayas abierto".
5. "¿Te alcanzó la muestra?"
Esta la hacen poco y es la que más duele cuando llega:
def muestra_necesaria(p1, p2): pb = (p1 + p2) / 2 return ceil((1.96 * np.sqrt(2 * pb * (1 - pb)) + 0.84 * np.sqrt(p1 * (1 - p1) + p2 * (1 - p2))) ** 2 / (p1 - p2) ** 2) n = muestra_necesaria(float(w.mean()), float(b.mean())) print('hacian falta por grupo:', n) print('tengo :', len(w), 'y', len(b))
hacian falta por grupo: 768 tengo : 725 y 801
768 hacían falta, y en WhatsApp tengo 725. Me quedé corta en uno de los dos grupos.
Esto no invalida el análisis: la diferencia salió significativa igual. Lo que dice es que estaba justo en el límite, y que si el efecto hubiera sido un poco más chico, no lo habría visto. Es exactamente la advertencia del capítulo 18, y decirla tú antes de que la pregunten vale mucho.
6. "¿Esto es causa o solo pasa que...?"
La pregunta difícil, y la única que no se contesta con una cuenta.
mezcla = sub.groupby('canal')['segmento'].value_counts(normalize=True).round(3) print(mezcla.to_string())
canal segmento
Web Minimarket 0.267
Mayorista 0.250
Bodega 0.247
Horeca 0.236
WhatsApp Horeca 0.261
Minimarket 0.257
Bodega 0.244
Mayorista 0.239
Los dos canales tienen prácticamente la misma mezcla de segmentos, entre 24% y 27% cada uno. O sea que el segmento no explica la diferencia.
Y aun así la respuesta correcta es: no lo sé. Porque el canal lo eligió el cliente, no yo. Puede que quien usa WhatsApp ya venga con más ganas de comprar, y eso no lo mide ninguna de mis columnas.
Lo que sí puedo decir es qué haría falta para saberlo: repartir clientes al azar entre los dos canales, que es la prueba A/B del capítulo 18. Sin eso, es correlación, y llamarla causa es lo que trabajamos en el capítulo 15.
Y cuando el resultado no es el que querían
Pasa, y es el momento donde más se nota quién sabe hacer esto.
Lo que no funciona: suavizarlo, buscar el corte que sí dio, o decir "hay una tendencia" cuando el intervalo cruza el cero.
Lo que funciona son tres frases, en este orden:
- El resultado, sin adornos. "No encontramos diferencia."
- Qué descarta. "Con esta muestra podríamos haber visto diferencias de 7 puntos o más, así que si existe algo, es menor que eso." Eso es información y vale dinero.
- Qué harías ahora. "Con este presupuesto yo probaría otra cosa antes que insistir acá."
Un no medido es un resultado. Un no disfrazado de sí es un problema que aparece seis meses después, cuando ya nadie se acuerda de tu lámina 💛
Lo que te llevas
- Las seis preguntas son casi siempre las mismas: certeza, línea base, dinero, segmentos, muestra y causa.
- El intervalo se dice en puntos, no en p-valor.
- Sin línea base, cualquier mejora parece más grande de lo que es.
- El dinero va con su borde bajo y con sus supuestos dichos.
- La muestra necesaria se calcula, y decir que te quedaste corta te da crédito.
- La causa la da el diseño, no la prueba.
- Un resultado negativo se presenta con lo que descarta y con qué harías ahora.
Comprueba que lo tienes
Presentas que WhatsApp sube 6,9 puntos y alguien pregunta "¿y eso es causa o solo pasa que los mejores clientes usan WhatsApp?". ¿Qué contestas?
- Que el canal lo eligió el cliente, así que es correlación, y qué haría falta para saberlo
- Que el valor p de 0,0053 demuestra que es causa
- Que sí, porque comprobé que los segmentos están parejos
- Que con más datos se sabría
Ejercicios
1. Cuánta muestra para ver diferencias más chicas
La cuenta que hay que hacer antes de empezar, no después.
base = float(b.mean()) for efecto in (0.07, 0.05, 0.03, 0.01): n = muestra_necesaria(base + efecto, base) print('para ver', round(efecto * 100, 1), 'puntos hacen falta', n, 'por grupo')
para ver 7.0 puntos hacen falta 755 por grupo para ver 5.0 puntos hacen falta 1494 por grupo para ver 3.0 puntos hacen falta 4187 por grupo para ver 1.0 puntos hacen falta 37984 por grupo
Para ver un punto hacen falta casi 38.000 por grupo. Cuando alguien te pida "medir si el cambio del botón mejora algo", esta tabla es la que te dice si es posible con el tráfico que tienen. Casi siempre no lo es, y saberlo antes ahorra un trimestre.
2. El error de pedir certeza sobre nada
Pregúntale a la fórmula cuánta muestra hace falta para detectar una diferencia de cero.
muestra_necesaria(0.60, 0.60)
OverflowError: cannot convert float infinity to integer
Infinita. Y no es un capricho de la fórmula: no existe una muestra que te permita demostrar que dos cosas son exactamente iguales.
Por eso "no encontramos diferencia" nunca quiere decir "son iguales". Lo que se puede decir es lo del punto 2 de arriba: cuál es la diferencia más chica que habrías podido ver.
3. Qué descarta tu no
Convierte un resultado negativo en información.
n_real = min(len(w), len(b)) for efecto in (0.10, 0.07, 0.05, 0.03): hace_falta = muestra_necesaria(float(b.mean()) + efecto, float(b.mean())) print('efecto de', round(efecto * 100), 'puntos ->', 'lo habria visto' if hace_falta <= n_real else 'no me alcanzaba')
efecto de 10 puntos -> lo habria visto efecto de 7 puntos -> no me alcanzaba efecto de 5 puntos -> no me alcanzaba efecto de 3 puntos -> no me alcanzaba
Esa es la frase entera: "si hubiera un efecto de 10 puntos o más lo habría visto; de 7 para abajo, con esta muestra no puedo decir nada".
Y fíjate en lo justo que quedó: 7 puntos necesitaban 755 por grupo y tengo 725. Por treinta filas. El efecto que encontré, 6,9 puntos, está exactamente en esa zona donde la muestra no daba garantías, y salió igual. Tuve suerte, y eso también se dice.
Convertir un no en un límite superior es lo que hace que un resultado negativo sirva para decidir en vez de tirarse a la basura.
4. La línea base que nadie te dio
Compara la mejora contra lo que ya pasaba, no contra cero.
base = float(b.mean()) print('sin hacer nada, de cada 1000:', round(base * 1000)) print('con el cambio, de cada 1000 :', round(float(w.mean()) * 1000)) print('ventas nuevas :', round(dif * 1000)) print('la mejora es el', round(dif / float(w.mean()) * 100, 1), '% de lo que se atribuye el proyecto')
sin hacer nada, de cada 1000: 583 con el cambio, de cada 1000 : 652 ventas nuevas : 69 la mejora es el 10.6 % de lo que se atribuye el proyecto
De las 652 ventas que el proyecto va a poder mostrar como suyas, 583 iban a pasar igual. Solo el 10,6% es mérito del cambio. Esa cuenta es la que evita que un proyecto se cuelgue medallas ajenas, y sirve para leer las de otros.
5. Comprueba si un factor te está confundiendo
El chequeo del punto 6, hecho función.
def mezcla_pareja(datos, factor, grupo='canal', tolerancia=0.05): t = datos.groupby(grupo)[factor].value_counts(normalize=True).unstack() brecha = float((t.max() - t.min()).max()) return round(brecha, 4), brecha < tolerancia print('segmento:', mezcla_pareja(sub, 'segmento')) print('ciudad :', mezcla_pareja(sub, 'ciudad'))
segmento: (0.0247, True) ciudad : (0.0129, True)
Las dos brechas son menores de 5 puntos, así que ni el segmento ni la ciudad están repartidos de forma que expliquen la diferencia. Es una buena señal y no es una prueba de causa: solo descarta dos sospechosos de los que sí mediste.
6. La hoja de respuestas, generada
Las seis, calculadas de una vez, para llevar impresas.
ticket = float(d['monto'].dropna().mean()) respuestas = [ ('certeza', 'entre ' + str(round((dif - 1.96 * se) * 100, 1)) + ' y ' + str(round((dif + 1.96 * se) * 100, 1)) + ' puntos'), ('linea base', str(round(float(b.mean()) * 1000)) + ' de cada 1000 ya compran'), ('dinero', str(round((dif - 1.96 * se) * 1000 * ticket)) + ' a ' + str(round((dif + 1.96 * se) * 1000 * ticket)) + ' soles'), ('segmentos', 'Horeca casi no se mueve'), ('muestra', 'hacian falta 768 por grupo, tengo ' + str(len(w))), ('causa', 'el canal lo eligio el cliente, es correlacion'), ] for pregunta, respuesta in respuestas: print(pregunta.ljust(11), respuesta)
certeza entre 2.1 y 11.8 puntos linea base 583 de cada 1000 ya compran dinero 16733 a 95439 soles segmentos Horeca casi no se mueve muestra hacian falta 768 por grupo, tengo 725 causa el canal lo eligio el cliente, es correlacion
Seis líneas. Eso es lo que llevo a una reunión debajo de la lámina, y en el 90% de los casos no necesito ninguna otra cosa.
La costumbre de generarlas con código y no de memoria tiene un motivo concreto: cuando alguien pregunte y tú contestes con el número exacto sin dudar, la conversación cambia de sitio 💛
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.