{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# De datos sucios a una conclusión\n",
    "\n",
    "Todo el libro junto, de punta a punta, y terminando en algo que se puede llevar a una reunión.\n",
    "\n",
    "Cuaderno de práctica del capítulo 16 de **Python desde cero**, de Miss Yera.\n",
    "\n",
    "Corre de arriba abajo. Si lo abres en Google Colab no necesitas instalar nada.\n",
    "\n",
    "Capítulo completo: https://missyera.com/guias/python-desde-cero/proyecto-final/\n",
    "\n",
    "Los ejercicios están al final y traen una celda vacía debajo de cada uno. Las\n",
    "respuestas viven en el cuaderno de soluciones, y merece la pena pelearse un\n",
    "rato antes de abrirlo 💛"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Antes de empezar"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# Este capitulo abre el archivo por su nombre, sin URL, que es lo que se hace\n",
    "# cuando lo tienes al lado. Aqui se baja primero para que exista.\n",
    "for nombre in ['ventas-miss-yera.csv']:\n",
    "    urllib.request.urlretrieve(f\"https://missyera.com/static/datasets/{nombre}\", nombre)\n",
    "    print(nombre, \"lista\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Antes de empezar\n",
    "\n",
    "Esta celda baja el ayudante que corrige tus ejercicios. Después, en cada\n",
    "ejercicio que se pueda corregir solo, vas a ver `%%revisa` arriba de la celda:\n",
    "escribe tu respuesta debajo, ejecuta, y te digo si te salió 💛"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# El ayudante de los cuadernos. Trae la corrección de los ejercicios y, en los\n",
    "# capítulos de consola, la celda mágica que ejecuta los comandos. Se baja en\n",
    "# vez de venir pegado aquí para que siempre sea el último.\n",
    "urllib.request.urlretrieve(\n",
    "    \"https://missyera.com/static/cuadernos/revisa.py\", \"revisa.py\")\n",
    "import revisa\n",
    "revisa.carga({\n",
    "    1: \"eydmaWxhcyc6IDMwMzcsICdjb2x1bW5hcyc6IDE0LCAnbnVsb3MnOiAxMzg0LCAnY29sdW1uYXNfY29uX251bG9zJzogMywgJ2ZpbGFzX2R1cGxpY2FkYXMnOiAzNywgJ2lkc19yZXBldGlkb3MnOiAzN30=\",\n",
    "    2: \"ICAgICAgICAgICAgY29uX3ByZXZpYSAgc2luX3ByZXZpYSAgZGlmZXJlbmNpYQpzZWdtZW50byAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCkJvZGVnYSAgICAgICAgICAgMC40MTEgICAgICAgMC4yMDYgICAgICAgMC4yMDUKTWluaW1hcmtldCAgICAgICAwLjYxMCAgICAgICAwLjQwNSAgICAgICAwLjIwNQpIb3JlY2EgICAgICAgICAgIDAuNjY4ICAgICAgIDAuNDcwICAgICAgIDAuMTk4Ck1heW9yaXN0YSAgICAgICAgMC43NTUgICAgICAgMC41NzEgICAgICAgMC4xODM=\",\n",
    "    3: \"dHJpbWVzdHJlCjIwMjVRMSAgICAwLjA5OAoyMDI1UTIgICAgMC4xNDkKMjAyNVEzICAgIDAuMjI3CjIwMjVRNCAgICAwLjI2NgoyMDI2UTEgICAgMC4yNTQKMjAyNlEyICAgIDAuMTk4CkZyZXE6IFEtREVDLCBOYW1lOiBicmVjaGEsIGR0eXBlOiBmbG9hdDY0\",\n",
    "    4: \"c2luX2NvbXByYV9wcmV2aWEKMCAgICAxMS41NwoxICAgIDExLjc0Ck5hbWU6IHVuaWRhZGVzLCBkdHlwZTogZmxvYXQ2NApzaW5fY29tcHJhX3ByZXZpYQowICAgIDUzLjgyCjEgICAgNTAuNDgKTmFtZTogcHJlY2lvX3VuaXRhcmlvLCBkdHlwZTogZmxvYXQ2NA==\",\n",
    "    5: \"c2F0aXNmYWNjacOzbiBiYWphOiAgMTA5NyA0OS4yJQpzYXRpc2ZhY2Npw7NuIGFsdGE6ICAxMTIxIDY3LjElCmJyZWNoYTogMTcuOSU=\",\n",
    "    6: \"NTQ0IHZpc2l0YXMgYSBjbGllbnRlcyBudWV2b3MgY2VycmFyb24gNDElIGNvbnRyYSA2MSUgZGUgbG9zIHJlY3VycmVudGVzLiBMYSBicmVjaGEgc29uIHVub3MgMTA5IHBlZGlkb3MsIG8gc2VhIHVub3MgUy81OCwxNzYgYWwgdGlja2V0IG1lZGlhbm8gZGUgUy81MzQu\",\n",
    "    7: \"cG9yIGNhbmFsOgogIE1hcmtldHBsYWNlICAgICA3NjMgZmlsYXMgICBicmVjaGEgMTQuMyUKICBUaWVuZGEgICAgICAgICAgNzI2IGZpbGFzICAgYnJlY2hhIDE5LjMlCiAgV2ViICAgICAgICAgICAgIDc5MiBmaWxhcyAgIGJyZWNoYSAyMi4yJQogIFdoYXRzQXBwICAgICAgICA3MTkgZmlsYXMgICBicmVjaGEgMjQuNiUKcG9yIHNlZ21lbnRvOgogIEJvZGVnYSAgICAgICAgICA3MDcgZmlsYXMgICBicmVjaGEgMjAuNSUKICBIb3JlY2EgICAgICAgICAgNzQyIGZpbGFzICAgYnJlY2hhIDE5LjglCiAgTWF5b3Jpc3RhICAgICAgIDc1MCBmaWxhcyAgIGJyZWNoYSAxOC4zJQogIE1pbmltYXJrZXQgICAgICA4MDEgZmlsYXMgICBicmVjaGEgMjAuNSU=\",\n",
    "    8: \"YXJjaGl2b3MgZXNjcml0b3M6IFsnY2llcnJlLXBvci1ncnVwby5jc3YnLCAnY29tby1zZS1oaXpvLnR4dCddCgpncnVwbyx2aXNpdGFzLGNpZXJyZQpudWV2b3MsNTQ0LDAuNDEzNgpyZWN1cnJlbnRlcywyNDU2LDAuNjE0CgpGdWVudGU6IHZlbnRhcy1taXNzLXllcmEuY3N2ClB5dGhvbiAzLjExCnBhbmRhcz09My4wLjUKbnVtcHk9PTIuNC42Cm1hdHBsb3RsaWI9PTMuMTEuMQpMaW1waWV6YTogZHVwbGljYWRvcyBwb3IgaWRfdmVudGEsIGNpdWRhZCBub3JtYWxpemFkYSwKbW9udG8gY29uIGNvbWEgZGVjaW1hbCBjb252ZXJ0aWRvLCBmZWNoYSBlbiBkb3MgZm9ybWF0b3Mu\",\n",
    "}, lenguaje=\"python\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Hola! Vamos a juntarlo todo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Doce capítulos después, tienes todo lo que hace falta. Ahora vamos a usarlo\n",
    "como se usa en el trabajo: **de un archivo sucio a algo que puedes decir en\n",
    "una reunión** 💜\n",
    "\n",
    "Y quiero que veas una cosa mientras avanzamos: la parte de código es la más\n",
    "corta. Lo largo es decidir qué mirar y comprobar que lo que viste es real.\n",
    "\n",
    "Los seis pasos, siempre en este orden:\n",
    "\n",
    "- 🔍 Auditar antes de tocar nada\n",
    "\n",
    "- 🧹 Limpiar dejando registro\n",
    "\n",
    "- 🧱 Crear lo que falta\n",
    "\n",
    "- 📊 Analizar\n",
    "\n",
    "- 🧪 Comprobar que no es casualidad\n",
    "\n",
    "- 💬 Concluir con un número"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paso 1. Auditar"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "df = pd.read_csv('ventas-miss-yera.csv')\n",
    "\n",
    "auditoria = {\n",
    "    'filas': len(df),\n",
    "    'columnas': df.shape[1],\n",
    "    'duplicados_id': int(df['id_venta'].duplicated().sum()),\n",
    "    'monto_es_texto': df['monto'].dtype == 'object' or str(df['monto'].dtype) == 'str',\n",
    "    'ciudades_distintas': df['ciudad'].nunique(),\n",
    "    'nulos_totales': int(df.isna().sum().sum()),\n",
    "}\n",
    "\n",
    "for clave, valor in auditoria.items():\n",
    "    print(f'{clave:20} {valor}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Seis líneas y ya sabes que hay problemas: 37 duplicados, el monto llegó como\n",
    "texto, nueve ciudades cuando el Perú de este archivo tiene seis, y 1.384 huecos.\n",
    "\n",
    "**Esa auditoría se hace antes de cualquier análisis y se guarda.**\n",
    "Es lo que le mandas a quien te pasó el archivo, y es lo que te protege el día que\n",
    "alguien cuestione tus números 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paso 2. Limpiar, dejando registro"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cambios = []\n",
    "\n",
    "antes = len(df)\n",
    "df = df.drop_duplicates(subset='id_venta').copy()\n",
    "cambios.append(f'{antes - len(df)} duplicados eliminados')\n",
    "\n",
    "antes_ciudades = df['ciudad'].nunique()\n",
    "df['ciudad'] = (df['ciudad'].str.strip().str.lower()\n",
    "                .str.normalize('NFKD')\n",
    "                .str.encode('ascii', 'ignore').str.decode('utf-8'))\n",
    "cambios.append(f'ciudades unificadas: {antes_ciudades} a {df[\"ciudad\"].nunique()}')\n",
    "\n",
    "df['monto'] = pd.to_numeric(df['monto'].str.replace(',', '.'), errors='coerce')\n",
    "cambios.append(f'montos ilegibles tras limpiar: {int(df[\"monto\"].isna().sum())}')\n",
    "\n",
    "fecha = pd.to_datetime(df['fecha'], format='%Y-%m-%d', errors='coerce')\n",
    "faltan = fecha.isna()\n",
    "fecha[faltan] = pd.to_datetime(df.loc[faltan, 'fecha'],\n",
    "                               format='%d/%m/%Y', errors='coerce')\n",
    "df['fecha'] = fecha\n",
    "cambios.append(f'fechas sin parsear: {int(fecha.isna().sum())}')\n",
    "\n",
    "for c in cambios:\n",
    "    print(' -', c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa lista de cambios es lo que en un proyecto real va al README o a la primera\n",
    "celda del cuaderno. **Un análisis sin registro de limpieza no es\n",
    "reproducible**, y sin reproducible no es defendible.\n",
    "\n",
    "### El error que sale si te saltas un paso\n",
    "\n",
    "Y para que veas por qué el orden importa, mira lo que pasa si intentas\n",
    "calcular antes de convertir el monto:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito.** Se ejecuta dentro de un `try` para que puedas seguir con \"ejecutar todo\" y aun así ver la queja."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    sucio = pd.read_csv('ventas-miss-yera.csv')\n",
    "    sucio['monto'].mean()\n",
    "except Exception as e:\n",
    "    print(f'{type(e).__name__}: {e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "TypeError: Cannot perform reduction 'mean' with string dtype\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\"No puedo promediar una columna de texto\". Y ya sabemos por qué es texto: los\n",
    "612 montos con coma decimal del capítulo 11.\n",
    "\n",
    "Un error así, al principio, es una suerte. El caso malo sería que pandas se\n",
    "inventara un promedio ignorando esas 612 filas y no dijera nada 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paso 3. Crear lo que falta"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ultima = pd.to_datetime(df['fecha_ultima_compra'], format='%Y-%m-%d',\n",
    "                        errors='coerce')\n",
    "\n",
    "df['sin_compra_previa'] = ultima.isna().astype(int)\n",
    "df['precio_unitario'] = (df['monto'] / df['unidades']).round(2)\n",
    "df['mes'] = df['fecha'].dt.to_period('M')\n",
    "\n",
    "print(df[['ciudad', 'monto', 'unidades', 'precio_unitario', 'sin_compra_previa']].head(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esas tres columnas no venían en el archivo. Son las que vas a usar para\n",
    "responder, y crearlas es la mitad del trabajo de un analista."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paso 4. Analizar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La pregunta que le voy a hacer al archivo:\n",
    "**¿los clientes nuevos se comportan distinto de los que ya compraron\n",
    "antes?**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "resumen = df.groupby('sin_compra_previa').agg(\n",
    "    ventas=('id_venta', 'count'),\n",
    "    tasa_compra=('compro', 'mean'),\n",
    "    ticket_mediano=('monto', 'median'),\n",
    "    unidades_medianas=('unidades', 'median'),\n",
    "    satisfaccion=('satisfaccion', 'mean'),\n",
    ").round(3)\n",
    "\n",
    "print(resumen)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está 👀\n",
    "\n",
    "Los que no tienen compra previa cierran el **41,4%** de las veces\n",
    "contra el **61,4%** de los que sí. Veinte puntos de diferencia.\n",
    "\n",
    "Y fíjate en lo que *no* cambia: el ticket es casi igual, las unidades\n",
    "son idénticas y la satisfacción también. O sea que la diferencia no es que\n",
    "compren menos cantidad, es que **cierran menos veces**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paso 5. Comprobar que no es casualidad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de decir esto en voz alta, tres comprobaciones. Esta parte es la que\n",
    "separa un dato de una conclusión 🧪"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. ¿Hay suficientes casos como para creerselo?\n",
    "print('clientes sin compra previa:', int(df['sin_compra_previa'].sum()))\n",
    "\n",
    "# 2. ¿Se repite en todas las ciudades o es una sola arrastrando?\n",
    "por_ciudad = df.groupby(['ciudad', 'sin_compra_previa'])['compro'].mean().unstack()\n",
    "por_ciudad.columns = ['con_previa', 'sin_previa']\n",
    "por_ciudad['diferencia'] = (por_ciudad['con_previa'] - por_ciudad['sin_previa']).round(3)\n",
    "print(por_ciudad.round(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Se repite en las seis.** La diferencia va de 18,8 a 22,4 puntos\n",
    "y en ninguna se da vuelta.\n",
    "\n",
    "Eso es lo que convierte un número en un hallazgo: si el patrón solo apareciera\n",
    "en una ciudad, sería esa ciudad y no el patrón 🌟"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3. ¿Y no será que los nuevos se concentran en un canal malo?\n",
    "print(df.groupby('sin_compra_previa')['canal'].value_counts(normalize=True).round(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Repartidos casi igual. Así que no es que los nuevos lleguen por un canal peor:\n",
    "la diferencia es del cliente nuevo en sí 💜"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paso 6. Concluir con un número"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "total_nuevos = int(df['sin_compra_previa'].sum())\n",
    "tasa_nuevos = df.loc[df['sin_compra_previa'] == 1, 'compro'].mean()\n",
    "tasa_recurrentes = df.loc[df['sin_compra_previa'] == 0, 'compro'].mean()\n",
    "ticket = df['monto'].median()\n",
    "\n",
    "brecha = tasa_recurrentes - tasa_nuevos\n",
    "cierres_perdidos = total_nuevos * brecha\n",
    "oportunidad = cierres_perdidos * ticket\n",
    "\n",
    "print(f'Visitas a clientes nuevos      : {total_nuevos}')\n",
    "print(f'Tasa de cierre nuevos          : {tasa_nuevos:.1%}')\n",
    "print(f'Tasa de cierre recurrentes     : {tasa_recurrentes:.1%}')\n",
    "print(f'Brecha                         : {brecha:.1%}')\n",
    "print(f'Cierres que se pierden         : {cierres_perdidos:.0f}')\n",
    "print(f'A ticket mediano de S/{ticket:,.2f}    : S/{oportunidad:,.0f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso es lo que se dice en la reunión:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "De las 3.000 ventas del periodo, 544 fueron a clientes sin compra previa.\n",
    "Esos cierran 20 puntos menos que los recurrentes, y el patrón se repite en las\n",
    "seis ciudades y en los cuatro canales. Si la primera visita cerrara como las\n",
    "demás, serían unos 109 pedidos más, que al ticket mediano de S/534 son unos\n",
    "S/58.000 del periodo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fíjate en cómo está escrito, que importa tanto como el cálculo:\n",
    "\n",
    "- 🔢 Usé la **mediana** y no el promedio para el ticket, porque el\n",
    "promedio está inflado por unas pocas ventas grandes.\n",
    "\n",
    "- 🗣️ Dije \"unos 109\" y \"unos S/58.000\", no \"108,9\" ni \"S/58.176,44\". Una\n",
    "estimación con dos decimales finge una precisión que no tiene.\n",
    "\n",
    "- 📍 Dije dónde se comprobó (seis ciudades, cuatro canales), que es lo que\n",
    "alguien va a preguntar.\n",
    "\n",
    "- 🚫 Y no dije que arreglarlo *vaya* a producir esos S/58.000. Dije que\n",
    "esa es la brecha. La diferencia es enorme y la explico abajo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El gráfico que acompaña"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib\n",
    "matplotlib.use('Agg')\n",
    "import matplotlib.pyplot as plt\n",
    "import tempfile\n",
    "from pathlib import Path\n",
    "\n",
    "carpeta = Path(tempfile.mkdtemp())\n",
    "orden = por_ciudad.sort_values('diferencia')\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(8, 4))\n",
    "ax.barh(orden.index, orden['con_previa'], color='#4DB8E8', label='Con compra previa')\n",
    "ax.barh(orden.index, orden['sin_previa'], color='#F092C7', label='Cliente nuevo')\n",
    "\n",
    "ax.set_title('El cliente nuevo cierra 20 puntos menos, en las seis ciudades',\n",
    "             loc='left', fontsize=12)\n",
    "ax.set_xlabel('Tasa de cierre')\n",
    "ax.set_xlim(0, 0.7)\n",
    "ax.legend(loc='lower right', frameon=False)\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['right'].set_visible(False)\n",
    "\n",
    "fig.tight_layout()\n",
    "ruta = carpeta / 'cierre-por-ciudad.png'\n",
    "fig.savefig(ruta, dpi=150)\n",
    "plt.close(fig)\n",
    "\n",
    "print('gráfico guardado:', ruta.exists())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un solo gráfico, con el título diciendo la conclusión y el eje empezando en\n",
    "cero. Es todo lo que hace falta para acompañar ese párrafo 📊"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paso 7. Comprobar que la conclusión no depende de ti"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este paso no lo hace casi nadie y es el que más tranquila me deja 🧘‍♀️\n",
    "\n",
    "En el paso 2 tomé decisiones: quité duplicados por `id_venta`,\n",
    "normalicé ciudades, convertí montos con coma. Cada una de esas decisiones la tomé\n",
    "yo. La pregunta honesta es: **¿la conclusión aguanta si las hubiera tomado\n",
    "distinto?**\n",
    "\n",
    "Se responde recalculando lo mismo sobre varios recortes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def brecha_de(tabla):\n",
    "    nuevos = tabla.loc[tabla['sin_compra_previa'] == 1, 'compro'].mean()\n",
    "    recurrentes = tabla.loc[tabla['sin_compra_previa'] == 0, 'compro'].mean()\n",
    "    return len(tabla), nuevos, recurrentes, recurrentes - nuevos\n",
    "\n",
    "\n",
    "crudo = pd.read_csv('ventas-miss-yera.csv')\n",
    "crudo['sin_compra_previa'] = crudo['fecha_ultima_compra'].isna().astype(int)\n",
    "\n",
    "recortes = {\n",
    "    'todo, como lo limpie': df,\n",
    "    'sin quitar duplicados': crudo,\n",
    "    'sin las satisfacciones nulas': df.dropna(subset=['satisfaccion']),\n",
    "    'solo 2025': df[df['fecha'].dt.year == 2025],\n",
    "    'solo 2026': df[df['fecha'].dt.year == 2026],\n",
    "    'sin Lima': df[df['ciudad'] != 'lima'],\n",
    "}\n",
    "\n",
    "for nombre, tabla in recortes.items():\n",
    "    filas, n, r, b = brecha_de(tabla)\n",
    "    print(f'{nombre:<30} {filas:>5} filas   '\n",
    "          f'nuevos {n:.1%}   recurrentes {r:.1%}   brecha {b:.1%}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está la respuesta y me gusta mucho 💜\n",
    "\n",
    "La brecha se mueve entre el 18,7% y el 22,6% en los seis recortes.\n",
    "**Ni uno solo la hace desaparecer**, ni le cambia el signo, ni la\n",
    "duplica. Incluso sin limpiar nada, con los duplicados dentro, sale 19,6%.\n",
    "\n",
    "Eso quiere decir que el hallazgo **no lo fabriqué yo con la\n",
    "limpieza**. Estaba en los datos antes de que yo los tocara, y eso es lo que\n",
    "te permite defenderlo en una reunión cuando alguien pregunte \"¿y si hubieras\n",
    "quitado los duplicados de otra forma?\" 🛡️\n",
    "\n",
    "La otra lectura, la que también hay que decir: el recorte de 2026 da 22,6% y el\n",
    "de 2025 da 18,7%. La brecha se está **ensanchando**, no cerrando. Ese\n",
    "es un dato adicional que salió gratis de esta comprobación.\n",
    "\n",
    "Y si algún recorte hubiera dado una brecha cercana a cero, la conclusión no\n",
    "sería \"el análisis está mal\". Sería **\"la conclusión vale para unos casos y\n",
    "no para otros\"**, y eso también se reporta. Un hallazgo que solo aparece\n",
    "con una limpieza concreta es un hallazgo de la limpieza, no de los datos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y ahora la parte que casi nadie escribe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este análisis tiene límites, y decirlos **te hace más creíble, no\n",
    "menos**. Es de las cosas que más me costó aprender.\n",
    "\n",
    "- ⚠️ **Esto no dice que la primera visita cause el problema.**\n",
    "Puede ser que a los clientes nuevos se les visite peor, o que sean negocios más\n",
    "chicos, o que el vendedor priorice a los conocidos. Correlación no es causa, y lo\n",
    "desarrollo en el\n",
    "[libro de estadística](https://missyera.com/guias/estadistica-desde-cero/).\n",
    "\n",
    "- 💸 **Los S/58.000 son la brecha, no un ahorro garantizado.**\n",
    "Cerrar una brecha entera nunca pasa. Es el tamaño del premio, y sirve para decidir\n",
    "cuánto vale la pena invertir en intentarlo.\n",
    "\n",
    "- ❓ **Falta la pregunta que los datos no contestan:** ¿por qué\n",
    "falta esa fecha? Si es porque el cliente es nuevo, todo esto vale. Si es porque\n",
    "alguien no la llena en ciertos casos, estoy midiendo otra cosa. Eso se pregunta,\n",
    "no se deduce.\n",
    "\n",
    "Y el paso siguiente honesto: **un experimento**. Elegir cien\n",
    "clientes nuevos al azar, darles un tratamiento distinto en la primera visita, y\n",
    "comparar con los otros. Eso sí responde si la causa es esa."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El cuaderno completo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Practica con estos mismos datos\n",
    "\n",
    "El cuaderno corre\n",
    "de arriba abajo en Google Colab sin instalar nada. Son las ventas de una\n",
    "distribuidora peruana con los defectos que traen los datos reales.\n",
    "\n",
    "[Descargar el cuaderno](https://missyera.com/static/cuadernos/python-desde-cero.ipynb)\n",
    "[Descargar el dataset (CSV)](https://missyera.com/static/datasets/ventas-miss-yera.csv)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Estos son distintos: no hay una sola respuesta correcta. Son las preguntas que\n",
    "te haría en una entrevista de trabajo 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Tu propia auditoría\n",
    "\n",
    "Escribe una función que reciba cualquier DataFrame y\n",
    "devuelva su ficha de calidad."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. La misma pregunta, otro corte\n",
    "\n",
    "Mira si la brecha también aparece por segmento de cliente."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. ¿Cambia con el tiempo?\n",
    "\n",
    "Comprueba si la brecha se mantiene a lo largo de los\n",
    "trimestres o es cosa de un periodo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. El contraejemplo\n",
    "\n",
    "Busca una variable donde la diferencia NO aparezca, para\n",
    "comprobar que no estás viendo patrones en todos lados."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Ponle precio a otra cosa\n",
    "\n",
    "Mira si la satisfacción también separa a los que cierran de\n",
    "los que no, usando el mismo método."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. El resumen ejecutivo\n",
    "\n",
    "Escribe una función que arme el párrafo de conclusión sola,\n",
    "con los números del momento."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Tu propio análisis de sensibilidad\n",
    "\n",
    "Recalcula la brecha cortando por canal y por segmento, y di\n",
    "si hay algún grupo donde no se cumple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 7\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. Dejarlo guardado para el yo del mes que viene\n",
    "\n",
    "Escribe el resultado en un CSV y, al lado, un archivo de\n",
    "texto que diga cómo se hizo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 8\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El análisis que nadie puede repetir, ni tú"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y para terminar el libro, la trampa que separa un análisis de una anécdota. Estas cuatro líneas corren perfectas y aun así no valen para entregar 🔁"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Terminas el análisis, sale una conclusión clara y la mandas. El código está ordenado y cada paso hace lo suyo.\n",
    "\n",
    "```\n",
    "d = pd.read_csv('ventas-miss-yera.csv')\n",
    "muestra = d.sample(500)\n",
    "\n",
    "resultado = analizar(muestra)\n",
    "print('conclusion:', resultado)\n",
    "```\n",
    "\n",
    "**¿Qué está mal?** La respuesta está en el cuaderno de soluciones. Míralo tú primero."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Te dan un CSV nuevo y quieres sacar conclusiones. ¿Qué haces primero?\n",
    "\n",
    "a) Mirar cuántas filas hay, qué falta y qué está sucio\n",
    "\n",
    "b) Hacer los gráficos, que es lo que van a mirar\n",
    "\n",
    "c) Calcular los promedios de cada columna\n",
    "\n",
    "d) Preguntar qué quieren que salga"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Terminaste el libro 🎉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "De verdad. Empezaste sin saber si Python era para ti y acabas de hacer un\n",
    "análisis completo, con auditoría, limpieza documentada, comprobaciones cruzadas,\n",
    "un número con su gráfico y sus límites bien dichos.\n",
    "\n",
    "Eso es exactamente lo que hace una analista de datos. No hay un truco más allá\n",
    "de esto: hay práctica.\n",
    "\n",
    "Lo que yo haría ahora, en este orden:\n",
    "\n",
    "- 📂 **Rehacer este análisis con un archivo tuyo.** El de tu\n",
    "trabajo, el de tu emprendimiento, el que sea. Con datos que te importan se\n",
    "aprende el triple.\n",
    "\n",
    "- 🗃️ **El [libro de SQL](https://missyera.com/guias/sql-desde-cero/)**, porque\n",
    "en una empresa los datos casi nunca llegan en CSV: están en una base y hay que ir\n",
    "por ellos.\n",
    "\n",
    "- 📐 **El [libro de\n",
    "estadística](https://missyera.com/guias/estadistica-desde-cero/)**, para saber cuándo una diferencia es real y cuándo es\n",
    "ruido. Es la que te evita las conclusiones apuradas.\n",
    "\n",
    "- 🤖 Y después el [libro de machine\n",
    "learning](https://missyera.com/guias/machine-learning-desde-cero/), cuando la pregunta pase de \"qué pasó\" a \"qué va a pasar\".\n",
    "\n",
    "Si quieres hacerlo acompañada y aplicándolo sobre tus propios datos, eso es\n",
    "justo lo que hacemos en el [Full Day IA](https://missyera.com/cursos/full-day-ia/) 💜\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un análisis que no se puede repetir no es un análisis. Es una anécdota."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Gracias por llegar hasta acá, de verdad. Me hace muy feliz que este libro\n",
    "exista y que lo estés usando.\n",
    "\n",
    "Que tengas un hermoso día! 🌟\n",
    "\n",
    "Chau, chau. Bye, bye. 🐣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ya programas. ¿Y ahora qué construyes?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python era el medio, no el objetivo. Con lo que sabes ahora puedes automatizar cosas de tu trabajo esta misma semana, y te recomiendo que empieces por ahí antes que por nada más 💪\n",
    "\n",
    "Cuando quieras que eso deje de ser un script que corres tú y pase a ser algo que opera un proceso solo, con sus pruebas y su forma de avisarte cuando se rompe, ese salto es [el curso de ingeniería de IA en producción](https://missyera.com/cursos/ingenieria-de-ia/).\n",
    "\n",
    "La diferencia entre las dos cosas es más grande de lo que parece desde acá: un script lo arreglas tú cuando falla porque estabas mirando, y un sistema tiene que avisarte solo. Eso son cuatro semanas de práctica, no un capítulo más.\n",
    "\n",
    "Y si sientes que necesitas repasar esto mismo con alguien explicándotelo, sin saltar todavía a construir sistemas, en [las clases grabadas](https://missyera.com/cursos/datos/) está el curso de Python que dicté en aula, con las preguntas que hicieron los alumnos. Son las mismas cosas que acabas de leer, dichas en voz alta 🐣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 16 de **Python desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/python-desde-cero/proyecto-final/\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
