{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Un intervalo de confianza para cualquier cosa\n",
    "\n",
    "El bootstrap en tres líneas, comprobado contra la fórmula que ya conoces. Y el caso donde da un número precioso y falso.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 11 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/remuestreo-y-bootstrap/\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": [
    "En el capítulo 10 aprendimos a ponerle un\n",
    "intervalo al promedio. Y quedó algo colgando que igual ni notaste 🥾\n",
    "\n",
    "Ese intervalo salió de una fórmula, y esa fórmula solo existe para el\n",
    "promedio. Para la mediana no la hay. Ni para el percentil 90, ni para la razón\n",
    "entre dos cosas, ni para casi nada de lo que te van a preguntar en una reunión de\n",
    "verdad."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from scipy import stats\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",
    "m = v['monto'].dropna()\n",
    "\n",
    "ee = m.std() / np.sqrt(len(m))\n",
    "bajo, alto = stats.t.interval(0.95, len(m) - 1, m.mean(), ee)\n",
    "print('promedio %.2f, y su intervalo va de %.2f a %.2f' % (m.mean(), bajo, alto))\n",
    "print('mediana  %.2f, y su intervalo va de ...' % m.median())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está el problema, escrito con puntos suspensivos 😅\n",
    "\n",
    "Y fíjate que la mediana es la que más falta hace aquí: 533,63 contra 803,76 de\n",
    "promedio, porque los montos tienen cola y el promedio se va para arriba. Eso ya\n",
    "lo vimos en el capítulo 3. O sea que el número que\n",
    "mejor describe estas ventas es justo el que no sabemos acompañar.\n",
    "\n",
    "Antes de seguir, piénsalo tú: **¿de dónde saldría el intervalo si\n",
    "pudieras repetir el experimento mil veces?** Guárdate esa respuesta, que\n",
    "es todo el capítulo 🐣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La idea, que es de las más bonitas que hay"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un intervalo de confianza contesta \"cuánto se movería este número si volviera\n",
    "a tomar la muestra\". El problema es que no puedes volver a tomarla: los 3.000\n",
    "pedidos son los que hay.\n",
    "\n",
    "El truco del bootstrap es descarado y funciona: **si no puedes repetir\n",
    "el experimento, repite la muestra**. Se saca de tus propios datos una\n",
    "muestra del mismo tamaño, *con reemplazo*, o sea permitiendo que una fila\n",
    "salga dos veces y otra ninguna. Eso da una muestra parecida pero no idéntica. Y\n",
    "se hace cinco mil veces.\n",
    "\n",
    "Lo de \"con reemplazo\" no es un detalle técnico, es la idea entera. Sin\n",
    "reemplazo estarías barajando las mismas 3.000 filas y saldría siempre el mismo\n",
    "número."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def bootstrap(datos, resumen, veces=5000, semilla=7):\n",
    "    \"\"\"El intervalo de lo que sea, sin una sola formula.\"\"\"\n",
    "    generador = np.random.default_rng(semilla)\n",
    "    datos = np.asarray(datos)\n",
    "    repes = [resumen(generador.choice(datos, len(datos), replace=True))\n",
    "             for _ in range(veces)]\n",
    "    return np.percentile(repes, [2.5, 97.5])\n",
    "\n",
    "\n",
    "print('la formula del promedio dice: de %.2f a %.2f' % (bajo, alto))\n",
    "print('y el bootstrap dice         : de %.2f a %.2f'\n",
    "      % tuple(bootstrap(m.values, np.mean)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Iguales hasta el primer decimal 🎯\n",
    "\n",
    "Y ese es el orden que hay que respetar siempre: **primero se comprueba\n",
    "el método donde ya sabes la respuesta, y después se usa donde no**. Si el\n",
    "bootstrap no hubiera reproducido la fórmula, no habría por qué creerle en lo que\n",
    "viene ahora.\n",
    "\n",
    "Fíjate también en lo que la función NO tiene: ninguna distribución, ninguna t\n",
    "de Student, ningún supuesto de normalidad. Solo remuestrear y mirar los\n",
    "percentiles 2,5 y 97,5, que dejan el 95% en medio."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y ahora la mediana, que era lo que no se podía"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('mediana: %.2f' % m.median())\n",
    "print('su intervalo del 95%%: de %.2f a %.2f'\n",
    "      % tuple(bootstrap(m.values, np.median)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ya está. Cambiando `np.mean` por `np.median` 💜\n",
    "\n",
    "Eso es lo que hace al bootstrap tan útil: la función no sabe ni le importa\n",
    "qué le pasas. Cualquier cosa que sepas calcular sobre una muestra, sabes\n",
    "acompañarla de su intervalo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Algo que ninguna fórmula te da"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora una de verdad, de las que te piden. Cuánto vale un punto de\n",
    "satisfacción, medido en soles de mediana:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sat = v[['monto', 'satisfaccion']].dropna()\n",
    "observada = sat['monto'].median() / sat['satisfaccion'].median()\n",
    "\n",
    "generador = np.random.default_rng(7)\n",
    "filas = np.arange(len(sat))\n",
    "repes = []\n",
    "for _ in range(2000):\n",
    "    t = sat.iloc[generador.choice(filas, len(filas), replace=True)]\n",
    "    repes.append(t['monto'].median() / t['satisfaccion'].median())\n",
    "\n",
    "print('filas con las dos columnas: %d' % len(sat))\n",
    "print('soles de mediana por punto de satisfaccion: %.4f' % observada)\n",
    "print('intervalo del 95%%: de %.4f a %.4f' % tuple(np.percentile(repes, [2.5, 97.5])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una razón entre dos medianas, con su intervalo. Búscale la fórmula a eso 🙃\n",
    "\n",
    "Y mira una cosa importante del código: se remuestrean **las filas\n",
    "enteras**, no cada columna por su lado. Si remuestrearas monto y\n",
    "satisfacción por separado romperías la relación entre las dos, que es justo lo\n",
    "que estás midiendo. Cuando el número junta dos columnas, la unidad que se\n",
    "remuestrea es la fila."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comparar dos grupos sin ninguna prueba"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y esto es lo que más te va a servir. La pregunta de siempre: ¿vende más caro\n",
    "Web o WhatsApp?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "canales = v.dropna(subset=['monto', 'canal'])\n",
    "web = canales[canales['canal'] == 'Web']['monto'].values\n",
    "wsp = canales[canales['canal'] == 'WhatsApp']['monto'].values\n",
    "\n",
    "generador = np.random.default_rng(7)\n",
    "difs = [np.median(generador.choice(web, len(web), replace=True))\n",
    "        - np.median(generador.choice(wsp, len(wsp), replace=True))\n",
    "        for _ in range(3000)]\n",
    "\n",
    "print('Web %d pedidos, WhatsApp %d' % (len(web), len(wsp)))\n",
    "print('la mediana de Web supera a la de WhatsApp en %.2f soles'\n",
    "      % (np.median(web) - np.median(wsp)))\n",
    "print('intervalo del 95%%: de %.2f a %.2f' % tuple(np.percentile(difs, [2.5, 97.5])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Doce soles de diferencia, y el intervalo va de **-45,08 a\n",
    "64,80** 🫠\n",
    "\n",
    "Cruza el cero. Con estos datos, la mediana de Web podría estar 45 soles por\n",
    "debajo de la de WhatsApp igual de bien que 65 por encima. La diferencia que\n",
    "viste no aguanta.\n",
    "\n",
    "Y esto es lo que hace el bootstrap tan honesto: no te da un sí o un no como\n",
    "una prueba de hipótesis, te da **el rango de lo que es compatible con tus\n",
    "datos**, y ahí se ve solo que doce soles no significan nada. Es la misma\n",
    "lección del capítulo 14, contada por otro camino."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si sabes calcularlo sobre una muestra, sabes ponerle un intervalo. El bootstrap no pide fórmula, pide que los datos sean una muestra de verdad."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dónde se rompe, que es lo que casi nadie cuenta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Te lo enseño porque un método que parece servir para todo da miedo, y con\n",
    "razón. Pídele al bootstrap un intervalo para el pedido **más caro**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "extremos = [np.max(np.random.default_rng(s).choice(m.values, len(m), replace=True))\n",
    "            for s in range(3000)]\n",
    "print('el pedido mas caro que existe: %.2f' % m.max())\n",
    "print('intervalo bootstrap del maximo: de %.2f a %.2f'\n",
    "      % tuple(np.percentile(extremos, [2.5, 97.5])))\n",
    "print('repeticiones que devuelven ese mismo maximo: %d de 3000'\n",
    "      % int(np.sum(np.array(extremos) == m.max())))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mira el techo del intervalo 😳\n",
    "\n",
    "Es exactamente 4.236,71, que es el máximo observado. Y no es casualidad: el\n",
    "bootstrap remuestrea de lo que hay, así que **nunca puede devolver un\n",
    "número mayor que el más grande que viste**. De hecho 1.895 de las 3.000\n",
    "repeticiones devuelven ese mismísimo valor, porque en una muestra de 3.000 con\n",
    "reemplazo es rarísimo que el más caro no salga.\n",
    "\n",
    "Y ese intervalo es falso. El pedido más caro que tu negocio puede recibir el\n",
    "mes que viene no está acotado por el más caro que recibió hasta hoy. La pregunta\n",
    "es legítima; el bootstrap no la sabe contestar.\n",
    "\n",
    "La regla, y sirve para más cosas que esta: **el bootstrap funciona para\n",
    "números que dependen de toda la muestra, y falla para los que dependen de un solo\n",
    "dato del borde**. El promedio y la mediana usan todo. El máximo, el\n",
    "mínimo y el percentil 99 con pocos datos dependen de una fila, y ahí no hay\n",
    "remuestreo que valga."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error que sale al escribirlo mal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y este error es bonito porque el que revienta es justo el concepto:"
   ]
  },
  {
   "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",
    "    generador = np.random.default_rng(7)\n",
    "    generador.choice(wsp, len(canales), replace=False)\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: Cannot take a larger sample than population when replace is False\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*No puedes tomar una muestra más grande que la población sin reemplazo.*\n",
    "Pues eso es el bootstrap dicho al revés: **solo se puede remuestrear\n",
    "porque se permite repetir filas**. Quítale el reemplazo y el método deja\n",
    "de existir 🔑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y en tu trabajo, ¿cuándo lo sacas?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Lo que te piden | Qué haría yo |\n",
    "|---|---|\n",
    "| El ticket promedio con su margen de error | La fórmula del capítulo 10. Está resuelto y es instantáneo |\n",
    "| El ticket **típico**, o sea la mediana, con su margen | Bootstrap. No hay fórmula sencilla |\n",
    "| El percentil 90 del tiempo de entrega | Bootstrap, y con cuidado: cuanto más al borde, menos fiable |\n",
    "| Cuánto sube el monto por cada punto de satisfacción | Bootstrap sobre las filas enteras, como arriba |\n",
    "| El pedido más grande que podemos esperar | Bootstrap no. Eso es teoría de valores extremos y es otro mundo |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La última fila es la que hay que saber decir en voz alta en una reunión, y es\n",
    "justo la que este capítulo mide 🔍\n",
    "\n",
    "Si quieres el original, el bootstrap lo publicó Bradley Efron en\n",
    "[Bootstrap Methods: Another Look at the\n",
    "Jackknife](https://projecteuclid.org/journals/annals-of-statistics/volume-7/issue-1/Bootstrap-Methods-Another-Look-at-the-Jackknife/10.1214/aos/1176344552.full) (1979), y scipy trae una versión con más opciones en\n",
    "[scipy.stats.bootstrap](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.bootstrap.html), que hace lo mismo\n",
    "que nuestras tres líneas y además corrige el sesgo 📄"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Un equipo quiere el intervalo de la mediana del ticket y escribe esto. Sale un intervalo tan estrecho que lo presentan como la gran precisión de sus datos.\n",
    "\n",
    "```\n",
    "repes = []\n",
    "for _ in range(5000):\n",
    "    muestra = generador.choice(montos, len(montos), replace=False)\n",
    "    repes.append(np.median(muestra))\n",
    "\n",
    "print(np.percentile(repes, [2.5, 97.5]))   # [533.63, 533.63]\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Sin `replace=True` no hay remuestreo: sacar sin reemplazo una muestra del mismo tamaño que los datos es **barajarlos**. Los 5.000 \"remuestreos\" son las mismas filas en otro orden, y la mediana de un conjunto no depende del orden, así que las 5.000 repeticiones dan exactamente el mismo número y el intervalo sale de ancho cero. Lo peor es que no da ningún error y que un intervalo estrechísimo se lee como buena noticia. La comprobación que lo caza es la del capítulo, hecha al revés: corre el bootstrap sobre el promedio y mira si reproduce la fórmula. Si te sale un intervalo de ancho cero donde la fórmula da 54 soles de ancho, algo está mal en tu código y no en tus datos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Seis. El 1 es el que quiero que hagas sí o sí, porque es la trampa de arriba\n",
    "ejecutada 💛"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Rompe el reemplazo y míralo\n",
    "\n",
    "Corre el bootstrap con `replace=False` y el mismo\n",
    "tamaño, y compara el ancho del intervalo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "generador = np.random.default_rng(7)\n",
    "mal = [np.median(generador.choice(m.values, len(m), replace=False))\n",
    "       for _ in range(200)]\n",
    "print('valores distintos que salieron:', len(set(mal)))\n",
    "print('intervalo:', np.percentile(mal, [2.5, 97.5]))\n",
    "Tiene que salir un solo valor distinto. Y una vez lo veas, ya no se te olvida\n",
    "para qué está ese `True`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. El percentil 90 del monto\n",
    "\n",
    "Ponle intervalo al percentil 90 y compáralo con el de la\n",
    "mediana."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "p90 = lambda x: np.percentile(x, 90)\n",
    "print('mediana   :', np.round(bootstrap(m.values, np.median), 2))\n",
    "print('percentil 90:', np.round(bootstrap(m.values, p90), 2))\n",
    "El del percentil 90 sale más ancho, y tiene que salir más ancho: hay menos\n",
    "datos cerca de ese punto que cerca del centro. Esa es la versión suave de lo que\n",
    "al máximo le pasa entero."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Cuántas repeticiones hacen falta\n",
    "\n",
    "Prueba con 200, 1.000, 5.000 y 20.000 y mira cuándo deja de\n",
    "moverse el intervalo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vas a ver que a partir de unos pocos miles el intervalo se estabiliza. Eso\n",
    "importa porque el bootstrap es lo único de este libro que cuesta tiempo de\n",
    "cómputo, y saber dónde parar es la diferencia entre esperar dos segundos o dos\n",
    "minutos para el mismo número ⏱️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Compáralo con la prueba del capítulo anterior\n",
    "\n",
    "Sobre Web contra WhatsApp, corre también una prueba de\n",
    "Mann-Whitney y mira si las dos dicen lo mismo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "from scipy.stats import mannwhitneyu\n",
    "print('valor p:', round(mannwhitneyu(web, wsp).pvalue, 4))\n",
    "Las dos tienen que apuntar al mismo sitio, y si no, hay que entender por qué\n",
    "antes de creerle a ninguna. La pregunta buena del ejercicio: ¿cuál de los dos\n",
    "resultados le enseñarías a alguien que no sabe estadística?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Un intervalo para una correlación\n",
    "\n",
    "Ponle intervalo a la correlación entre monto y satisfacción,\n",
    "remuestreando filas enteras."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ojo con lo que acabas de leer arriba: la unidad que se remuestrea es la fila,\n",
    "porque la correlación vive en la relación entre las dos columnas. Si las\n",
    "remuestreas por separado, la correlación se va a cero y te lo habrás cargado tú.\n",
    "Este ejercicio conecta directo con el capítulo 15."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Bootstrap con doce filas\n",
    "\n",
    "Quédate con 12 pedidos al azar y ponle intervalo al\n",
    "promedio, con bootstrap y con la fórmula de la t."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los dos salen anchísimos, y ahí está la lección: el bootstrap no crea\n",
    "información. Con doce filas hay poca, y ningún método te la va a dar. Si el\n",
    "intervalo te sale estrecho con doce datos, sospecha del código antes que\n",
    "alegrarte 🚩"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. ¿Cuántos remuestreos hacen falta de verdad?\n",
    "\n",
    "Saca el mismo intervalo del promedio con 100, con 1000 y con 10000 remuestreos, y compara los anchos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "for veces in [100, 1000, 10000]:\n",
    "    izq, der = np.percentile(bootstrap(v['monto'], np.mean, veces=veces),\n",
    "                             [2.5, 97.5])\n",
    "    print('con %6d remuestreos: de %7.2f a %7.2f, ancho %6.2f'\n",
    "          % (veces, izq, der, der - izq))\n",
    "con    100 remuestreos: de  779.02 a  823.08, ancho  44.07\n",
    "con   1000 remuestreos: de  780.37 a  828.75, ancho  48.38\n",
    "con  10000 remuestreos: de  778.36 a  829.61, ancho  51.25\n",
    "Con cien remuestreos el intervalo sale más estrecho, y eso es lo peligroso 😬\n",
    "\n",
    "Uno esperaría que pocas repeticiones dieran un resultado \"impreciso\", y lo que dan es un resultado *optimista*: los extremos de verdad son los casos raros, y con cien tiradas los casos raros casi no salen. Así que el intervalo se ve más ajustado de lo que es.\n",
    "\n",
    "**Un intervalo demasiado estrecho no se ve mal, se ve bien.** Por eso el número de remuestreos no se escoge por lo que tarda, y por eso la función trae 5000 puestos de fábrica 🔁"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. Un intervalo para cada ciudad\n",
    "\n",
    "Saca el intervalo del promedio ciudad por ciudad y mira si alguno se queda fuera de los demás."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "for ciudad, t in v.groupby('ciudad'):\n",
    "    izq, der = np.percentile(bootstrap(t['monto'], np.mean), [2.5, 97.5])\n",
    "    print('%-10s n=%4d  promedio %7.2f  de %7.2f a %7.2f'\n",
    "          % (ciudad, len(t), t['monto'].mean(), izq, der))\n",
    "arequipa   n= 546  promedio  801.92  de  744.09 a  867.10\n",
    "chiclayo   n= 510  promedio  813.91  de  750.86 a  876.31\n",
    "cusco      n= 496  promedio  780.70  de  722.27 a  842.35\n",
    "lima       n= 471  promedio  792.74  de  730.13 a  855.81\n",
    "piura      n= 506  promedio  792.16  de  734.38 a  854.71\n",
    "trujillo   n= 471  promedio  842.69  de  771.11 a  916.17\n",
    "Trujillo es la más alta y su intervalo se pisa con el de todas 🙃\n",
    "\n",
    "Trujillo promedia 842 y Cusco 780, que son 62 soles de diferencia y en una reunión suena a mucho. Pero el intervalo de Trujillo baja hasta 771 y el de Cusco sube hasta 842: se solapan enteros.\n",
    "\n",
    "**Seis intervalos que se pisan entre sí son seis ciudades que venden igual.** Esta tabla se hace en cuatro líneas y es la que te evita montar una campaña en Cusco para arreglar algo que no está roto 🗺️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Te piden el intervalo del tiempo de entrega en el percentil 99, y tienes 80 pedidos. ¿Qué haces?\n",
    "\n",
    "a) Lo calculas y avisas de que con 80 datos ese intervalo es poco fiable\n",
    "\n",
    "b) Bootstrap y ya, que sirve para cualquier cosa\n",
    "\n",
    "c) La fórmula de la t, que es más seria\n",
    "\n",
    "d) Dices que no se puede calcular\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Sirve para casi cualquier cosa. El percentil 99 de 80 datos depende prácticamente de una fila, que es el caso donde el capítulo mide que falla.\n",
    "\n",
    "*c)* La t es para el promedio. Para un percentil no aplica, y usarla ahí da un número con cara de correcto.\n",
    "\n",
    "*d)* Sí se puede, y además te lo van a pedir igual. Lo que hay que hacer es darlo diciendo lo que vale.\n",
    "\n",
    "Casi nunca la respuesta es negarse. Es dar el número con lo que sabes de su fiabilidad al lado."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 🥾 Bootstrap es remuestrear tus datos con reemplazo miles de veces y mirar\n",
    "los percentiles 2,5 y 97,5 de lo que salga.\n",
    "\n",
    "- ✅ Primero se comprueba donde ya hay fórmula: 776,94 a 830,85 contra 776,84\n",
    "a 830,68. Después se usa donde no la hay.\n",
    "\n",
    "- 🔁 Sirve para la mediana, para una razón, para una diferencia y para\n",
    "cualquier cosa que sepas calcular sobre una muestra.\n",
    "\n",
    "- 🧩 Cuando el número junta dos columnas, se remuestrean **filas\n",
    "enteras**.\n",
    "\n",
    "- 🚫 Y falla para lo que depende de un dato del borde: el intervalo del máximo\n",
    "tiene por techo el máximo que ya viste, y eso es mentira.\n",
    "\n",
    "En el capítulo 12 el valor p se calculó barajando\n",
    "etiquetas, que es este mismo truco con otra cara: cuando no hay fórmula, se\n",
    "simula. Y el vocabulario suelto está en el\n",
    "[glosario de IA](https://missyera.com/glosario-ia/) 📖\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 11 de **Estadística desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/estadistica-desde-cero/remuestreo-y-bootstrap/\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
}
