Último capítulo con datos 🎓 Vamos a hacer un análisis entero, de la pregunta al informe, usando todo lo del libro.
Y voy a hacerlo en el orden correcto, que es el que casi nunca se sigue: la pregunta y el criterio primero, los datos después.
Y antes de tocar los datos, contéstate lo mismo que me contesto yo: ¿qué decisión se va a tomar con este análisis? Si no hay una decisión detrás, lo que te están pidiendo no es un análisis, es un reporte 🎓
Paso 1: la pregunta y el criterio, antes de mirar nada
La pregunta. Marketplace es el canal que peor cierra y WhatsApp el que mejor. ¿Vale la pena empujar a los clientes de Marketplace hacia WhatsApp?
El criterio, escrito antes. Me lo tomo en serio si:
- 📏 La diferencia es de al menos 5 puntos de tasa de cierre. Menos de eso no paga el esfuerzo de cambiar un proceso.
- 🎯 El intervalo del 95% no toca el cero.
- 🧩 El efecto aguanta dentro de cada segmento. Si solo existe en el total, es Simpson y no me sirve.
Escribir esto antes es lo que impide el p-hacking del capítulo 17. Si el resultado sale a 4 puntos, ya no puedo decir "bueno, 4 también está bien" 🙅
Paso 2: cargar y limpiar
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) print('ventas: %d | clientes: %d | periodo: %s a %s' % (len(v), v['cliente_id'].nunique(), v['fecha'].min().date(), v['fecha'].max().date()))
ventas: 3000 | clientes: 617 | periodo: 2025-01-01 a 2026-06-24
Y aquí ya anoto una limitación para el informe: 3.000 ventas de 617 clientes, o sea que las filas no son independientes (capítulo 17).
Paso 3: describir antes de concluir
wa = v[v['canal'] == 'WhatsApp'] mp = v[v['canal'] == 'Marketplace'] for nombre, g in [('WhatsApp', wa), ('Marketplace', mp)]: print('%-12s n=%3d | cierre %.4f | monto medio %.2f | mediana %.2f' % (nombre, len(g), g['compro'].mean(), g['monto'].mean(), g['monto'].median()))
WhatsApp n=719 | cierre 0.6551 | monto medio 789.14 | mediana 521.76 Marketplace n=763 | cierre 0.4613 | monto medio 812.27 | mediana 551.29
Primer dato importante y que hay que decir en voz alta: el monto es prácticamente igual en los dos canales (789 contra 812, y las medianas también).
Eso simplifica muchísimo el problema. Solo cambia la tasa de cierre, no el tamaño de la venta, así que todo el efecto se puede traducir a "cuántas ventas más se cierran" 💡
Paso 4: la comparación con su intervalo
dif = wa['compro'].mean() - mp['compro'].mean() ee = np.sqrt(wa['compro'].var(ddof=1) / len(wa) + mp['compro'].var(ddof=1) / len(mp)) print('diferencia: %.4f (%.2f puntos)' % (dif, 100 * dif)) print('intervalo del 95%%: [%.4f, %.4f]' % (dif - 1.96 * ee, dif + 1.96 * ee)) print('valor p: %.3g' % stats.ttest_ind(wa['compro'], mp['compro'], equal_var=False).pvalue)
diferencia: 0.1937 (19.37 puntos) intervalo del 95%: [0.1441, 0.2434] valor p: 3.51e-14
19,37 puntos, con un intervalo que va de 14,41 a 24,34 y no se acerca al cero ✅
Los dos primeros criterios se cumplen con muchísimo margen: pedía 5 puntos y hay 19, y el peor caso del intervalo sigue siendo 14 puntos.
Paso 5: el tamaño del efecto
def d_de_cohen(a, b): na, nb = len(a), len(b) juntas = np.sqrt(((na - 1) * a.var(ddof=1) + (nb - 1) * b.var(ddof=1)) / (na + nb - 2)) return (a.mean() - b.mean()) / juntas print('d de Cohen: %.4f' % d_de_cohen(wa['compro'], mp['compro']))
d de Cohen: 0.3972
0,3972, o sea chico tirando a mediano según los cortes del capítulo 14.
Y esto conviene contarlo con cuidado, porque puede sonar a contradicción: 19 puntos de diferencia y un efecto "chico". Las dos cosas son ciertas. El efecto es chico en desviaciones porque una variable 0/1 tiene una dispersión enorme; y 19 puntos son 19 puntos de negocio 💰
Por eso hay que reportar los dos números, y por eso el que va primero en el informe es el de puntos.
Paso 6: el paso que casi nadie da
Antes de creerme nada, el control de Simpson del capítulo 17. ¿Aguanta el efecto dentro de cada segmento?
for seg in sorted(v['segmento'].unique()): a = wa.loc[wa['segmento'] == seg, 'compro'] b = mp.loc[mp['segmento'] == seg, 'compro'] d = a.mean() - b.mean() e = np.sqrt(a.var(ddof=1) / len(a) + b.var(ddof=1) / len(b)) print('%-11s WhatsApp %.4f (n=%3d) | Marketplace %.4f (n=%3d) | ' 'dif %+.4f IC [%+.4f, %+.4f]' % (seg, a.mean(), len(a), b.mean(), len(b), d, d - 1.96 * e, d + 1.96 * e))
Bodega WhatsApp 0.4545 (n=176) | Marketplace 0.2762 (n=181) | dif +0.1783 IC [+0.0798, +0.2768] Horeca WhatsApp 0.6578 (n=187) | Marketplace 0.5050 (n=202) | dif +0.1528 IC [+0.0557, +0.2499] Mayorista WhatsApp 0.8187 (n=171) | Marketplace 0.6368 (n=190) | dif +0.1819 IC [+0.0921, +0.2716] Minimarket WhatsApp 0.6919 (n=185) | Marketplace 0.4158 (n=190) | dif +0.2761 IC [+0.1792, +0.3730]
Aguanta en los cuatro 🎉
Las diferencias van de +15,28 a +27,61 puntos, y ninguno de los cuatro intervalos toca el cero. Esto no es Simpson: el efecto está dentro de cada grupo, no lo fabrica la mezcla.
Tercer criterio cumplido. Y fíjate en que este paso podría haber tumbado el análisis entero después de cinco pasos de trabajo, que es exactamente por qué se hace 🛡️
Paso 7: traducirlo a dinero
tickets = v.loc[v['compro'] == 1, 'monto_final_facturado'] ticket = tickets.median() cierres_extra = dif * len(mp) valor = cierres_extra * ticket print('ticket mediano de una venta cerrada: %.2f' % ticket) print('ventas por Marketplace en el periodo: %d' % len(mp)) print('cierres adicionales si tuvieran la tasa de WhatsApp: %.1f' % cierres_extra) print() print('valor central: S/ %.0f' % valor) print('valor pesimista: S/ %.0f' % ((dif - 1.96 * ee) * len(mp) * ticket)) print('valor optimista: S/ %.0f' % ((dif + 1.96 * ee) * len(mp) * ticket))
ticket mediano de una venta cerrada: 570.61 ventas por Marketplace en el periodo: 763 cierres adicionales si tuvieran la tasa de WhatsApp: 147.8 valor central: S/ 84349 valor pesimista: S/ 62748 valor optimista: S/ 105951
Entre 62.748 y 105.951 soles en año y medio 💰
Uso la mediana del ticket y no la media a propósito, por lo del capítulo 3: la media está inflada por los mayoristas y daría un número más bonito y menos defendible.
El informe
Todo lo anterior cabe en esto, que es lo único que hay que llevar a la reunión 📄
| Apartado | Contenido |
|---|---|
| Pregunta | ¿Conviene mover clientes de Marketplace a WhatsApp? |
| Hallazgo | WhatsApp cierra 19,37 puntos más (65,51% contra 46,13%), IC 95%: 14,41 a 24,34 puntos |
| Robustez | Se mantiene en los cuatro segmentos, de +15,28 a +27,61 puntos, ningún intervalo toca el cero |
| Tamaño | d = 0,40; el monto por venta no cambia entre canales (789 contra 812) |
| Valor | Entre S/ 62.748 y S/ 105.951 en 18 meses, con ticket mediano de S/ 570,61 |
| Datos | 3.000 ventas de 617 clientes, enero 2025 a junio 2026 |
| Límites | Es observacional, no un experimento. Las filas no son independientes. Junio de 2026 está incompleto. |
| Siguiente paso | Prueba con 100 clientes de Marketplace durante 3 meses |
El error que casi me cuesta el informe
Mientras armaba esto escribí el nombre del canal con minúscula, que es como lo escribe medio mundo 🙃
v.groupby('canal')['compro'].mean()['Whatsapp']
KeyError: 'Whatsapp'
Menos mal. Ahora mira exactamente el mismo despiste hecho con un filtro:
print('con la W mal escrita: ', v.loc[v['canal'] == 'Whatsapp', 'compro'].mean()) print('filas que trae: ', (v['canal'] == 'Whatsapp').sum()) print('bien escrito: ', v.loc[v['canal'] == 'WhatsApp', 'compro'].mean())
con la W mal escrita: nan filas que trae: 0 bien escrito: 0.655076495132128
Cero filas y un nan, sin protestar. Es el mismo error del
capítulo 7, y lo repito aquí a propósito porque en un informe de siete pasos hay
veinte filtros, y basta con que uno esté mal 😖
Mi regla, después de tropezarme muchas veces: después de cada filtro, imprime cuántas filas quedaron. Una línea que no cuesta nada y que caza esto en el segundo en que pasa.
La limitación que hay que decir en voz alta
Y ahora lo más importante del capítulo, que es lo que el análisis no demuestra 🗣️
Todo esto es observacional. Yo no asigné clientes a canales: ellos eligieron. Así que la explicación alternativa está viva y es muy plausible: quien escribe por WhatsApp ya venía decidido, y quien navega en Marketplace está comparando precios.
Si eso es lo que pasa, el canal no causa nada: refleja la intención que el cliente ya traía. Y mover a la gente de un sitio a otro no movería el resultado ni un punto.
Controlar por segmento, que es lo que hicimos, descarta ese confusor. No descarta la intención de compra, que no está en los datos 🤷
Por eso el siguiente paso del informe no es "implementar", es probar. Y con los números del capítulo 14 ya sabemos cuánta gente hace falta.
Y dos más, de los que salen montando el informe
v.groupby(['ciudad', 'segmento'])['monto'].agg('promedio')
AttributeError: 'SeriesGroupBy' object has no attribute 'promedio'
Se llama mean. Lo pongo aquí, al final del libro, porque es el error que más veces vas a cometer y nunca deja de dar rabia: uno escribe en español sin darse cuenta 🙃
d_de_cohen(a, b, 0.8)
TypeError: d_de_cohen() takes 2 positional arguments but 3 were given
El 0,8 es el umbral de "efecto grande" de la tabla de Cohen, y no es un parámetro de la función: es algo que decides tú al leer el número. Este error es bonito porque nace de una confusión de fondo, la de mezclar lo que se calcula con lo que se interpreta, y esa confusión es medio libro.
Practica 💪
1. Diseña la prueba que hace falta
Calcula cuántos clientes de Marketplace necesitas en una prueba real para detectar la mitad del efecto observado.
base = mp['compro'].mean() for objetivo in [base + 0.19, base + 0.10, base + 0.05]: h = 2 * (np.arcsin(np.sqrt(objetivo)) - np.arcsin(np.sqrt(base))) n = 2 * (1.96 + 0.84) ** 2 / h ** 2 print('de %.2f%% a %.2f%% (+%.0f puntos) -> %.0f clientes por grupo' % (100 * base, 100 * objetivo, 100 * (objetivo - base), n))
de 46.13% a 65.13% (+19 puntos) -> 106 clientes por grupo de 46.13% a 56.13% (+10 puntos) -> 390 clientes por grupo de 46.13% a 51.13% (+5 puntos) -> 1566 clientes por grupo
Con 106 por grupo detectas el efecto completo; con 390, la mitad 📋
Ese es el número que va en el informe. Y fíjate en lo útil que es: si en tu Marketplace pasan 200 clientes al mes, la prueba de 390 por grupo tarda cuatro meses. Se puede planificar.
Y el escenario de 5 puntos, con 1.566 por grupo, dice algo también: si la mejora real fuera pequeña, no la vas a poder demostrar con un piloto razonable. Más vale saberlo antes de prometerlo 🗓️
2. ¿Y si el efecto fuera la satisfacción?
Comprueba si los clientes de WhatsApp vienen con más satisfacción de partida, que sería una explicación alternativa.
sa = wa['satisfaccion'].dropna() sb = mp['satisfaccion'].dropna() print('satisfaccion media: WhatsApp %.4f (n=%d) | Marketplace %.4f (n=%d)' % (sa.mean(), len(sa), sb.mean(), len(sb))) print('p = %.4f' % stats.ttest_ind(sa, sb, equal_var=False).pvalue) print('d = %.4f' % d_de_cohen(sa, sb))
satisfaccion media: WhatsApp 3.0429 (n=652) | Marketplace 2.9677 (n=711) p = 0.3251 d = 0.0534
No hay diferencia: p = 0,3251 y d = 0,0534 🙂
O sea que la satisfacción no explica el efecto del canal. Los clientes de los dos canales llegan igual de contentos.
Este es un ejercicio de descarte, y son los que más valen. No añade nada positivo al informe, pero quita del medio una objeción que alguien iba a hacer en la reunión 🛡️
Ojo con el matiz: descartar la satisfacción no descarta la intención de compra, que es otra cosa y no está medida. Descartar confusores es infinito; descartas los que puedes y dices cuáles quedan 📝
3. ¿Se mantiene a lo largo del tiempo?
Un efecto que solo existe en unos meses es sospechoso. Comprueba trimestre a trimestre.
v['trimestre'] = v['fecha'].dt.to_period('Q') for tri, g in v.groupby('trimestre'): a = g.loc[g['canal'] == 'WhatsApp', 'compro'] b = g.loc[g['canal'] == 'Marketplace', 'compro'] if len(a) < 30 or len(b) < 30: continue print('%s WhatsApp %.4f (n=%3d) | Marketplace %.4f (n=%3d) | dif %+.4f' % (tri, a.mean(), len(a), b.mean(), len(b), a.mean() - b.mean()))
2025Q1 WhatsApp 0.6607 (n=112) | Marketplace 0.5000 (n=140) | dif +0.1607 2025Q2 WhatsApp 0.7109 (n=128) | Marketplace 0.4148 (n=135) | dif +0.2961 2025Q3 WhatsApp 0.5702 (n=114) | Marketplace 0.4874 (n=119) | dif +0.0828 2025Q4 WhatsApp 0.5950 (n=121) | Marketplace 0.5000 (n=138) | dif +0.0950 2026Q1 WhatsApp 0.7661 (n=124) | Marketplace 0.4348 (n=115) | dif +0.3313 2026Q2 WhatsApp 0.6167 (n=120) | Marketplace 0.4224 (n=116) | dif +0.1943
Los seis trimestres, todos positivos, de +8,28 a +33,13 puntos 💪
Esto es lo que más confianza da de todo el análisis. Seis mediciones independientes en el tiempo y las seis apuntan al mismo lado.
Ahora mira cuánto bailan: 16,07, luego 29,61, luego 8,28, 9,50, 33,13 y 19,43. Con unas 120 ventas por celda, cada trimestre trae un margen de error enorme, así que ese baile es exactamente lo que espera el capítulo 10 🎢
Y por eso hay que resistirse a contar historias con la serie. "El efecto se disparó en 2026Q1" o "cayó en 2025Q3" serían justo el p-hacking del capítulo 17: seis mediciones ruidosas siempre tienen una punta y un valle.
4. La versión honesta contando clientes
Repite el análisis contando clientes en vez de ventas, para respetar la independencia.
por_cliente = (v[v['canal'].isin(['WhatsApp', 'Marketplace'])] .groupby(['cliente_id', 'canal'])['compro'].mean().reset_index()) ca = por_cliente.loc[por_cliente['canal'] == 'WhatsApp', 'compro'] cb = por_cliente.loc[por_cliente['canal'] == 'Marketplace', 'compro'] dc = ca.mean() - cb.mean() ec = np.sqrt(ca.var(ddof=1) / len(ca) + cb.var(ddof=1) / len(cb)) print('por venta: dif %.4f | IC [%.4f, %.4f] | n = %d y %d' % (dif, dif - 1.96 * ee, dif + 1.96 * ee, len(wa), len(mp))) print('por cliente: dif %.4f | IC [%.4f, %.4f] | n = %d y %d' % (dc, dc - 1.96 * ec, dc + 1.96 * ec, len(ca), len(cb)))
por venta: dif 0.1937 | IC [0.1441, 0.2434] | n = 719 y 763 por cliente: dif 0.2040 | IC [0.1486, 0.2593] | n = 428 y 443
Casi el mismo resultado: 19,37 puntos por venta y 20,40 por cliente 🎯
El intervalo se ensancha un poco (de 9,9 a 11,1 puntos de ancho), que es lo esperable al pasar de 719 a 428 casos. Pero la conclusión no se mueve, e incluso sale algo más grande contando clientes.
Esta comprobación es la que convierte la limitación "las filas no son independientes" en una nota al pie en vez de un problema. Cuesta cinco líneas y te la va a preguntar cualquiera que sepa 🧾
5. Lo que pasaría si te equivocaras
Calcula el coste de la decisión en el peor caso: que el efecto sea cero y muevas a todos igual.
clientes_marketplace = mp['cliente_id'].nunique() coste_por_cliente = 15 # supuesto: tiempo del equipo comercial, en soles coste = clientes_marketplace * coste_por_cliente print('clientes de Marketplace: %d' % clientes_marketplace) print('coste si el efecto fuera cero: S/ %.0f' % coste) print('valor si el efecto es el pesimista: S/ %.0f' % ((dif - 1.96 * ee) * len(mp) * ticket)) print() print('el efecto tendria que ser mayor que %.4f puntos para pagar el coste' % (coste / (len(mp) * ticket)))
clientes de Marketplace: 443 coste si el efecto fuera cero: S/ 6645 valor si el efecto es el pesimista: S/ 62748 el efecto tendria que ser mayor que 0.0153 puntos para pagar el coste
El punto de equilibrio está en 1,53 puntos de mejora, y el peor caso del intervalo es 14,41 puntos 🎉
O sea que la decisión es cómoda: incluso equivocándonos en nueve de cada diez partes del efecto, sigue saliendo a cuenta.
Ese coste de 15 soles por cliente me lo inventé yo y hay que decirlo así de claro en el informe. Lo importante no es el número sino la estructura: cuando pones el punto de equilibrio al lado del intervalo, la decisión se toma sola ⚖️
6. El análisis entero, en una función
Empaqueta todo para poder repetirlo con cualquier par de canales.
def compara_canales(df, canal_a, canal_b, control='segmento'): a = df[df['canal'] == canal_a]['compro'] b = df[df['canal'] == canal_b]['compro'] d = a.mean() - b.mean() e = np.sqrt(a.var(ddof=1) / len(a) + b.var(ddof=1) / len(b)) aguanta = 0 grupos = df[control].nunique() for g in df[control].unique(): sub = df[df[control] == g] x = sub[sub['canal'] == canal_a]['compro'] y = sub[sub['canal'] == canal_b]['compro'] ee_g = np.sqrt(x.var(ddof=1) / len(x) + y.var(ddof=1) / len(y)) if (x.mean() - y.mean()) - 1.96 * ee_g > 0: aguanta += 1 return ('%-12s vs %-12s dif %+.4f IC [%+.4f, %+.4f] | aguanta en %d/%d %ss' % (canal_a, canal_b, d, d - 1.96 * e, d + 1.96 * e, aguanta, grupos, control)) for a, b in [('WhatsApp', 'Marketplace'), ('WhatsApp', 'Tienda'), ('Tienda', 'Web'), ('Web', 'Marketplace')]: print(compara_canales(v, a, b))
WhatsApp vs Marketplace dif +0.1937 IC [+0.1441, +0.2434] | aguanta en 4/4 segmentos WhatsApp vs Tienda dif +0.0352 IC [-0.0143, +0.0848] | aguanta en 2/4 segmentos Tienda vs Web dif +0.0390 IC [-0.0103, +0.0883] | aguanta en 1/4 segmentos Web vs Marketplace dif +0.1195 IC [+0.0701, +0.1688] | aguanta en 4/4 segmentos
Las cuatro comparaciones en cuatro líneas 🙌
Y sale una segunda historia que no habíamos visto: Web contra Marketplace son casi 12 puntos, y aguanta en los 4 segmentos. Otro candidato, y bueno.
Mientras que WhatsApp contra Tienda y Tienda contra Web son ruido: sus intervalos contienen el cero, aunque algún segmento suelto salga positivo. Eso de "aguanta en 2 de 4" es justo lo que produce el azar cuando no hay nada.
O sea que el archivo no dice "hay cuatro canales de calidad distinta": dice que Marketplace está solo abajo y los otros tres son equivalentes. Y esa frase es mucho más accionable que un ranking 📊
7. ¿Qué columna explica el cierre?
Antes de cerrar el informe, mira cuánto separa cada columna la tasa de cierre.
for col in ['ciudad', 'canal', 'segmento', 'categoria']: t = pd.crosstab(v[col], v['compro'], normalize='index')[1] print('%-11s cierra de %.1f%% a %.1f%%, o sea %.1f puntos de diferencia' % (col, t.min() * 100, t.max() * 100, (t.max() - t.min()) * 100))
ciudad cierra de 56.9% a 59.2%, o sea 2.3 puntos de diferencia canal cierra de 46.1% a 65.5%, o sea 19.4 puntos de diferencia segmento cierra de 37.5% a 72.4%, o sea 34.9 puntos de diferencia categoria cierra de 55.9% a 59.1%, o sea 3.1 puntos de diferencia
Esta es la tabla con la que yo abriría el informe 💼
Dos columnas mueven la aguja y dos no. Y el orden importa: el segmento vale 34,9 puntos y el canal 19,4, así que si hay que elegir una sola palanca, ya sabes cuál.
Fíjate en que esto no lleva ninguna prueba estadística. Es solo contar bien, y contesta el 80% de lo que te van a preguntar. Las pruebas vienen después, para saber si te lo puedes creer 🎯
8. ¿Cambió algo con el tiempo?
Parte los datos por la mitad del periodo y comprueba si la tasa de cierre se movió.
ordenado = v.sort_values('fecha') mitad = len(ordenado) // 2 antes, despues = ordenado.iloc[:mitad], ordenado.iloc[mitad:] print('primera mitad del tiempo: cierra %.4f (n=%d)' % (antes['compro'].mean(), len(antes))) print('segunda mitad: cierra %.4f (n=%d)' % (despues['compro'].mean(), len(despues))) print('p = %.4f' % stats.chi2_contingency( pd.crosstab(ordenado['fecha'] >= antes['fecha'].max(), ordenado['compro']))[1])
primera mitad del tiempo: cierra 0.5800 (n=1500) segunda mitad: cierra 0.5753 (n=1500) p = 0.8399
Nada. Y eso es una buena noticia para el informe 💚
Esta comprobación se llama estabilidad y casi nadie la hace: si la tasa hubiera cambiado a mitad del periodo, todo lo que concluiste sobre el conjunto estaría mezclando dos mundos. Como no cambió, puedes hablar de "la tasa de cierre" en singular.
Es una línea en el informe y es de las que más credibilidad dan, porque enseña que miraste antes de afirmar 🗓️
La recomendación que le baja la conversión a un segmento
Y para terminar el libro, la trampa que está en el salto del número a la decisión, que es donde de verdad se pierde el dinero 😰
La trampa
La conclusión es clara: WhatsApp convierte 3,5 puntos más que Tienda. Propones mover a todos los clientes a WhatsApp.
# en total # WhatsApp 0.655 Tienda 0.620 # dentro de Horeca # WhatsApp 0.658 Tienda 0.759 # n = 187 y 166 p = 0.0492
Qué está mal
En Horeca, mover clientes a WhatsApp les baja la conversión diez puntos 😰 Y el número general dice lo contrario.
WhatsApp gana en Bodega, en Mayorista y en Minimarket. Pierde en Horeca, y pierde de forma significativa. Así que la recomendación correcta no es "muevan a todos": es "muevan a estos tres y a Horeca déjenlo donde está".
Ojo con lo que no dice esta trampa. Contra Marketplace, que es la comparación del capítulo, WhatsApp gana en los cuatro segmentos y ahí la recomendación general sí aguanta. La diferencia entre las dos comparaciones es exactamente el trabajo: comprobar, no suponer.
Y el error de fondo no está en el cálculo, está en el salto del promedio a la decisión. Un promedio describe a un grupo. Una decisión se aplica a personas concretas, y ninguna de ellas es el promedio. Si la conclusión aguanta en todos los cortes, tienes una recomendación. Si no aguanta, tienes algo mejor: una recomendación con nombre y apellido.
Comprueba que lo tienes
WhatsApp cierra 19,37 puntos más que Marketplace y aguanta en los cuatro segmentos. ¿Qué recomiendas?
- Probar con un piloto, porque esto es observacional y no un experimento
- Mover a todos los clientes de Marketplace a WhatsApp
- Cerrar Marketplace
- Nada, porque el efecto es chico (d = 0,40)
Lo que te llevas
- 📝 Pregunta y criterio antes de mirar. Escribir "me lo tomo en serio a partir de 5 puntos" es lo que impide el p-hacking.
- 📊 WhatsApp cierra 19,37 puntos más que Marketplace, IC de 14,41 a 24,34, y el monto por venta no cambia entre canales.
- 🧩 El efecto aguanta en los cuatro segmentos, de +15,28 a +27,61. No es Simpson.
- 💰 Traducido: entre 62.748 y 105.951 soles en 18 meses, con el ticket mediano y no con la media.
- 🗣️ Es observacional. Controlar por segmento descarta ese confusor, no la intención de compra. Por eso el siguiente paso es probar, no implementar.
- ⚖️ Con el punto de equilibrio al lado del intervalo (1,53 puntos contra 14,41 del peor caso), la decisión se toma sola.
Y si de todo el capítulo te llevas una sola frase, que sea esta:
La pregunta y el criterio se escriben antes de mirar los datos. Después, ya no vale.
Y si quieres hacer este mismo recorrido con los datos de tu empresa y acompañada, eso es justo lo que hacemos en el Full Day IA 💜
Qué viene ahora
Se acabó 🎉 En el capítulo 23 te dejo las referencias: qué leer después, qué herramientas usar y de dónde salió cada cosa de este libro.
La estadística es la mitad del trabajo, y esta es la otra
Si terminaste esto, ya sabes lo que separa un número que significa algo de un número que suena bien:
- De dónde salió la muestra
- Si la diferencia es real o es ruido
- Y qué prueba tocaba usar
Ese criterio es exactamente el que falta en la mayoría de los sistemas de IA que veo en empresas: nadie sabe decir si el sistema funciona o solo lo parece. Es lo que se construye en el curso de ingeniería de IA en producción, donde los evals son el centro y no un adorno del final.
Y si lo tuyo no es construir sistemas sino analizar bien, esto que acabas de terminar ya te habilita para eso, que es un oficio entero y con demanda. El capítulo de referencias te deja por dónde seguir sin gastar un sol, y lo que le falta a la estadística para que sea trabajo pagado son las herramientas: sacar el dato tú misma y presentarlo. Eso está en las clases grabadas de Excel y Power BI 💪
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.