Hiciste el análisis. Los números están bien, la prueba es la que tocaba y el cuaderno corre de arriba abajo.
Y ahora tienes que decirlo en una frase, porque nadie va a leer tu cuaderno. Esa frase es el trabajo entero visto desde afuera, y casi nunca se enseña 🌸
Los números que vamos a contar
import pandas as pd import numpy as np from scipy import stats 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', 'monto']].dropna(subset=['canal', 'compro']) w = d[d['canal'] == 'WhatsApp']['compro'] b = d[d['canal'] == 'Web']['compro'] print('WhatsApp: n =', len(w), '| convierte', round(float(w.mean()), 4)) print('Web : n =', len(b), '| convierte', round(float(b.mean()), 4))
WhatsApp: n = 725 | convierte 0.6524 Web : n = 801 | convierte 0.583
Dos canales, la conversión de cada uno. De ahí en adelante todo es la misma información contada de maneras distintas.
Titular 1: el relativo, que es el que todos usan
dif = float(w.mean() - b.mean()) print('sube un', round(dif / float(b.mean()) * 100, 1), '%')
sube un 11.9 %
"WhatsApp convierte un 11,9% más que la web."
Suena bien y es cierto. Y tiene dos trampas que no se ven.
La primera: es un porcentaje de un porcentaje. La conversión pasó de 58,3% a 65,2%, y eso es un 11,9% más de lo que ya había. Quien lo escuche sin pensar va a entender "sube 11,9 puntos", que es casi el doble de lo que pasó.
La segunda es peor, y se ve mirando un segmento chico:
bodega = d[d['segmento'] == 'Bodega'] a2 = bodega[bodega['canal'] == 'WhatsApp']['compro'] b2 = bodega[bodega['canal'] == 'Web']['compro'] print('Bodega WhatsApp:', round(float(a2.mean()), 4)) print('Bodega Web :', round(float(b2.mean()), 4)) print('relativo :', round((float(a2.mean()) - float(b2.mean())) / float(b2.mean()) * 100, 1), '%')
Bodega WhatsApp: 0.452 Bodega Web : 0.3889 relativo : 16.2 %
16,2% en Bodega contra 11,9% general. ¿Bodega es el mejor segmento?
No necesariamente: el relativo crece solo cuando la base es más chica. Bodega convierte peor de entrada, así que la misma cantidad de puntos parece más. Con una base del 1%, subir un punto es un 100% de mejora, y sigue siendo un punto 🙃
Titular 2: el absoluto, que es el honesto
print('sube', round(dif * 100, 1), 'puntos')
sube 6.9 puntos
"WhatsApp convierte 6,9 puntos más que la web."
Menos vistoso, imposible de malinterpretar. Y fíjate en la palabra puntos, que no es un adorno: decir "6,9 por ciento más" sería mentira, porque el por ciento ya está ocupado por la conversión.
Esa distinción entre puntos porcentuales y por ciento es la que más se confunde en informes reales, y la que más discusiones caras provoca.
Titular 3: el intervalo, que es el que aguanta preguntas
se = float(np.sqrt(w.var(ddof=1) / len(w) + b.var(ddof=1) / len(b))) lo, hi = dif - 1.96 * se, dif + 1.96 * se print('entre', round(lo * 100, 1), 'y', round(hi * 100, 1), 'puntos') print('valor p:', round(float(stats.ttest_ind(w, b, equal_var=False)[1]), 4))
entre 2.1 y 11.8 puntos valor p: 0.0053
"WhatsApp convierte entre 2 y 12 puntos más."
Este es el bueno, y es el que casi nadie pone en el titular porque parece que te estás lavando las manos.
Es al revés. Mira lo que hace en una reunión: si alguien te pregunta "¿y qué tan seguro estás?", ya lo contestaste. Y si el plan de la empresa necesitaba 10 puntos para salir a cuenta, el intervalo te está diciendo que puede que no alcance, y eso es exactamente lo que esa persona necesita saber antes de firmar.
El valor p dice que la diferencia es real. El intervalo dice de qué tamaño. En una reunión el segundo importa más, y lo trabajamos en el capítulo 10.
Titular 4: la plata, que es el que consigue el presupuesto
ticket = float(d['monto'].dropna().mean()) print('ticket medio:', round(ticket, 2)) for n in (1000, 10000): print(' con', n, 'clientes ->', round(dif * n, 1), 'ventas mas,', round(dif * n * ticket), 'soles')
ticket medio: 808.24 con 1000 clientes -> 69.4 ventas mas, 56086 soles con 10000 clientes -> 693.9 ventas mas, 560861 soles
"Con 1.000 clientes son unas 69 ventas más, cerca de 56.000 soles."
Este es el que hace que alguien deje de mirar el teléfono 💰
Y también el más fácil de inflar. Tres cosas que hay que decir en voz alta al darlo:
- De dónde sale el 1.000. Si te lo inventaste para que el número quede bonito, se nota en la primera pregunta.
- Que el ticket medio es un promedio, con toda la dispersión que trabajamos en el capítulo 4 detrás.
- Que el rango también se traduce. Con el borde bajo del intervalo, esos 56.000 se convierten en 17.000. Da menos gusto decirlo y es lo que te salva cuando alguien haga la cuenta.
El titular que no sirve, y es el más común
"Analizamos 3.037 ventas con una prueba t de Welch y obtuvimos un p-valor de 0,0053."
Eso es lo que hiciste, no lo que encontraste. Y es el titular de la mitad de los informes que me toca leer.
Sale de un miedo razonable: si no digo el método, van a pensar que me lo inventé. Pero el método va en el cuerpo y en el anexo, donde quien quiera revisarlo lo encuentra. El titular es para quien tiene que decidir algo.
La regla, en una línea
Qué cambia, cuánto, con cuánta certeza y qué significa en tu negocio.
Los cuatro juntos caben en una frase:
"Mover clientes a WhatsApp sube la conversión entre 2 y 12 puntos, o sea entre 17.000 y 95.000 soles al año con 1.000 clientes."
Eso es el análisis entero, dicho en el idioma de quien paga 🌟
El error de escribir el titular antes de mirar
d['segmento'].mean()
TypeError: Cannot perform reduction 'mean' with string dtype
Parece un error tonto y es el síntoma de algo que no lo es: alguien quiso poner "el segmento promedio" en una lámina.
No existe un segmento promedio. Lo que existe es el más frecuente, que es la moda del capítulo 3. Cuando un titular pide un número que no se puede calcular, casi siempre es porque la frase se escribió antes que el análisis.
Lo que te llevas
- El titular dice qué encontraste, no qué hiciste.
- El relativo exagera, y exagera más cuanto más chica es la base.
- Puntos porcentuales y por ciento no son lo mismo.
- El intervalo va en el titular, no en el anexo: contesta la pregunta antes de que te la hagan.
- Traducir a dinero consigue presupuesto, y hay que traducir también el borde bajo.
- Qué cambia, cuánto, con cuánta certeza y qué significa.
Comprueba que lo tienes
Tienes una diferencia de 6,9 puntos con un intervalo que va de 2,1 a 11,8. ¿Cuál de estos titulares se sostiene si te preguntan?
- WhatsApp convierte entre 2 y 12 puntos más, y con 1.000 clientes son unos 56.000 soles
- WhatsApp convierte 11,9% más que Web
- WhatsApp es mejor canal que Web
- Analizamos 3.037 ventas con una prueba t de Welch
Ejercicios
1. El relativo crece cuando la base baja
Comprueba que el mismo punto de mejora se ve distinto según de dónde partas.
for base in (0.60, 0.30, 0.10, 0.02): print('base', base, '-> subir 1 punto es un', round(0.01 / base * 100, 1), '% de mejora')
base 0.6 -> subir 1 punto es un 1.7 % de mejora base 0.3 -> subir 1 punto es un 3.3 % de mejora base 0.1 -> subir 1 punto es un 10.0 % de mejora base 0.02 -> subir 1 punto es un 50.0 % de mejora
El mismo punto, de 1,7% a 50%. Por eso cuando alguien te presuma un porcentaje de mejora enorme, la primera pregunta es "¿sobre qué base?". Y por eso cuando lo presumas tú, dilo antes de que te lo pregunten.
2. Traduce el borde bajo, no solo el centro
La cuenta que hay que llevar hecha a la reunión.
ticket = float(d['monto'].dropna().mean()) for etiqueta, efecto in (('borde bajo', lo), ('centro', dif), ('borde alto', hi)): print(etiqueta.ljust(11), '->', round(efecto * 1000 * ticket), 'soles')
borde bajo -> 16733 soles centro -> 56086 soles borde alto -> 95439 soles
De 17.000 a 95.000. Ese rango es la respuesta honesta, y es la que quieres tener a mano cuando alguien te diga "entonces contamos con 56.000".
3. El titular de tu compañero, revisado
Comprueba si "casi el doble" se sostiene.
print('WhatsApp / Web =', round(float(w.mean()) / float(b.mean()), 3)) print('WhatsApp - Web =', round(dif, 4))
WhatsApp / Web = 1.119 WhatsApp - Web = 0.0694
1,119 veces, no el doble. Cuando alguien diga "casi el doble", divide y mira: la mayoría de las veces esa frase sale de confundir el cociente con la diferencia, o de mirar un gráfico con el eje cortado, que es el truco que trabajo en Gráficos que se entienden.
4. Cuánto se ensancha el titular si te falta muestra
Ve recortando la muestra y mira el intervalo abrirse.
def intervalo(a, bb): di = float(a.mean() - bb.mean()) s = float(np.sqrt(a.var(ddof=1) / len(a) + bb.var(ddof=1) / len(bb))) return (di - 1.96 * s) * 100, (di + 1.96 * s) * 100 for corte, nombre in ((1, 'entera'), (2, 'mitad'), (4, 'cuarto')): mw = w.sample(len(w) // corte, random_state=0) mb = b.sample(len(b) // corte, random_state=0) a1, a2 = intervalo(mw, mb) print(nombre.ljust(7), 'n =', len(mw) + len(mb), '| entre', round(a1, 1), 'y', round(a2, 1), '| ancho', round(a2 - a1, 1))
entera n = 1526 | entre 2.1 y 11.8 | ancho 9.7 mitad n = 762 | entre 2.5 y 16.2 | ancho 13.7 cuarto n = 381 | entre -1.1 y 18.1 | ancho 19.2
Con la muestra entera el titular es "sube entre 2 y 12 puntos". Con un cuarto pasa a "entre menos 1 y 18", que cruza el cero: ya no puedes descartar que no haya diferencia.
Los mismos datos y la misma dirección, y un titular que se convierte en "no sabemos". La muestra no es un detalle técnico del anexo: es lo que decide si tienes titular o no lo tienes. Cuánta hace falta se calcula antes, y está en el capítulo 18.
5. Escribe las cuatro versiones de un tirón
Una función para no volver a improvisar el titular.
def titulares(a, b, ticket, clientes=1000): dif = float(a.mean() - b.mean()) se = float(np.sqrt(a.var(ddof=1) / len(a) + b.var(ddof=1) / len(b))) return { 'relativo': str(round(dif / float(b.mean()) * 100, 1)) + '%', 'absoluto': str(round(dif * 100, 1)) + ' puntos', 'intervalo': str(round((dif - 1.96 * se) * 100, 1)) + ' a ' + str(round((dif + 1.96 * se) * 100, 1)) + ' puntos', 'plata': str(round(dif * clientes * ticket)) + ' soles'} for clave, valor in titulares(w, b, ticket).items(): print(clave.ljust(10), valor)
relativo 11.9% absoluto 6.9 puntos intervalo 2.1 a 11.8 puntos plata 56086 soles
Guárdatela. Correrla en dos segundos y ver las cuatro juntas te obliga a elegir a propósito, en vez de escribir la primera que se te ocurra, que casi siempre es el relativo porque es la más grande.
6. El titular con la limitación dentro
Cuenta cuántos clientes distintos hay detrás de esas ventas.
sub = df[df['canal'].isin(['WhatsApp', 'Web'])] print('ventas :', len(sub)) print('clientes:', sub['cliente_id'].nunique()) print('ventas por cliente:', round(len(sub) / sub['cliente_id'].nunique(), 2))
ventas : 1526 clientes: 571 ventas por cliente: 2.67
1.526 ventas de 571 clientes, o sea unas 2,7 por cabeza. Las filas no son independientes: si un cliente compra siempre, aporta tres filas que dicen lo mismo. Es el problema número cuatro del capítulo 17, y hace que el intervalo real sea más ancho que el que calculamos.
Dicho en el titular: "entre 2 y 12 puntos, con la salvedad de que son 571 clientes y no 1.526 independientes, así que el rango real es algo más ancho". Es una frase incómoda y es la que hace que te crean la próxima vez 💛
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.