{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Probabilidad, lo justo y necesario\n",
    "\n",
    "Condicional, independencia y Bayes. Con el error de dar la vuelta a una probabilidad, que cuesta campañas enteras.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 7 de **Estadística 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/estadistica-desde-cero/probabilidad/\n",
    "\n",
    "Este es el cuaderno de **soluciones**. Trae el código de cada ejercicio, la\n",
    "explicación de la trampa y la respuesta del quiz. Si vienes del cuaderno de\n",
    "práctica sin haberlo intentado, vuelve 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Empieza la segunda mitad del libro 🎬\n",
    "\n",
    "Hasta ahora describíamos lo que teníamos delante. A partir de aquí vamos a\n",
    "afirmar cosas sobre lo que *no* medimos, y para eso hace falta un poco de\n",
    "probabilidad.\n",
    "\n",
    "Poca, te lo prometo. Nada de urnas con bolas ni de barajas. Tres ideas, y las\n",
    "tres se calculan con la misma tabla de ventas de siempre."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "URL = 'https://missyera.com/static/datasets/ventas-miss-yera.csv'\n",
    "\n",
    "\n",
    "def carga_limpia(url):\n",
    "    \"\"\"La misma del capítulo 2.\"\"\"\n",
    "    v = pd.read_csv(url).drop_duplicates()\n",
    "    v['ciudad'] = (v['ciudad'].str.strip().str.lower()\n",
    "                   .str.normalize('NFKD')\n",
    "                   .str.encode('ascii', 'ignore').str.decode('utf-8'))\n",
    "    v['monto'] = pd.to_numeric(v['monto'].str.replace(',', '.'))\n",
    "    for col in ['fecha', 'fecha_ultima_compra']:\n",
    "        f = pd.to_datetime(v[col], format='%Y-%m-%d', errors='coerce')\n",
    "        falta = f.isna() & v[col].notna()\n",
    "        f[falta] = pd.to_datetime(v.loc[falta, col], format='%d/%m/%Y',\n",
    "                                  errors='coerce')\n",
    "        v[col] = f\n",
    "    return v\n",
    "\n",
    "\n",
    "v = carga_limpia(URL)\n",
    "\n",
    "print('P(compra)    = %.4f' % v['compro'].mean())\n",
    "print('P(mayorista) = %.4f' % (v['segmento'] == 'Mayorista').mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí tienes la única definición de probabilidad que vas a necesitar:\n",
    "**la proporción de veces que pasa algo**. Y ya sabes calcularla\n",
    "desde el capítulo 2, porque es la media de una columna de ceros y unos 😌\n",
    "\n",
    "Y dime: **¿te has creído alguna vez un resultado de laboratorio sin preguntar cuánta gente tiene esa enfermedad?** Es el mismo error que vamos a ver acá, y cuesta mucho más caro en datos 🎲"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Regla 1: la suma, con su descuento"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Cuál es la probabilidad de que una venta sea de un mayorista\n",
    "**o** venga por WhatsApp?\n",
    "\n",
    "La tentación es sumar. Vamos a ver:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mayorista = v['segmento'] == 'Mayorista'\n",
    "whatsapp = v['canal'] == 'WhatsApp'\n",
    "\n",
    "print('P(mayorista)        = %.4f' % mayorista.mean())\n",
    "print('P(whatsapp)         = %.4f' % whatsapp.mean())\n",
    "print('sumando los dos     = %.4f' % (mayorista.mean() + whatsapp.mean()))\n",
    "print('P(mayorista O whatsapp) = %.4f' % (mayorista | whatsapp).mean())\n",
    "print('lo que sobra        = %.4f' % (mayorista & whatsapp).mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sumando sale 0,4897 y la verdad es 0,4327. Sobran exactamente 0,0570, que es\n",
    "la proporción de ventas que son **las dos cosas a la vez**: un\n",
    "mayorista que compró por WhatsApp.\n",
    "\n",
    "Al sumar las contaste dos veces. Por eso la regla es restar la\n",
    "intersección 🧮"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "P(A∪B)=P(A)+P(B)−P(A∩B)\n",
    "\n",
    "sumas las dos probabilidades y le restas lo que contaste dos veces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Regla 2: la condicional, que es la que se usa de verdad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\"Dado que ya sé algo, ¿qué probabilidad hay de lo otro\". En pandas esto es\n",
    "simplemente filtrar antes de promediar:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "P(A∣B)=P(A∩B)P(B)\n",
    "\n",
    "de todo lo que cumple B, qué parte cumple también A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('P(compra)                = %.4f' % v['compro'].mean())\n",
    "print('P(compra | mayorista)    = %.4f' % v.loc[mayorista, 'compro'].mean())\n",
    "print('P(compra | whatsapp)     = %.4f' % v.loc[whatsapp, 'compro'].mean())\n",
    "print('P(compra | marketplace)  = %.4f'\n",
    "      % v.loc[v['canal'] == 'Marketplace', 'compro'].mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Saber quién es el cliente **cambia** la probabilidad: de 0,5777\n",
    "sube a 0,7240 si es mayorista y baja a 0,4613 si viene por Marketplace 📈\n",
    "\n",
    "Y eso es, literalmente, de qué va el análisis de datos: buscar el dato que,\n",
    "al conocerlo, cambia lo que esperas 🔮"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Regla 3: independencia"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos cosas son independientes si saber una no te dice nada de la otra. La\n",
    "prueba es esta: si son independientes, la probabilidad de que pasen las dos es\n",
    "el producto de las dos por separado.\n",
    "\n",
    "Probemos con ciudad, que llevamos seis capítulos sospechando que no sirve\n",
    "para nada:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for c in ['lima', 'arequipa', 'trujillo']:\n",
    "    es_ciudad = v['ciudad'] == c\n",
    "    juntas = (es_ciudad & (v['compro'] == 1)).mean()\n",
    "    producto = es_ciudad.mean() * v['compro'].mean()\n",
    "    print('%-9s juntas %.4f | producto %.4f | diferencia %+.4f'\n",
    "          % (c, juntas, producto, juntas - producto))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Prácticamente idénticas. **Ciudad y compra son independientes**:\n",
    "saber de qué ciudad es una venta no te dice nada sobre si se va a cerrar ✅\n",
    "\n",
    "Y ahora el contraste, con segmento:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "juntas = (mayorista & (v['compro'] == 1)).mean()\n",
    "producto = mayorista.mean() * v['compro'].mean()\n",
    "\n",
    "print('juntas   %.4f' % juntas)\n",
    "print('producto %.4f' % producto)\n",
    "print('la realidad es %.1f%% mayor de lo que serIa si fueran independientes'\n",
    "      % (100 * (juntas / producto - 1)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un 25,3% por encima. Aquí no hay independencia ninguna: ser mayorista y\n",
    "comprar van juntos 🤝\n",
    "\n",
    "Guarda este cálculo porque es exactamente el que hace la prueba ji cuadrado\n",
    "del capítulo 13. Lo único que añade la prueba es decirte cuánta diferencia hace\n",
    "falta para que no sea casualidad."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error que cuesta campañas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este es el importante del capítulo, y lo he visto en presentaciones de\n",
    "verdad 😖"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('P(compra | mayorista) = %.4f' % v.loc[mayorista, 'compro'].mean())\n",
    "print('P(mayorista | compra) = %.4f'\n",
    "      % (v.loc[v['compro'] == 1, 'segmento'] == 'Mayorista').mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "0,7240 y 0,3133. **Son dos números completamente distintos**, y\n",
    "en castellano se parecen tanto que se confunden todo el rato:\n",
    "\n",
    "- ✅ \"El **72,40%** de los mayoristas compra\". Verdad.\n",
    "\n",
    "- ✅ \"El **31,33%** de los que compran son mayoristas\". Verdad.\n",
    "\n",
    "- ❌ \"El 72,40% de los que compran son mayoristas\". Falso, y es lo que la\n",
    "gente entiende.\n",
    "\n",
    "Y la consecuencia es de plata. Si crees que el 72% de tus compradores son\n",
    "mayoristas, concentras el equipo comercial ahí y abandonas a los demás. Pero\n",
    "resulta que **casi siete de cada diez ventas cerradas vienen de otros\n",
    "segmentos** 💸\n",
    "\n",
    "La regla mnemotécnica: *P(A dado B) no es P(B dado A)*. Cuando leas un\n",
    "porcentaje, pregúntate siempre **sobre qué total está calculado**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bayes, que es la fórmula para darle la vuelta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El teorema de Bayes es justo eso: cómo pasar de una a la otra sin\n",
    "equivocarse."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "P(A∣B)=P(B∣A)P(A)P(B)\n",
    "\n",
    "para darle la vuelta a una condicional multiplicas por la tasa base y divides por la del otro suceso"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vamos a comprobar que funciona:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p_may = mayorista.mean()\n",
    "p_compra = v['compro'].mean()\n",
    "p_compra_dado_may = v.loc[mayorista, 'compro'].mean()\n",
    "\n",
    "bayes = p_compra_dado_may * p_may / p_compra\n",
    "\n",
    "print('con Bayes:  %.4f' % bayes)\n",
    "print('contando:   %.4f'\n",
    "      % (v.loc[v['compro'] == 1, 'segmento'] == 'Mayorista').mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Clavado 🎯\n",
    "\n",
    "Fíjate en la pieza que hace todo el trabajo: **P(A), la proporción de\n",
    "mayoristas**, que es 0,25. Como solo una de cada cuatro ventas es de un\n",
    "mayorista, por muy alta que sea su tasa de compra no pueden ser la mayoría de\n",
    "los cierres.\n",
    "\n",
    "A esa P(A) se le llama **tasa base**, y olvidarla es el error más\n",
    "famoso de toda la probabilidad."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La tasa base, con las 21 negativas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Imagina que alguien de sistemas te ofrece un detector de ventas rotas. Dice\n",
    "que acierta el 99%: si la venta está rota la detecta el 99% de las veces, y si\n",
    "está bien solo se equivoca el 1%.\n",
    "\n",
    "Suena excelente. Vamos a ver qué pasa cuando lo sueltas sobre estas ventas,\n",
    "donde ya sabemos que hay 21 rotas de 3.000:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tasa_base = (v['monto'] < 0).mean()\n",
    "sensibilidad = 0.99\n",
    "falsa_alarma = 0.01\n",
    "\n",
    "alarmas_buenas = sensibilidad * tasa_base\n",
    "alarmas_falsas = falsa_alarma * (1 - tasa_base)\n",
    "\n",
    "print('proporcion de ventas rotas: %.5f' % tasa_base)\n",
    "print('alarmas correctas por venta: %.5f' % alarmas_buenas)\n",
    "print('alarmas falsas por venta:    %.5f' % alarmas_falsas)\n",
    "print()\n",
    "print('P(rota | suena la alarma) = %.4f'\n",
    "      % (alarmas_buenas / (alarmas_buenas + alarmas_falsas)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**El 41,10%.** O sea que cuando el detector del 99% suena, se\n",
    "equivoca más veces de las que acierta 😵\n",
    "\n",
    "Y no es que el detector sea malo: es buenísimo. El problema es que hay 2.979\n",
    "ventas sanas y solo 21 rotas. El 1% de errores sobre 2.979 son 29,79 alarmas\n",
    "falsas, más que las 20,79 correctas que salen del 99% sobre 21.\n",
    "\n",
    "Esto es lo que pasa con todos los detectores de cosas raras: fraude,\n",
    "enfermedades poco frecuentes, fallos de máquina. **Cuando lo que buscas es\n",
    "raro, la mayoría de las alarmas son falsas aunque el detector sea\n",
    "buenísimo** 🚨\n",
    "\n",
    "Es también la razón por la que el libro de machine learning tiene un capítulo\n",
    "entero sobre [clases\n",
    "raras](https://missyera.com/guias/machine-learning-desde-cero/clases-raras/): el mismo problema, con modelos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error del capítulo"
   ]
  },
  {
   "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",
    "    v.groupby('segmento')['compro'].mean()['Mayoristas']\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",
    "KeyError: 'Mayoristas'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una ese de más 🤦 En el archivo el segmento se llama `Mayorista`,\n",
    "en singular.\n",
    "\n",
    "Parece tonto, y lo pongo aquí porque este error tiene una versión hermana que\n",
    "**no** da error y que es de las que más daño hacen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('mal escrito:  %.4f' % v.loc[v['segmento'] == 'Mayoristas', 'compro'].mean())\n",
    "print('bien escrito: %.4f' % v.loc[v['segmento'] == 'Mayorista', 'compro'].mean())\n",
    "print('cuantas filas trae el mal escrito:',\n",
    "      (v['segmento'] == 'Mayoristas').sum())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Con `groupby` te avisa. Con un filtro, **no**: te\n",
    "devuelve cero filas y un `nan`, tan tranquilo 😶\n",
    "\n",
    "Y si en vez de una media hubieras pedido un `sum()`, te habría\n",
    "devuelto 0,0, que parece un número de verdad y se cuela en cualquier informe.\n",
    "\n",
    "La defensa es la de siempre y cuesta un segundo: después de filtrar,\n",
    "**mira cuántas filas quedaron**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y dos más, y los dos son la regla número uno"
   ]
  },
  {
   "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",
    "    np.random.default_rng(7).choice(['si', 'no'], p=[0.7, 0.5])\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",
    "ValueError: Probabilities do not sum to 1. See Notes section of docstring for more information.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está el axioma en un mensaje de error: **las probabilidades de todo lo que puede pasar suman 1**. Un 70% y un 50% suman 120%, y no hay mundo donde eso signifique algo."
   ]
  },
  {
   "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",
    "    np.random.default_rng(7).binomial(10, 1.3)\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",
    "ValueError: p < 0, p > 1 or p is NaN\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y el otro lado de lo mismo: una probabilidad sola tampoco puede pasar de 1. Suele salir de haber guardado un porcentaje sin dividir entre cien, que es de los descuidos más caros que hay porque a veces no revienta y te deja el número mal 🎲"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Practica 💪"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. La probabilidad de compra por canal, ordenada\n",
    "\n",
    "Calcula P(compra | canal) para los cuatro canales y\n",
    "ordénalos. ¿Cuánto separa al mejor del peor?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "por_canal = v.groupby('canal')['compro'].agg(['mean', 'count']).sort_values('mean')\n",
    "por_canal.columns = ['P(compra)', 'ventas']\n",
    "\n",
    "print(por_canal.round(4))\n",
    "print()\n",
    "print('el mejor supera al peor en %.1f puntos'\n",
    "      % (100 * (por_canal['P(compra)'].max() - por_canal['P(compra)'].min())))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "           P(compra)  ventas\n",
    "canal\n",
    "Marketplace     0.4613     763\n",
    "Web             0.5808     792\n",
    "Tienda          0.6198     726\n",
    "WhatsApp        0.6551     719\n",
    "\n",
    "el mejor supera al peor en 19.4 puntos\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "19,4 puntos entre WhatsApp y Marketplace 📱\n",
    "\n",
    "Y ahí hay una decisión de negocio esperando. Pero cuidado con el salto fácil:\n",
    "esto **no** dice que mover a la gente a WhatsApp vaya a subir las\n",
    "ventas.\n",
    "\n",
    "Puede que WhatsApp cierre más porque por ahí escriben los clientes que ya\n",
    "estaban decididos, y Marketplace reciba a los que están mirando precios. En ese\n",
    "caso el canal no causa nada, solo refleja quién ya venía convencido.\n",
    "\n",
    "Distinguir eso es el capítulo 15, y es el problema más difícil de todo el\n",
    "libro 🧩"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Un test de independencia casero\n",
    "\n",
    "Escribe algo que, dada una columna categórica, te diga\n",
    "cuánto se aleja de la independencia respecto a `compro`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cuanto_depende(df, columna):\n",
    "    p_compra = df['compro'].mean()\n",
    "    peor = 0.0\n",
    "    for valor in df[columna].unique():\n",
    "        es = df[columna] == valor\n",
    "        juntas = (es & (df['compro'] == 1)).mean()\n",
    "        producto = es.mean() * p_compra\n",
    "        peor = max(peor, abs(juntas / producto - 1))\n",
    "    return peor\n",
    "\n",
    "\n",
    "for col in ['ciudad', 'categoria', 'canal', 'segmento']:\n",
    "    print('%-11s se desvia hasta %5.1f%% de la independencia'\n",
    "          % (col, 100 * cuanto_depende(v, col)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ciudad      se desvia hasta   2.5% de la independencia\n",
    "categoria   se desvia hasta   3.2% de la independencia\n",
    "canal       se desvia hasta  20.1% de la independencia\n",
    "segmento    se desvia hasta  35.1% de la independencia\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está el ranking del archivo entero, en cuatro líneas 🏆\n",
    "\n",
    "Segmento manda con un 35,1%, canal le sigue con 20,1%, y ciudad y categoría\n",
    "se quedan por debajo del 4%, o sea que son ruido.\n",
    "\n",
    "Es exactamente lo mismo que veníamos diciendo a ojo desde el capítulo 1, pero\n",
    "ahora con un número que se puede comparar entre columnas.\n",
    "\n",
    "Lo que este cálculo **no** te dice es si un 3,4% es poco porque\n",
    "no hay relación o poco porque no hay datos suficientes. Para eso hace falta el\n",
    "capítulo 13 🔬"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Dale la vuelta tú\n",
    "\n",
    "Sabemos que P(compra | WhatsApp) es 0,6551. Calcula\n",
    "P(WhatsApp | compra) con Bayes y compruébalo contando."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p_wa = whatsapp.mean()\n",
    "p_compra = v['compro'].mean()\n",
    "p_compra_dado_wa = v.loc[whatsapp, 'compro'].mean()\n",
    "\n",
    "print('P(whatsapp | compra) con Bayes: %.4f'\n",
    "      % (p_compra_dado_wa * p_wa / p_compra))\n",
    "print('contando:                       %.4f'\n",
    "      % (v.loc[v['compro'] == 1, 'canal'] == 'WhatsApp').mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "P(whatsapp | compra) con Bayes: 0.2718\n",
    "contando:                       0.2718\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El 65,51% de los que llegan por WhatsApp compran, y solo el\n",
    "**27,18%** de las compras llegan por WhatsApp 🔁\n",
    "\n",
    "Otra vez la tasa base haciendo su trabajo: WhatsApp es solo el 23,97% de las\n",
    "ventas, así que por muy bien que convierta no puede aportar la mayoría de los\n",
    "cierres.\n",
    "\n",
    "Y de aquí sale una idea de negocio que sí es defendible: *si WhatsApp\n",
    "convierte mucho mejor y solo trae una cuarta parte del tráfico, hay margen para\n",
    "mandarle más*. Eso es una hipótesis para probar, no una conclusión, pero es\n",
    "del tipo bueno 🌱"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Un detector con datos de verdad\n",
    "\n",
    "Usa la satisfacción baja (1 o 2) como alarma de \"esta\n",
    "venta no se va a cerrar\" y calcula si sirve."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "con_dato = v.dropna(subset=['satisfaccion'])\n",
    "alarma = con_dato['satisfaccion'] <= 2\n",
    "no_cerro = con_dato['compro'] == 0\n",
    "\n",
    "print('filas con satisfaccion: %d' % len(con_dato))\n",
    "print('P(alarma)               = %.4f' % alarma.mean())\n",
    "print('P(no cerro)             = %.4f' % no_cerro.mean())\n",
    "print()\n",
    "print('P(alarma | no cerro)    = %.4f' % alarma[no_cerro].mean())\n",
    "print('P(alarma | si cerro)    = %.4f' % alarma[~no_cerro].mean())\n",
    "print('P(no cerro | alarma)    = %.4f' % no_cerro[alarma].mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "filas con satisfaccion: 2769\n",
    "P(alarma)               = 0.3962\n",
    "P(no cerro)             = 0.4258\n",
    "\n",
    "P(alarma | no cerro)    = 0.4724\n",
    "P(alarma | si cerro)    = 0.3396\n",
    "P(no cerro | alarma)    = 0.5077\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vamos a leerlo como se lee un detector 🕵️\n",
    "\n",
    "Caza el 47,24% de las ventas que no se cierran, y se equivoca avisando en el\n",
    "33,96% de las que sí se cierran. O sea que distingue algo, pero poco: la\n",
    "diferencia entre las dos es de 13 puntos.\n",
    "\n",
    "Y el número que decide es el último: **cuando la alarma suena, acierta\n",
    "el 50,77% de las veces**. Prácticamente una moneda al aire 🪙\n",
    "\n",
    "Compáralo con la tasa base: sin ningún detector, si dijeras \"no se va a\n",
    "cerrar\" a todo, acertarías el 42,58%. La alarma te sube al 50,77%. Mejora ocho\n",
    "puntos, que no es nada despreciable, pero está lejísimos de \"esta venta se cae\n",
    "seguro\".\n",
    "\n",
    "Esa comparación contra la tasa base es lo que hay que exigirle a cualquier\n",
    "alerta antes de montar un proceso encima 📋"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Fabrica dos cosas independientes\n",
    "\n",
    "Añade una columna con un número al azar y comprueba que es\n",
    "independiente de la compra. Sirve para ver qué pinta tiene la independencia de\n",
    "verdad."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "generador = np.random.default_rng(7)\n",
    "v['moneda'] = generador.integers(0, 2, size=len(v))\n",
    "\n",
    "cara = v['moneda'] == 1\n",
    "juntas = (cara & (v['compro'] == 1)).mean()\n",
    "producto = cara.mean() * v['compro'].mean()\n",
    "\n",
    "print('P(cara)          = %.4f' % cara.mean())\n",
    "print('P(compra | cara) = %.4f' % v.loc[cara, 'compro'].mean())\n",
    "print('P(compra)        = %.4f' % v['compro'].mean())\n",
    "print()\n",
    "print('juntas %.4f | producto %.4f | se desvia %.2f%%'\n",
    "      % (juntas, producto, 100 * (juntas / producto - 1)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "P(cara)          = 0.5033\n",
    "P(compra | cara) = 0.5755\n",
    "P(compra)        = 0.5777\n",
    "\n",
    "juntas 0.2897 | producto 0.2908 | se desvia -0.38%\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "0,38% de desviación 🎲\n",
    "\n",
    "Y esto es lo valioso del ejercicio: **ahora sabes cuánto se desvía algo\n",
    "que es independiente de verdad**. Con 3.000 filas, el azar puro produce\n",
    "desviaciones de medio punto porcentual.\n",
    "\n",
    "Así que cuando ciudad se desvía 2,5%, ya tienes con qué compararlo: es unas\n",
    "seis veces el ruido de una moneda, pero catorce veces menos que segmento.\n",
    "\n",
    "Fabricar una columna de basura a propósito y medir cuánto se desvía es uno de\n",
    "los trucos más útiles que conozco. Te da la vara de medir del ruido con tus\n",
    "propios datos, sin teoría 📏"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Probabilidad de dos filtros a la vez\n",
    "\n",
    "¿Qué probabilidad hay de que una venta sea de un mayorista\n",
    "Y venga por WhatsApp Y se cierre? Calcúlalo directo y con la cadena de\n",
    "condicionales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "directo = (mayorista & whatsapp & (v['compro'] == 1)).mean()\n",
    "\n",
    "cadena = (mayorista.mean()\n",
    "          * whatsapp[mayorista].mean()\n",
    "          * v.loc[mayorista & whatsapp, 'compro'].mean())\n",
    "\n",
    "print('directo: %.6f' % directo)\n",
    "print('cadena:  %.6f' % cadena)\n",
    "print()\n",
    "print('son %d ventas de %d' % ((mayorista & whatsapp & (v['compro'] == 1)).sum(),\n",
    "                               len(v)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "directo: 0.046667\n",
    "cadena:  0.046667\n",
    "\n",
    "son 140 ventas de 3000\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las dos formas dan lo mismo, como tenía que ser ✅\n",
    "\n",
    "La cadena se lee de izquierda a derecha: *la probabilidad de ser\n",
    "mayorista, por la de venir por WhatsApp dado que eres mayorista, por la de\n",
    "cerrar dado que eres las dos cosas*.\n",
    "\n",
    "Parece un rodeo cuando puedes contar directo, y lo es aquí. Pero es la forma\n",
    "en que se construyen los cálculos cuando no tienes la tabla entera, solo trozos,\n",
    "y es la base de cómo funcionan los modelos que estiman probabilidades.\n",
    "\n",
    "Mira también el número de filas: **140**. Ese es el aviso de\n",
    "verdad de este ejercicio. Cada condición que añades parte la muestra, y con 140\n",
    "filas cualquier porcentaje que calcules ya tiene un margen de error gordo.\n",
    "Cuánto de gordo es el capítulo 10 📉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. La probabilidad de cerrar, por segmento\n",
    "\n",
    "Calcula qué porcentaje cierra en cada segmento y ordénalos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tabla = pd.crosstab(v['segmento'], v['compro'], normalize='index')\n",
    "print((tabla * 100).round(1).to_string())\n",
    "print()\n",
    "print('cierra mas seguido: %s' % tabla[1].idxmax())\n",
    "print('cierra menos:       %s' % tabla[1].idxmin())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "compro         0     1\n",
    "segmento\n",
    "Bodega      62.5  37.5\n",
    "Horeca      36.8  63.2\n",
    "Mayorista   27.6  72.4\n",
    "Minimarket  43.1  56.9\n",
    "\n",
    "cierra mas seguido: Mayorista\n",
    "cierra menos:       Bodega\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso es una probabilidad condicional, aunque no lo parezca 🧮\n",
    "\n",
    "El 72,4% del mayorista es **P(cierra | es mayorista)**, escrito como lo escribiría cualquiera. Y la diferencia con el 37,5% de la bodega es enorme: casi el doble.\n",
    "\n",
    "Fíjate en el `normalize='index'`, que es lo único que hay que entender de la línea: dice que cada fila sume 100, o sea que condicionas *por segmento*. Si pusieras `'columns'` estarías contestando la pregunta al revés, que es exactamente el error que este capítulo persigue 🔄"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. ¿Son independientes el canal y el cierre?\n",
    "\n",
    "Comprueba si P(Web y cierra) es igual a P(Web) por P(cierra)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p_web = (v['canal'] == 'Web').mean()\n",
    "p_cierra = v['compro'].mean()\n",
    "juntas_real = ((v['canal'] == 'Web') & (v['compro'] == 1)).mean()\n",
    "print('P(Web)           = %.4f' % p_web)\n",
    "print('P(cierra)        = %.4f' % p_cierra)\n",
    "print('si fueran independientes, P(las dos) = %.4f' % (p_web * p_cierra))\n",
    "print('P(las dos) de verdad                 = %.4f' % juntas_real)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "P(Web)           = 0.2640\n",
    "P(cierra)        = 0.5777\n",
    "si fueran independientes, P(las dos) = 0.1525\n",
    "P(las dos) de verdad                 = 0.1533\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "0,1525 contra 0,1533. Prácticamente iguales 💚\n",
    "\n",
    "O sea que el canal Web y cerrar la venta **son casi independientes**: saber que un pedido vino por Web no te dice casi nada sobre si va a cerrar. Compáralo con el ejercicio anterior, donde el segmento cambiaba la probabilidad del 37% al 72%.\n",
    "\n",
    "Esta comparación de dos números es la definición de independencia ejecutada, y es la comprobación que hay que hacer **antes** de multiplicar probabilidades. Multiplicar cosas que no son independientes es el error del capítulo 🔗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dos porcentajes del mismo canal que no dicen lo mismo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de cerrar, la trampa. Los dos números están bien calculados y la conclusión que sale de mezclarlos está muy mal 🔀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Quieres saber cuántos mayoristas van a comprar el mes que viene. Tienes las dos probabilidades por separado, así que las multiplicas.\n",
    "\n",
    "```\n",
    "p_compra   = (v['compro'] == 1).mean()          # 0.5777\n",
    "p_mayor    = (v['segmento'] == 'Mayorista').mean()  # 0.2500\n",
    "\n",
    "print(round(p_compra * p_mayor * 3000))\n",
    "# 433\n",
    "\n",
    "real = ((v['compro'] == 1) &\n",
    "        (v['segmento'] == 'Mayorista')).mean()\n",
    "print(round(real * 3000))\n",
    "# 543\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Ciento diez clientes de diferencia, un 25% por debajo 🤝\n",
    "\n",
    "Multiplicar dos probabilidades solo vale si los dos sucesos son **independientes**, o sea si saber uno no te dice nada del otro. Y aquí sí te dice: los mayoristas compran más que la media, así que \"ser mayorista\" y \"comprar\" van juntos.\n",
    "\n",
    "Lo que hay que usar es la condicional: la proporción de compra *dentro* de los mayoristas, no la del archivo entero. Con tres condiciones a la vez el error crece: multiplicando salen 104 clientes y de verdad son 140.\n",
    "\n",
    "La independencia no se supone, se comprueba. Y en datos de negocio casi nunca se cumple, porque todo está relacionado con todo: la ciudad con el canal, el canal con el segmento y el segmento con lo que compra. **Si multiplicas, di en voz alta por qué crees que son independientes**, y verás lo rápido que se te cae."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "El 72,40% de los mayoristas compra. ¿Qué porcentaje de las compras son de mayoristas?\n",
    "\n",
    "a) El 31,33%, porque los mayoristas son solo una cuarta parte de las ventas\n",
    "\n",
    "b) También el 72,40%\n",
    "\n",
    "c) No se puede saber con esa información\n",
    "\n",
    "d) Más del 72,40%, porque compran más\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Esa es la misma probabilidad al revés, y no son la misma. Pregúntate sobre qué total está calculado cada una.\n",
    "\n",
    "*c)* Sí se puede, si además sabes qué proporción de las ventas son de mayoristas.\n",
    "\n",
    "*d)* Que conviertan mejor no los hace mayoría: son el 25% de la cartera.\n",
    "\n",
    "P(A dado B) no es P(B dado A), y confundirlas cambia a quién le vendes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 🎲 Una probabilidad es la proporción de veces que pasa algo, o sea la media\n",
    "de una columna de ceros y unos.\n",
    "\n",
    "- ➕ P(A o B) no es P(A) + P(B). Hay que restar lo que se cuenta dos veces:\n",
    "aquí 0,4897 contra 0,4327 de verdad.\n",
    "\n",
    "- 🔍 La condicional es filtrar antes de promediar. P(compra) es 0,5777 y\n",
    "P(compra|mayorista) es 0,7240.\n",
    "\n",
    "- 🔀 **P(A|B) no es P(B|A).** El 72,40% de los mayoristas compra,\n",
    "pero solo el 31,33% de las compras son de mayoristas.\n",
    "\n",
    "- ⚖️ Bayes es la fórmula para darle la vuelta, y la pieza que manda es la tasa\n",
    "base.\n",
    "\n",
    "- 🚨 Un detector con 99% de acierto sobre algo que pasa el 0,70% de las veces\n",
    "acierta el 41,11% de sus alarmas.\n",
    "\n",
    "- 👻 Un filtro mal escrito no da error: devuelve cero filas y un\n",
    "`nan`. Cuenta las filas después de filtrar.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Todo porcentaje contesta a un \"dado qué\". Si no sabes cuál, no sabes qué\n",
    "te están diciendo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y esto de darle la vuelta a una condicional es exactamente lo que hace que\n",
    "un modelo con 99% de acierto sea inútil en un problema raro. Está contado en\n",
    "el [libro de machine learning](https://missyera.com/guias/machine-learning-desde-cero/) 🤖"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué viene ahora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el capítulo 8 vemos las tres distribuciones que de verdad se usan\n",
    "(binomial, Poisson y normal) y el teorema que explica por qué la campana aparece\n",
    "por todas partes aunque tus datos no sean campana 🔔"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Qué es el teorema de Bayes?La regla que te deja dar vuelta una probabilidad condicional: si sabes la probabilidad de B dado A, te da la de A dado B. Es cómo se pasa de \"el test da positivo cuando hay enfermedad\" a lo que de verdad quieres saber, que es \"hay enfermedad cuando el test da positivo\".\n",
    "\n",
    "¿Cuál es la fórmula del teorema de Bayes?La probabilidad de A dado B es igual a la probabilidad de B dado A, por la probabilidad de A, dividido entre la probabilidad de B.\n",
    "\n",
    "¿Un ejemplo del teorema de Bayes?Un test que acierta el 99% suena infalible hasta que la enfermedad la tiene una persona de cada diez mil. Ahí la mayoría de los positivos son falsos, y el teorema es lo que lo demuestra con números. Está resuelto en este capítulo.\n",
    "\n",
    "¿Qué es la probabilidad condicional?La probabilidad de algo cuando ya sabes que pasó otra cosa. Cambia el resultado más de lo que la intuición espera, y es la pieza que necesita Bayes.\n",
    "\n",
    "¿Cómo se calcula la probabilidad condicional?Se divide la probabilidad de que pasen las dos cosas entre la probabilidad de la que ya sabes que pasó. Dicho fácil: te quedas solo con los casos donde pasó la primera y cuentas dentro de esos.\n",
    "\n",
    "¿Qué es el teorema de la probabilidad total?La forma de calcular la probabilidad de algo sumando por partes: se parte el mundo en casos que no se pisan, se calcula dentro de cada uno y se suma pesando por lo que pesa cada caso. Es el denominador de Bayes.\n",
    "\n",
    "¿Cuáles son las fórmulas de probabilidad que hacen falta?Tres: la de la unión, la de la intersección y la condicional. Con esas tres y el teorema de Bayes está cubierto casi todo lo que un analista necesita."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 7 de **Estadística desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/estadistica-desde-cero/probabilidad/\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
}
