{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Cuál de los tres promedios reportar\n",
    "\n",
    "El ticket promedio es 803 soles y el 65,93% de las ventas está por debajo. Quién tiene razón, y por qué.\n",
    "\n",
    "Cuaderno de práctica del capítulo 3 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/media-mediana-y-moda/\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\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: \"dmVudGFzIGNlcnJhZGFzOiAxNzMzCm1lZGlhICAgODMzLjEwCm1lZGlhbmEgNTcwLjYx\",\n",
    "    4: \"ICAgICAgICAgICAgICBtZWFuICBjb3VudApzZWdtZW50byAgICAgICAgICAgICAgICAgCkJvZGVnYSAgICAgIDE3OC43MCAgICA3MDcKTWF5b3Jpc3RhICAxOTg4LjUxICAgICA1MAoKbWVkaWEgZGUgbGFzIG1lZGlhczogMTA4My42MQptZWRpYSBkZSB2ZXJkYWQ6ICAgICAyOTguMjQ=\",\n",
    "    5: \"ZmluYWwgcmVhbDogICAgICAgICAgICAgICAgOTMuNjAKY29uIGxhIGFyaXRtZXRpY2EgKCszLjMzJSk6IDExMC4zNApjb24gbGEgZ2VvbWV0cmljYSAoLTIuMTglKTogOTMuNjA=\",\n",
    "    6: \"Y29tcHJvICAgICAgIC0+IG1lZGlhIDAuNTc3NyB8IG1lZGlhbmEgMS4wCnNhdGlzZmFjY2lvbiAtPiBtZWRpYSAzLjAwOTggfCBtZWRpYW5hIDMuMA==\",\n",
    "    7: \"bW9udG86IGxhIHTDrXBpY2EgZXMgNTMzLjYzLCBwZXJvIGVsIHByb21lZGlvIHN1YmUgYSA4MDMuNzYgKCs1MSUpIHBvciBsYSBjb2xhIGRlIGFycmliYS4gUmVwb3J0YSBsYSBtZWRpYW5hLgoKbW9udG8gZW4gQm9kZWdhOiAxNzguNzAgZGUgcHJvbWVkaW8sIHkgbGEgbWVkaWFuYSBjYXNpIGlndWFsICgxODMuMjYpLiBSZXBvcnRhIGxhIG1lZGlhLg==\",\n",
    "    8: \"bW9udG8gICAgICAgICBtZWRpYSAgIDgwMy43NiAgIG1lZGlhbmEgICA1MzMuNjMgICBzZSBsbGV2YW4gMjcwLjEzCnVuaWRhZGVzICAgICAgbWVkaWEgICAgMTEuNjAgICBtZWRpYW5hICAgIDEyLjAwICAgc2UgbGxldmFuICAtMC40MApkZXNjdWVudG8gICAgIG1lZGlhICAgICAwLjEzICAgbWVkaWFuYSAgICAgMC4xMyAgIHNlIGxsZXZhbiAgLTAuMDAKc2F0aXNmYWNjaW9uICBtZWRpYSAgICAgMy4wMSAgIG1lZGlhbmEgICAgIDMuMDAgICBzZSBsbGV2YW4gICAwLjAx\",\n",
    "    9: \"ICAgICAgICAgICAgICAgbWVhbiAgIG1lZGlhbiAgY291bnQgIHBlc2EKc2VnbWVudG8KQm9kZWdhICAgICAgIDE3OC43MCAgIDE4My4yNiAgICA3MDcgIDAuMjQKSG9yZWNhICAgICAgIDc1NS4xOCAgIDc0My4wMCAgICA3NDIgIDAuMjUKTWF5b3Jpc3RhICAgMTg1Ni4zNCAgMTg2OS4yNiAgICA3NTAgIDAuMjUKTWluaW1hcmtldCAgIDQxNC45MiAgIDQxMy43NCAgICA4MDEgIDAuMjcKCm1lZGlhIGdsb2JhbCAgICAgIDogODAzLjc2Cm1lZGlhIGRlIGxhcyBtZWRpYXM6IDgwMS4yOA==\",\n",
    "}, lenguaje=\"python\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Frase que se oye en todas las empresas: **\"nuestro ticket promedio es\n",
    "de 803 soles\"**.\n",
    "\n",
    "Vamos a ver si es verdad, y sobre todo si sirve para algo 🧐"
   ]
  },
  {
   "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('media   %.2f' % v['monto'].mean())\n",
    "print('mediana %.2f' % v['monto'].median())\n",
    "print('moda    %.2f' % v['monto'].mode()[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres medidas del mismo centro y tres respuestas distintas: 803, 533 y 204.\n",
    "Y cada una tiene su significado 👇\n",
    "\n",
    "- ➕ **La media** es lo que sale de sumar todo y repartirlo en\n",
    "partes iguales. Contesta \"si todas las ventas hubieran sido iguales, ¿de cuánto\n",
    "serían?\".\n",
    "\n",
    "- 🎯 **La mediana** es el valor de en medio cuando las ordenas.\n",
    "La mitad de las ventas está por debajo y la mitad por encima.\n",
    "\n",
    "- 🔁 **La moda** es el valor que más se repite.\n",
    "\n",
    "Y ahora la pregunta del millón: ¿cuál llevo a la reunión?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "x¯=1n∑i=1nxi\n",
    "\n",
    "sumas todos los datos y los repartes en partes iguales entre cuántos hay"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de seguir, una pregunta: **¿cuánto compra un cliente típico de tu negocio?** Si tienes el número en la cabeza, apúntalo. Al final del capítulo vas a saber si es la media o la mediana, y si esas dos se parecen 💰"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El dato que decide"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "debajo = (v['monto'] < v['monto'].mean()).mean()\n",
    "print('ventas por debajo de la media: %.2f%%' % (100 * debajo))\n",
    "print('asimetria: %.4f' % v['monto'].skew())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está 💥\n",
    "\n",
    "**El 65,93% de las ventas está por debajo del \"promedio\".**\n",
    "\n",
    "O sea que si sales de la reunión pensando \"una venta típica es de 803 soles\",\n",
    "sales pensando algo que le pasa a una minoría. Dos de cada tres ventas son más\n",
    "chicas que eso.\n",
    "\n",
    "Esa asimetría de 1,3405 es el número que lo explica. Cuando es positiva\n",
    "quiere decir que hay cola por la derecha: muchas ventas chicas y unas pocas\n",
    "enormes que empujan la media hacia arriba. La mediana ni se entera, porque a\n",
    "ella solo le importa quién está en el medio de la fila, no cuánto pesa el\n",
    "último 🧘"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Un solo dato, y mira lo que pasa"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esto es lo que más me gusta enseñar de este capítulo, porque se ve en dos\n",
    "segundos. Voy a añadir **una sola venta** de medio millón:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "con_una_mas = pd.concat([v['monto'], pd.Series([500000.0])])\n",
    "\n",
    "print('media   antes %.2f -> despues %.2f' % (v['monto'].mean(), con_una_mas.mean()))\n",
    "print('mediana antes %.2f -> despues %.2f' % (v['monto'].median(), con_una_mas.median()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una fila entre tres mil, y la media sube **166 soles**. La\n",
    "mediana se mueve **39 céntimos**.\n",
    "\n",
    "Eso es lo que quiere decir que la mediana sea *robusta*: para moverla\n",
    "hay que mover a la gente de en medio, y un dato extremo no lo consigue por muy\n",
    "extremo que sea 💪"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La media recortada, que es el término medio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hay una opción intermedia que se usa poco y está muy bien: quitar un\n",
    "porcentaje de cada punta y promediar el resto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy import stats\n",
    "\n",
    "for recorte in [0, 0.05, 0.10, 0.25]:\n",
    "    print('recortando %2.0f%% de cada lado: %.2f'\n",
    "          % (100 * recorte, stats.trim_mean(v['monto'], recorte)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mira la escalera: cuanto más recortas, más se acerca a la mediana. Con el 25%\n",
    "de cada lado sale 570,44, ya casi la mediana de 533,63.\n",
    "\n",
    "Y tiene sentido: si recortaras el 50% de cada lado no quedaría nadie salvo el\n",
    "de en medio, o sea la mediana. De hecho probemos a pasarnos:"
   ]
  },
  {
   "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",
    "    stats.trim_mean(v['monto'], 0.6)\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: Proportion too big.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese error es correcto y hasta bonito 😄 No puedes recortar el 60% de cada\n",
    "lado porque no queda nada que promediar.\n",
    "\n",
    "La media recortada es la que usan las competiciones deportivas cuando quitan\n",
    "la nota más alta y la más baja de los jueces. Sirve cuando sospechas de errores\n",
    "de captura en los extremos, pero quieres seguir usando toda la información del\n",
    "medio."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y ahora el hallazgo que no me esperaba"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hasta aquí todo iba según el manual: media 803, mediana 533, cola larga,\n",
    "usa la mediana. Pero se me ocurrió mirar lo mismo dentro de cada segmento y me\n",
    "llevé una sorpresa 👀"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(v.groupby('segmento')['monto'].agg(['mean', 'median', 'count']).round(2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Léelo despacio porque es importante: **dentro de cada segmento, la\n",
    "media y la mediana son casi iguales**.\n",
    "\n",
    "Bodega: 178,70 y 183,26. Minimarket: 414,92 y 413,74. Mayorista: 1.856,34 y\n",
    "1.869,26. En ninguno hay cola larga.\n",
    "\n",
    "Y sin embargo en el total se separan 270 soles 🤯\n",
    "\n",
    "Entonces, ¿de dónde salía la asimetría? **No de las ventas: de la\n",
    "mezcla.** Hay cuatro poblaciones muy distintas metidas en el mismo\n",
    "archivo, una que vende a 178 soles y otra que vende a 1.856. Al juntarlas, el\n",
    "montón se ve deforme aunque cada trozo sea simétrico."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(v.groupby('segmento')['monto']\n",
    "      .apply(lambda s: (s < s.mean()).mean()).round(4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Confirmado: dentro de cada segmento, alrededor del 50% de las ventas está\n",
    "por debajo de su media, que es lo que pasa cuando no hay cola. El 65,93% del\n",
    "total era un espejismo de la mezcla.\n",
    "\n",
    "Esto cambia la recomendación práctica y me parece lo más útil del capítulo:\n",
    "**antes de discutir si media o mediana, pregúntate si estás promediando\n",
    "cosas que no se parecen**. Aquí el \"ticket promedio de la empresa\" no\n",
    "existe: existen cuatro tickets promedio y son cuatro negocios distintos 🏪\n",
    "\n",
    "Esta idea, la de que un número global puede contar una historia que ningún\n",
    "grupo confirma, tiene nombre propio y su versión más salvaje sale en el\n",
    "capítulo 17: la paradoja de Simpson."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La media de las medias"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Otra trampa clásica, y esta la he visto en informes que iban a directorio.\n",
    "Cuando ya tienes una media por grupo, es tentador promediar esas medias:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "por_segmento = v.groupby('segmento')['monto'].agg(['mean', 'count'])\n",
    "\n",
    "print('media de las medias: %.2f' % por_segmento['mean'].mean())\n",
    "print('media de verdad:     %.2f' % v['monto'].mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aquí la diferencia es chica, 2,48 soles, y te voy a decir exactamente por\n",
    "qué: **porque los cuatro segmentos tienen casi el mismo número de\n",
    "filas** (707, 742, 750, 801).\n",
    "\n",
    "La media de las medias trata a los cuatro grupos como si pesaran lo mismo. Si\n",
    "Bodega tuviera 50 ventas y Mayorista 2.500, ese cálculo le daría a las 50 el\n",
    "mismo peso que a las 2.500 y el resultado sería un disparate.\n",
    "\n",
    "Lo correcto cuando los grupos son de tamaños distintos es la\n",
    "**media ponderada**, que le da a cada grupo el peso que le toca:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "x¯p=∑gngx¯g∑gng\n",
    "\n",
    "cada grupo aporta a la media según cuántas filas tiene, y no uno por grupo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ponderada = np.average(por_segmento['mean'], weights=por_segmento['count'])\n",
    "print('media ponderada: %.4f' % ponderada)\n",
    "print('media de verdad: %.4f' % v['monto'].mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Idénticas hasta el último decimal, como tenía que ser ✅"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error más caro del capítulo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y llegamos al que de verdad hace daño, porque este sí cambia decisiones de\n",
    "plata 💸\n",
    "\n",
    "En el capítulo 1 calculamos cuánto cambia la venta mensual, y salió que la\n",
    "media de los cambios era +0,31%. Suena a que el negocio crece despacito. Vamos a\n",
    "comprobarlo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mes = v.groupby(v['fecha'].dt.to_period('M'))['monto'].sum()\n",
    "r = mes.pct_change().dropna()\n",
    "\n",
    "print('media de los cambios mensuales: %+.4f%%' % (100 * r.mean()))\n",
    "print('cambio real del primer al ultimo mes: %+.4f%%'\n",
    "      % (100 * (mes.iloc[-1] / mes.iloc[0] - 1)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La media dice **+0,31% al mes**. La realidad es que el negocio\n",
    "**cayó 19,66%** 😱\n",
    "\n",
    "Y no hay ningún error de cálculo. La media aritmética de los porcentajes está\n",
    "bien calculada. Lo que pasa es que **los porcentajes no se promedian\n",
    "sumando**.\n",
    "\n",
    "Piénsalo con un ejemplo de cabeza: si algo sube 50% y luego baja 50%, la\n",
    "media aritmética dice 0%. Pero 100 sube a 150 y luego baja a 75. Perdiste el\n",
    "25%. Las subidas y las bajadas no son simétricas porque se aplican sobre bases\n",
    "distintas.\n",
    "\n",
    "Lo que hay que usar es la **media geométrica**: multiplicar en\n",
    "vez de sumar, y sacar la raíz."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "g=(∏i=1n(1+ri))1/n−1\n",
    "\n",
    "multiplicas todos los factores de crecimiento y sacas la raíz enésima, en vez de sumarlos y dividir"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "geometrica = np.prod(1 + r) ** (1 / len(r)) - 1\n",
    "\n",
    "print('media geometrica: %+.4f%%' % (100 * geometrica))\n",
    "print()\n",
    "print('primer mes:                       %.2f' % mes.iloc[0])\n",
    "print('proyectando con la aritmetica:    %.2f' % (mes.iloc[0] * (1 + r.mean()) ** len(r)))\n",
    "print('proyectando con la geometrica:    %.2f' % (mes.iloc[0] * (1 + geometrica) ** len(r)))\n",
    "print('lo que pasó de verdad:            %.2f' % mes.iloc[-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La media geométrica **reproduce el resultado real al céntimo**.\n",
    "La aritmética se equivoca en 32.237 soles 📉\n",
    "\n",
    "La regla, corta y para toda la vida: **si lo que promedias son tasas,\n",
    "porcentajes o factores de crecimiento, la media es geométrica**. Si son\n",
    "cantidades sueltas (soles, unidades, minutos), es aritmética."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cuál reportar, en una tabla"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Situación | Qué usar | Por qué |\n",
    "|---|---|---|\n",
    "| Datos simétricos, sin extremos | Media | Usa toda la información |\n",
    "| Cola larga o valores extremos | Mediana | No se mueve con un dato raro |\n",
    "| Sospecha de errores en las puntas | Media recortada | Tira las puntas y conserva el medio |\n",
    "| Grupos de tamaños distintos | Media ponderada | Cada grupo pesa lo que le toca |\n",
    "| Tasas y crecimientos | Media geométrica | Los porcentajes se multiplican |\n",
    "| Categorías | Moda | Es la única que existe |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y una recomendación que vale más que la tabla: **reporta las dos,\n",
    "media y mediana, siempre**. Cuando se parecen, no pasa nada y quedas\n",
    "bien. Cuando se separan, esa separación es en sí misma el hallazgo 🌟"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y dos más que salen al promediar"
   ]
  },
  {
   "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.average(v['monto'], weights=[1, 2, 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",
    "TypeError: Axis must be specified when shapes of a and weights differ.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La media ponderada necesita **un peso por cada valor**, y aquí hay 3.000 montos y tres pesos. Es el error de quien copia el ejemplo de la documentación sin cambiar la lista."
   ]
  },
  {
   "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')['monto'].agg('mediana')\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",
    "AttributeError: 'SeriesGroupBy' object has no attribute 'mediana'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Se llama `median`. Te lo pongo porque a mí me pasa cada dos por tres: uno escribe en español sin darse cuenta, y el mensaje no dice \"no sé qué es mediana\", dice que el objeto no tiene ese atributo, que es más confuso de lo que debería 🙃"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Practica 💪"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. El ticket promedio de verdad\n",
    "\n",
    "Calcula el ticket promedio como lo querría oír el negocio:\n",
    "solo sobre las ventas que se cerraron. Da media y mediana."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. La moda sobre una variable continua\n",
    "\n",
    "Sacamos la moda de `monto` al principio y salió\n",
    "204,47. ¿Cuántas veces aparece ese valor? ¿Sirve de algo?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. La media ponderada que devuelve nada\n",
    "\n",
    "Calcula el descuento medio ponderado por unidades, o sea\n",
    "dándole más peso a las ventas grandes. Mira bien el resultado."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. ¿Y si los grupos fueran desiguales?\n",
    "\n",
    "Vimos que la media de las medias fallaba por poco porque\n",
    "los segmentos son parecidos de tamaño. Fabrica el caso desigual: quédate con\n",
    "todas las ventas de Bodega y solo 50 de Mayorista, y compara otra vez."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Media geométrica en algo que sí crece\n",
    "\n",
    "El negocio del archivo cae. Fabrica una serie que crezca\n",
    "de verdad (100 que sube 20%, luego 30% y luego baja 40%) y comprueba con las dos\n",
    "medias cuál reconstruye el final."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. La mediana también se puede romper\n",
    "\n",
    "Llevamos medio capítulo diciendo que la mediana es\n",
    "robusta. Encuentra el caso en que no sirve: calcula media y mediana de\n",
    "`compro` y de `satisfaccion`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Tu resumen de dos líneas\n",
    "\n",
    "Escribe una función que reciba una columna y devuelva la\n",
    "frase que pondrías en un informe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 7\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. ¿En qué columnas se separan?\n",
    "\n",
    "Calcula media y mediana de las cuatro numéricas y mira en cuáles se alejan."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 8\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 9. La media de las medias no es la media\n",
    "\n",
    "Calcula el promedio del monto por segmento y después promedia esos cuatro números. Compáralo con el promedio global."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 9\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Trescientos soles entre dos formas de decir lo normal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de cerrar el capítulo, la trampa. Las dos cuentas están bien hechas y una de las dos te va a hacer quedar mal 💸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Te piden cuánto compra un cliente típico. Sacas el promedio, que es lo que significa típico, y lo mandas.\n",
    "\n",
    "```\n",
    "print(round(v['monto'].mean(), 2))\n",
    "# 803.76\n",
    "\n",
    "print(round(v['monto'].median(), 2))\n",
    "# 533.63\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",
    "Dentro de cada segmento la media y la mediana coinciden, y en el total se separan 270 soles. ¿Qué está pasando?\n",
    "\n",
    "a) Estamos promediando cuatro negocios que venden a niveles distintos\n",
    "\n",
    "b) Hay ventas atípicas que estiran la media del total\n",
    "\n",
    "c) La mediana del total está mal calculada\n",
    "\n",
    "d) Falta limpiar los datos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 📊 Media 803,76, mediana 533,63, y el 65,93% de las ventas por debajo del\n",
    "\"promedio\". Ese porcentaje es el que hay que mirar para saber si la media\n",
    "engaña.\n",
    "\n",
    "- 🪨 Una sola venta de medio millón mueve la media 166 soles y la mediana 39\n",
    "céntimos. Eso es ser robusta.\n",
    "\n",
    "- 🧩 Dentro de cada segmento la media y la mediana coinciden. La asimetría del\n",
    "total no venía de las ventas, venía de mezclar cuatro negocios distintos.\n",
    "\n",
    "- ⚖️ La media de las medias solo funciona si los grupos son del mismo tamaño.\n",
    "Con 707 bodegas y 50 mayoristas se equivocó en más del triple.\n",
    "\n",
    "- 📉 Para tasas y crecimientos, media geométrica. La aritmética proyectaba\n",
    "+0,31% mensual en un negocio que cayó 19,66%.\n",
    "\n",
    "- 🕳️ `np.average` devuelve `nan` si hay un solo nulo.\n",
    "`mean()` de pandas los ignora sin decírtelo.\n",
    "\n",
    "- ✌️ Reporta siempre las dos. Si se parecen, bien; si se separan, esa\n",
    "separación es el hallazgo.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si la media y la mediana se separan, esa separación es el hallazgo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y para calcular estos tres sobre tus propios datos, con groupby y en tres\n",
    "líneas, está el [libro de Python](https://missyera.com/guias/python-desde-cero/) 🐍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué viene ahora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El centro es media historia. En el capítulo 4 vamos a la otra mitad: cuánto\n",
    "se separan los datos de ese centro. Y ahí sale por qué dos negocios con el\n",
    "mismo promedio pueden ser completamente distintos 📏"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Cuál es la diferencia entre media y mediana?La media suma todo y divide; la mediana es el valor del medio cuando ordenas los datos. Un solo dato enorme mueve la media y no mueve la mediana, y por eso los sueldos y los tickets se reportan con mediana.\n",
    "\n",
    "¿Qué es la media geométrica y cuándo se usa?Se multiplican los valores y se saca la raíz enésima. Se usa cuando los números son tasas de crecimiento: promediar crecimientos con la media normal da un número que no existe.\n",
    "\n",
    "¿Qué es la media ponderada?Cada dato pesa distinto en el promedio. Es lo que hay que usar cuando una tienda vendió mil pedidos y otra vendió diez: si las promedias igual, la chica cuenta lo mismo que la grande.\n",
    "\n",
    "¿Cómo se calcula la mediana?Ordenas los datos y tomas el del medio. Si son pares, el promedio de los dos del medio.\n",
    "\n",
    "¿Cuál reporto, la media o la mediana?Mira el histograma. Si es simétrico dan casi lo mismo y da igual. Si tiene cola, la media se va detrás de la cola y hay que reportar la mediana, o las dos y decir por qué se separan."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 3 de **Estadística desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/estadistica-desde-cero/media-mediana-y-moda/\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
}
