{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ¿Qué prueba uso?\n",
    "\n",
    "t, ji cuadrado, ANOVA y las no paramétricas, elegidas por el tipo de variable y comprobadas sobre las mismas ventas.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 13 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/que-prueba-uso/\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": [
    "Ya sabes qué es un valor p. Ahora toca la pregunta práctica que atasca a todo\n",
    "el mundo: **¿cuál de todas las pruebas uso?** 🧰\n",
    "\n",
    "La buena noticia es que la respuesta es casi mecánica. Depende del tipo de las\n",
    "dos variables que estés comparando, que es justo lo del capítulo 2."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Lo que comparas | Prueba | Si hay cola o pocos datos |\n",
    "|---|---|---|\n",
    "| Categórica contra categórica | Ji cuadrado | Prueba exacta de Fisher |\n",
    "| Numérica entre 2 grupos | Prueba t | Mann-Whitney |\n",
    "| Numérica entre 3 o más grupos | ANOVA | Kruskal-Wallis |\n",
    "| Numérica contra numérica | Correlación | Spearman (capítulo 15) |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vamos con las tres primeras sobre las ventas de siempre."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\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",
    "print('filas:', len(v))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y una pregunta que ordena el capítulo entero: **¿qué tipo de dato tienen las dos cosas que quieres comparar?** Con eso contestado, la prueba se elige sola y no hace falta memorizar nada 🧰"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ji cuadrado: dos categóricas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Es la prueba de la independencia del capítulo 7, con un número encima.\n",
    "Empezamos por la tabla:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tabla = pd.crosstab(v['ciudad'], v['compro'])\n",
    "print(tabla)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chi2, p, gl, esperados = stats.chi2_contingency(tabla)\n",
    "\n",
    "print('ji cuadrado: %.4f' % chi2)\n",
    "print('grados de libertad: %d' % gl)\n",
    "print('valor p: %.4f' % p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "p = 0,9813. **El 98% de los mundos sin efecto darían una tabla al menos\n",
    "tan desigual como esta** 🤷\n",
    "\n",
    "Este es el número que llevo prometiendo desde el capítulo 1, y ahí está: la\n",
    "ciudad no tiene absolutamente nada que decir sobre si una venta se cierra.\n",
    "\n",
    "Lo bonito de ji cuadrado es lo que hay dentro: compara lo observado con lo que\n",
    "saldría si fueran independientes, que es justo el producto de probabilidades del\n",
    "capítulo 7."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "χ2=∑(O−E)2E\n",
    "\n",
    "sumas, celda por celda, cuánto se aleja lo observado de lo que saldría si las dos columnas fueran independientes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(pd.DataFrame(esperados.round(1), index=tabla.index,\n",
    "                   columns=tabla.columns))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Compara con la tabla real: 232 contra 230,6, 218 contra 215,4... Todas\n",
    "pegadas. Por eso el ji cuadrado sale casi cero 🎯\n",
    "\n",
    "Ahora las cuatro categóricas del archivo, de una pasada:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for col in ['ciudad', 'categoria', 'canal', 'segmento']:\n",
    "    chi2, p, gl, esp = stats.chi2_contingency(pd.crosstab(v[col], v['compro']))\n",
    "    print('%-11s chi2 = %8.4f | p = %.4g' % (col, chi2, p))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está el archivo entero resumido en cuatro líneas 🏆 Segmento y canal\n",
    "mandan; ciudad y categoría no dicen nada.\n",
    "\n",
    "Es exactamente el mismo orden que salió en el capítulo 7 con el cálculo\n",
    "casero. Lo que añade la prueba es el \"cuánto hace falta para no ser casualidad\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y ahora lo que casi nadie hace: mirar los residuos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un ji cuadrado te dice \"aquí pasa algo\" pero no *dónde*. Para eso están\n",
    "los residuos, que miden cuánto se desvía cada celda:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tabla_seg = pd.crosstab(v['segmento'], v['compro'])\n",
    "chi2, p, gl, esp = stats.chi2_contingency(tabla_seg)\n",
    "\n",
    "residuos = (tabla_seg - esp) / np.sqrt(esp)\n",
    "print(residuos.round(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora sí se ve la película 🎬\n",
    "\n",
    "Un residuo se lee como una puntuación z: por encima de 2 en valor absoluto ya\n",
    "es notable, por encima de 3 es fuerte.\n",
    "\n",
    "- 🔴 **Bodega: +8,299** en la columna de \"no compró\". Es la celda\n",
    "que más manda de toda la tabla: hay muchísimas más bodegas que no compran de las\n",
    "que cabría esperar.\n",
    "\n",
    "- 🟢 **Mayorista: +5,273** en \"sí compró\". La otra punta.\n",
    "\n",
    "- ⚪ **Minimarket: 0,365 y -0,312**. Se comporta exactamente como\n",
    "la media. No aporta nada al resultado.\n",
    "\n",
    "O sea que ese ji cuadrado de 194,30 no viene de los cuatro segmentos: viene\n",
    "sobre todo de Bodega. Si el negocio quiere mover una aguja,\n",
    "**ahí** está la aguja 📍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ANOVA: una numérica entre varios grupos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La prueba t compara dos grupos. Con cuatro, la que toca es ANOVA:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grupos = [g['monto'].values for _, g in v.groupby('canal')]\n",
    "print('monto por canal:', stats.f_oneway(*grupos))\n",
    "\n",
    "grupos_seg = [g['monto'].values for _, g in v.groupby('segmento')]\n",
    "print('monto por segmento:', stats.f_oneway(*grupos_seg))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los dos extremos posibles, en la misma pantalla 😄\n",
    "\n",
    "Por canal, p = 0,9056: los cuatro canales facturan lo mismo por venta. Por\n",
    "segmento, p = 0,0 literal, o sea tan chico que ni se representa.\n",
    "\n",
    "Y aquí hay una pregunta que quizá te estés haciendo: si tengo cuatro grupos,\n",
    "¿por qué no hago las seis pruebas t de todos contra todos? Porque cada prueba\n",
    "tiene su 5% de falsa alarma, y seis pruebas suben eso al 26%. ANOVA hace una sola\n",
    "pregunta (\"¿hay algún grupo distinto?\") con un solo 5% 🛡️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Las no paramétricas, para cuando hay cola"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ANOVA y la t comparan medias y suponen forma acampanada. Y ya sabemos que el\n",
    "monto tiene una cola de campeonato. La versión que no supone nada compara\n",
    "posiciones en vez de valores:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('ANOVA por canal:          p = %.4f' % stats.f_oneway(*grupos).pvalue)\n",
    "print('Kruskal-Wallis por canal: p = %.4f' % stats.kruskal(*grupos).pvalue)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "0,9056 y 0,8179. Las dos dicen lo mismo, así que la cola no estaba\n",
    "molestando 👍\n",
    "\n",
    "Mi criterio, y te lo doy como criterio y no como ley: **corre las dos y\n",
    "mira si coinciden**. Cuando coinciden, reporta la paramétrica que todo el\n",
    "mundo entiende. Cuando discrepan, la cola sí importa y hay que mirar por qué."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error del capítulo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Quiero ji cuadrado sobre un cruce que no existe:"
   ]
  },
  {
   "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.chi2_contingency([[10, 5], [0, 0]])\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: The internally computed table of expected frequencies has a zero element at (np.int64(1), np.int64(0)).\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese error es de los que enseñan 🎓\n",
    "\n",
    "Ji cuadrado divide entre los valores esperados. Si una fila entera está vacía,\n",
    "su esperado es cero y la división se va al infinito. No es un capricho del\n",
    "software: la prueba **no está definida** ahí.\n",
    "\n",
    "Y hay un pariente que no da error y que hay que vigilar. La regla es que\n",
    "**ningún esperado debería bajar de 5**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chi2, p, gl, esp = stats.chi2_contingency(pd.crosstab(v['ciudad'], v['compro']))\n",
    "print('el esperado mas chico de nuestra tabla: %.1f' % esp.min())\n",
    "\n",
    "pequena = pd.crosstab(v['ciudad'], v['compro']).head(2) // 100\n",
    "print()\n",
    "print(pequena)\n",
    "chi2b, pb, glb, espb = stats.chi2_contingency(pequena)\n",
    "print('esperado mas chico: %.2f | p = %.4f' % (espb.min(), pb))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Con esperados de 1,78 la prueba corre igual y te devuelve un p tan formal como\n",
    "cualquier otro 😑 Solo que no vale nada, porque la aproximación en la que se\n",
    "basa necesita celdas con al menos cinco casos esperados.\n",
    "\n",
    "Para tablas chicas está la prueba exacta de Fisher, que calcula la\n",
    "probabilidad contando todas las tablas posibles en vez de aproximar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y dos más, y las dos se quejan de lo mismo"
   ]
  },
  {
   "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.f_oneway(v['monto'])\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: At least two samples are required; got 1.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ANOVA compara **varios** grupos, así que con uno no hay nada que comparar. Sale al pasarle la columna entera en vez de la lista de grupos, que es justo la confusión que este capítulo intenta quitar."
   ]
  },
  {
   "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.kruskal(v['monto'])\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",
    "IndexError: tuple index out of range\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y su prima no paramétrica se queja de lo mismo, aunque con un mensaje muchísimo peor: un `IndexError` que no dice nada. Guárdate esto, que vale para toda la librería: **un mensaje malo no significa un error raro**. Cuando no entiendas la queja, lo primero que hay que mirar es si le pasaste los datos con la forma que pedía 🧩"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Practica 💪"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. ¿Qué canal manda dentro del ji cuadrado?\n",
    "\n",
    "Ya sabemos que canal sale con p = 4,3e-14. Saca los\n",
    "residuos y di qué canal es el responsable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tabla_canal = pd.crosstab(v['canal'], v['compro'])\n",
    "chi2, p, gl, esp = stats.chi2_contingency(tabla_canal)\n",
    "\n",
    "print(((tabla_canal - esp) / np.sqrt(esp)).round(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "compro           0      1\n",
    "canal                    \n",
    "Marketplace  4.945 -4.228\n",
    "Tienda      -1.748  1.495\n",
    "Web         -0.136  0.116\n",
    "WhatsApp    -3.194  2.731\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Marketplace, con +4,945 en \"no compró\" 🔴\n",
    "\n",
    "Y mira Web: -0,136 y +0,116. Casi cero. Web se comporta prácticamente como el\n",
    "promedio, o sea que no aporta nada al resultado de la prueba.\n",
    "\n",
    "Eso cambia la conclusión práctica. No es que \"el canal importe\": es que\n",
    "**Marketplace convierte mal y WhatsApp convierte bien**, y los otros\n",
    "dos están en la media. Con esa frase se puede hacer algo; con \"el canal es\n",
    "significativo\", no 💼"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. La prueba de Fisher para tablas chicas\n",
    "\n",
    "Coge las ventas de un solo día y compara segmento contra\n",
    "compra con las dos pruebas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "un_dia = v[v['fecha'] == '2025-03-14']\n",
    "tabla_dia = pd.crosstab(un_dia['segmento'] == 'Mayorista', un_dia['compro'])\n",
    "\n",
    "print(tabla_dia)\n",
    "print()\n",
    "chi2, p, gl, esp = stats.chi2_contingency(tabla_dia)\n",
    "print('esperado mas chico: %.2f' % esp.min())\n",
    "print('ji cuadrado: p = %.4f' % p)\n",
    "print('Fisher:      p = %.4f' % stats.fisher_exact(tabla_dia).pvalue)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "compro    0  1\n",
    "segmento      \n",
    "False     3  1\n",
    "True      0  1\n",
    "\n",
    "esperado mas chico: 0.40\n",
    "ji cuadrado: p = 0.8195\n",
    "Fisher:      p = 0.4000\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cinco ventas ese día, y el esperado más chico es 0,40 😬\n",
    "\n",
    "Y mira los dos valores p: ji cuadrado dice **0,8195** y Fisher\n",
    "dice **0,4000**. Sobre la misma tabla de cinco filas, uno da el\n",
    "doble que el otro.\n",
    "\n",
    "Ninguno encuentra nada, que con cinco filas es la única respuesta honesta\n",
    "posible, pero fíjate en que **ji cuadrado no protestó**: corrió con\n",
    "esperados de 0,40 y devolvió un número con cuatro decimales. Fisher es el que\n",
    "hay que usar aquí, porque cuenta todas las tablas posibles en vez de aproximar.\n",
    "\n",
    "La lección no es sobre pruebas, es sobre datos: **con cinco filas no se\n",
    "hace inferencia de nada**. Y sin embargo se hace todo el tiempo, cada vez\n",
    "que alguien filtra por región, mes y categoría a la vez 🧊"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Todas contra todas, con y sin corrección\n",
    "\n",
    "Haz las seis comparaciones de segmentos dos a dos y cuenta\n",
    "cuántas salen significativas. Luego aplica la corrección de Bonferroni."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from itertools import combinations\n",
    "\n",
    "segmentos = sorted(v['segmento'].unique())\n",
    "pares = list(combinations(segmentos, 2))\n",
    "alfa_corregido = 0.05 / len(pares)\n",
    "\n",
    "print('%d comparaciones, alfa corregido = %.4f' % (len(pares), alfa_corregido))\n",
    "print()\n",
    "for a, b in pares:\n",
    "    ga = v.loc[v['segmento'] == a, 'compro']\n",
    "    gb = v.loc[v['segmento'] == b, 'compro']\n",
    "    pv = stats.ttest_ind(ga, gb, equal_var=False).pvalue\n",
    "    print('%-11s vs %-11s p = %.6f  %s %s'\n",
    "          % (a, b, pv,\n",
    "             'sig' if pv < 0.05 else '   ',\n",
    "             'sig corregido' if pv < alfa_corregido else ''))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "6 comparaciones, alfa corregido = 0.0083\n",
    "\n",
    "Bodega      vs Horeca      p = 0.000000  sig sig corregido\n",
    "Bodega      vs Mayorista   p = 0.000000  sig sig corregido\n",
    "Bodega      vs Minimarket  p = 0.000000  sig sig corregido\n",
    "Horeca      vs Mayorista   p = 0.000142  sig sig corregido\n",
    "Horeca      vs Minimarket  p = 0.011806  sig \n",
    "Mayorista   vs Minimarket  p = 0.000000  sig sig corregido\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cinco de las seis aguantan la corrección. La que se cae es justo la que\n",
    "usamos de ejemplo en el capítulo 12: Horeca contra Minimarket, con p = 0,011806,\n",
    "que queda por encima del 0,0083 corregido 🫠\n",
    "\n",
    "Con alfa de 0,05 era significativa. Con el alfa corregido de 0,0083, no.\n",
    "\n",
    "Y no hay nada nuevo en los datos: lo único que cambió es que ahora estoy\n",
    "haciendo seis preguntas en vez de una, así que exijo más evidencia para cada una.\n",
    "Bonferroni divide alfa entre el número de comparaciones, y es la corrección más\n",
    "simple que existe.\n",
    "\n",
    "Es también la más severa, y ahí está su crítica: protege tanto de falsas\n",
    "alarmas que te hace perder efectos reales. Volvemos a esto en el capítulo 17 ⚖️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Comparar la misma cosa antes y después\n",
    "\n",
    "Las pruebas de arriba comparan grupos distintos. Prueba la\n",
    "versión emparejada: parte el año y compara los mismos clientes en los dos\n",
    "tramos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mitad = v['fecha'].median()\n",
    "antes = v[v['fecha'] <= mitad].groupby('cliente_id')['monto'].mean()\n",
    "despues = v[v['fecha'] > mitad].groupby('cliente_id')['monto'].mean()\n",
    "\n",
    "comunes = antes.index.intersection(despues.index)\n",
    "a = antes[comunes]\n",
    "d = despues[comunes]\n",
    "\n",
    "print('clientes en los dos tramos: %d' % len(comunes))\n",
    "print('promedio antes:   %.2f' % a.mean())\n",
    "print('promedio despues: %.2f' % d.mean())\n",
    "print()\n",
    "print('emparejada:      p = %.4f' % stats.ttest_rel(a, d).pvalue)\n",
    "print('sin emparejar:   p = %.4f' % stats.ttest_ind(a, d).pvalue)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "clientes en los dos tramos: 507\n",
    "promedio antes:   822.34\n",
    "promedio despues: 798.75\n",
    "\n",
    "emparejada:      p = 0.4772\n",
    "sin emparejar:   p = 0.4722\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "507 clientes aparecen en los dos tramos. Promediaban 822,34 antes y 798,75\n",
    "después, y las dos pruebas dicen que no pasó nada (p = 0,4772 y p = 0,4722) 🙂\n",
    "\n",
    "Lo que quiero que veas es la diferencia entre las dos pruebas. La emparejada\n",
    "mira **el cambio de cada cliente consigo mismo**; la otra trata los\n",
    "dos tramos como si fueran gente distinta.\n",
    "\n",
    "Cuando de verdad hay un efecto, la emparejada lo detecta muchísimo mejor,\n",
    "porque se quita de encima toda la variación entre clientes (que aquí es enorme:\n",
    "un mayorista contra una bodega). Aquí dan casi lo mismo porque no hay nada que\n",
    "detectar.\n",
    "\n",
    "Regla: **si puedes emparejar, empareja**. Antes y después del\n",
    "mismo cliente, de la misma tienda, del mismo mes del año anterior 🔗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Cuando la paramétrica y la no paramétrica discrepan\n",
    "\n",
    "Fabrica el caso: coge los montos de Bodega, mételes un\n",
    "atípico enorme y mira qué prueba se deja engañar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bod = v.loc[v['segmento'] == 'Bodega', 'monto'].values\n",
    "mini = v.loc[v['segmento'] == 'Minimarket', 'monto'].values\n",
    "\n",
    "print('sin tocar nada:')\n",
    "print('  t:            p = %.3e' % stats.ttest_ind(bod, mini, equal_var=False).pvalue)\n",
    "print('  Mann-Whitney: p = %.3e' % stats.mannwhitneyu(bod, mini).pvalue)\n",
    "\n",
    "trucado = np.concatenate([bod, [500000.0]])\n",
    "print()\n",
    "print('metiendo una venta de 500000 en Bodega:')\n",
    "print('  media de Bodega pasa de %.2f a %.2f' % (bod.mean(), trucado.mean()))\n",
    "print('  t:            p = %.4f' % stats.ttest_ind(trucado, mini, equal_var=False).pvalue)\n",
    "print('  Mann-Whitney: p = %.3e' % stats.mannwhitneyu(trucado, mini).pvalue)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "sin tocar nada:\n",
    "  t:            p = 1.042e-222\n",
    "  Mann-Whitney: p = 6.808e-189\n",
    "\n",
    "metiendo una venta de 500000 en Bodega:\n",
    "  media de Bodega pasa de 178.70 a 884.66\n",
    "  t:            p = 0.5060\n",
    "  Mann-Whitney: p = 6.662e-188\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una sola fila 🤯\n",
    "\n",
    "La prueba t pasa de p = 10 elevado a -222 (o sea \"segurísimo que son\n",
    "distintos\") a **p = 0,5060**, que significa \"no distingo nada\". Una\n",
    "venta entre 708.\n",
    "\n",
    "Y mira lo que le hizo a la media: Bodega pasa de 178,70 a 884,66, o sea que\n",
    "por culpa de una fila el segmento que menos vende parece el que más.\n",
    "\n",
    "Mann-Whitney ni se inmuta: pasa de 10 elevado a -189 a 10 elevado a -188.\n",
    "Como solo mira posiciones, esa venta enorme es simplemente \"la última de la\n",
    "fila\", y da igual que valga 500.000 o 1.000.\n",
    "\n",
    "Este es el argumento entero a favor de las no paramétricas, en un ejemplo.\n",
    "Cuando sospeches de tus datos, o cuando los extremos sean de verdad,\n",
    "**usa la que no se deja arrastrar** 🛡️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Tu elector de pruebas\n",
    "\n",
    "Escribe una función que, dadas dos columnas, elija la\n",
    "prueba y la corra."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compara(df, grupo, medida):\n",
    "    niveles = df[grupo].nunique()\n",
    "    datos = [g[medida].dropna().values for _, g in df.groupby(grupo)]\n",
    "    categorica = df[medida].nunique() <= 2\n",
    "\n",
    "    if categorica:\n",
    "        chi2, pv, gl, esp = stats.chi2_contingency(pd.crosstab(df[grupo], df[medida]))\n",
    "        return 'ji cuadrado', pv, esp.min() < 5\n",
    "    if niveles == 2:\n",
    "        return 'prueba t', stats.ttest_ind(*datos, equal_var=False).pvalue, False\n",
    "    return 'ANOVA', stats.f_oneway(*datos).pvalue, False\n",
    "\n",
    "\n",
    "for grupo, medida in [('segmento', 'compro'), ('ciudad', 'compro'),\n",
    "                      ('canal', 'monto'), ('segmento', 'monto')]:\n",
    "    prueba, pv, aviso = compara(v, grupo, medida)\n",
    "    print('%-10s vs %-7s -> %-12s p = %-10.4g %s'\n",
    "          % (grupo, medida, prueba, pv, 'AVISO: celdas chicas' if aviso else ''))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "segmento   vs compro  -> ji cuadrado  p = 7.193e-42  \n",
    "ciudad     vs compro  -> ji cuadrado  p = 0.9813     \n",
    "canal      vs monto   -> ANOVA        p = 0.9056     \n",
    "segmento   vs monto   -> ANOVA        p = 0          \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuatro cruces, cuatro pruebas bien elegidas, una línea cada uno 🙌\n",
    "\n",
    "Esta función la puedes pegar en cualquier cuaderno. Y fíjate en lo que hace\n",
    "por dentro: la elección se decide mirando `nunique()`, que es\n",
    "exactamente el detector de tipos del capítulo 2.\n",
    "\n",
    "Un aviso para que no la uses a ciegas: **esto elige la prueba, no\n",
    "sustituye a pensar**. Te devuelve un p y nada más. Le falta lo que viene\n",
    "en el capítulo 14, que es lo que de verdad hay que reportar 😌"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. La paramétrica y la no paramétrica, lado a lado\n",
    "\n",
    "Corre ANOVA y Kruskal sobre segmento y sobre ciudad, y compara."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grupos = [g['monto'].values for _, g in v.groupby('segmento')]\n",
    "print('ANOVA        : p = %.6e' % stats.f_oneway(*grupos).pvalue)\n",
    "print('Kruskal      : p = %.6e' % stats.kruskal(*grupos).pvalue)\n",
    "print()\n",
    "grupos_c = [g['monto'].values for _, g in v.groupby('ciudad')]\n",
    "print('por ciudad, ANOVA  : p = %.4f' % stats.f_oneway(*grupos_c).pvalue)\n",
    "print('por ciudad, Kruskal: p = %.4f' % stats.kruskal(*grupos_c).pvalue)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ANOVA        : p = 0.000000e+00\n",
    "Kruskal      : p = 0.000000e+00\n",
    "\n",
    "por ciudad, ANOVA  : p = 0.8429\n",
    "por ciudad, Kruskal: p = 0.9901\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las dos dicen lo mismo las dos veces, y eso es tranquilizador 💚\n",
    "\n",
    "Cuando la paramétrica y la no paramétrica coinciden, la elección daba igual y puedes reportar cualquiera. Cuando **no** coinciden es cuando hay que pararse a pensar, y suele significar que los supuestos de la paramétrica no se cumplen.\n",
    "\n",
    "Y fíjate en ese `0.000000e+00` de arriba: no es que el valor p sea cero, es que es tan pequeño que no cabe en un número de computadora. Se reporta como *p < 0,001* y nunca como cero 🔬"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. Ji cuadrado sobre las cuatro categóricas\n",
    "\n",
    "Cruza cada columna categórica contra si el pedido cerró, y ordénalas por lo que separan."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for col in ['ciudad', 'canal', 'categoria', 'segmento']:\n",
    "    t = pd.crosstab(v[col], v['compro'])\n",
    "    chi, p2, _, _ = stats.chi2_contingency(t)\n",
    "    print('%-11s chi2 = %8.2f   p = %.6f' % (col, chi, p2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ciudad      chi2 =     0.73   p = 0.981325\n",
    "canal       chi2 =    65.31   p = 0.000000\n",
    "categoria   chi2 =     1.61   p = 0.806734\n",
    "segmento    chi2 =   194.30   p = 0.000000\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos separan y dos no, y no son las que uno diría 🤔\n",
    "\n",
    "El segmento arrasa, como en todo el libro. Pero mira el **canal**, con un chi cuadrado de 65,31: para el monto no separaba nada (era 1,03 veces en el capítulo 1) y para *cerrar o no cerrar* separa muchísimo.\n",
    "\n",
    "Esa es la lección escondida: **una columna puede no explicar una cosa y explicar otra**. \"¿Esta variable sirve?\" es una pregunta mal hecha si no dices para qué 🎯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La prueba equivocada sobre los datos correctos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de cerrar, la trampa. Y esta vez el resultado sale bien igual, que es lo que hace que nadie la corrija 😵"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Comparas el monto antes y después del descuento para ver cuánto se está regalando. Usas la prueba t de siempre, sale significativo, y lo das por bueno.\n",
    "\n",
    "```\n",
    "from scipy import stats\n",
    "\n",
    "print(stats.ttest_ind(antes, despues).pvalue)\n",
    "# 0.0001\n",
    "\n",
    "print(stats.ttest_rel(antes, despues).pvalue)\n",
    "# 4.4e-176\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Las dos dicen que hay diferencia, y hay 172 órdenes de magnitud entre ellas 😵\n",
    "\n",
    "Son las mismas 1.733 ventas medidas dos veces: antes del descuento y después. Están **emparejadas**, fila a fila. La prueba independiente no lo sabe, así que trata las dos columnas como si fueran dos grupos de clientes distintos y tiene que abrirse paso entre toda la variación que hay entre unas ventas y otras, que es enorme.\n",
    "\n",
    "La pareada mira solo la resta de cada fila consigo misma, que son 97,13 soles de media y muy consistente. Toda esa variación que estorbaba desaparece.\n",
    "\n",
    "Aquí las dos daban significativo y no pasó nada. Pero el caso normal es el contrario: **una diferencia real que existe y que la prueba equivocada no ve**. La pregunta que decide la prueba es siempre la misma: ¿cada fila de un lado tiene su pareja del otro?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "El ji cuadrado del segmento sale 194,30 con p = 7,2e-42. ¿Qué haces después?\n",
    "\n",
    "a) Miro los residuos para saber qué celda manda\n",
    "\n",
    "b) Reporto que el segmento es significativo y sigo\n",
    "\n",
    "c) Hago las seis pruebas de a dos para ver cuáles difieren\n",
    "\n",
    "d) Reviso si la muestra es suficiente\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Con eso nadie puede hacer nada. La prueba dice que pasa algo, no dónde.\n",
    "\n",
    "*c)* Se puede, pero antes hay algo más barato que sale de la misma tabla.\n",
    "\n",
    "*d)* Con 3.000 filas y un p de 10 elevado a -42, la muestra no es el problema.\n",
    "\n",
    "Bodega tiene un residuo de +8,299 en la celda de no compró: ahí está la aguja."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 🧰 La prueba la elige el tipo de las variables. Dos categóricas, ji cuadrado.\n",
    "Numérica entre dos grupos, t. Entre tres o más, ANOVA.\n",
    "\n",
    "- 📊 Ciudad da p = 0,9813 y categoría 0,8067: nada. Canal 4,3e-14 y segmento\n",
    "7,2e-42: todo.\n",
    "\n",
    "- 🔍 Los residuos dicen dónde está el efecto. Bodega tiene +8,299 en \"no\n",
    "compró\" y Minimarket 0,365, o sea que Minimarket no aporta nada al\n",
    "resultado.\n",
    "\n",
    "- 🛡️ ANOVA en vez de seis pruebas t porque seis pruebas al 5% suben la falsa\n",
    "alarma al 26%.\n",
    "\n",
    "- 👻 Ji cuadrado con esperados de 1,78 corre igual y devuelve un p que no vale\n",
    "nada. Con cinco filas, ji cuadrado dio 0,8195 y Fisher 0,4000 sobre la misma\n",
    "tabla.\n",
    "\n",
    "- 🪨 Una sola venta inventada llevó la prueba t de p = 10⁻²²² a p = 0,5060, y\n",
    "la media de Bodega de 178,70 a 884,66. Mann-Whitney ni se movió.\n",
    "\n",
    "- 🔗 Si puedes emparejar, empareja: la prueba emparejada se quita de encima\n",
    "toda la variación entre individuos.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La prueba no se elige por costumbre. La eligen los tipos de las dos\n",
    "variables que comparas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las cuatro pruebas de este capítulo salen de `scipy.stats`, que es\n",
    "una línea de import. Si Python todavía te da respeto, empieza por el\n",
    "[libro de Python](https://missyera.com/guias/python-desde-cero/) 🧰"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué viene ahora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Todo este capítulo devuelve valores p, y ya avisé en el 10 de que un p no\n",
    "mide importancia. En el capítulo 14 vemos lo que hay que reportar al lado:\n",
    "el tamaño del efecto y la potencia 📐"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Qué es la prueba de chi cuadrado?La prueba para saber si dos variables de categorías tienen que ver una con la otra. Canal y si compró o no, por ejemplo.\n",
    "\n",
    "¿Cuándo uso chi cuadrado y cuándo una prueba t?Chi cuadrado cuando las dos variables son categorías. La prueba t cuando comparas el promedio de un número entre dos grupos.\n",
    "\n",
    "¿Qué es ANOVA y en qué se diferencia de la prueba t?ANOVA compara promedios de tres grupos o más. La prueba t solo compara dos, y hacer muchas pruebas t seguidas es como comprar muchos boletos: alguna sale significativa por azar.\n",
    "\n",
    "¿Qué pruebas uso si mis datos no son normales?Las no paramétricas: Mann Whitney en vez de la t, Kruskal Wallis en vez de ANOVA. Trabajan sobre el orden de los datos y no piden campana."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 13 de **Estadística desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/estadistica-desde-cero/que-prueba-uso/\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
}
