Capítulo 19 de 23 10 secciones 10 min

Compartir

El titular dice lo que encontraste, no lo que hiciste

Los mismos números dan cuatro titulares distintos. Cuál eliges decide si te aprueban el proyecto o te hacen volver con más datos.

El titular de un análisis tiene que decir qué encontraste, con su incertidumbre y su consecuencia, no qué método usaste. Los mismos números dan cuatro titulares: el relativo exagera, el absoluto es honesto, el intervalo es el que aguanta preguntas y el de dinero es el que consigue presupuesto 🌸

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.

¿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?