{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# La correlación que salió cero\n",
    "\n",
    "Pearson, Spearman, y la frase que hay que decir en voz alta cada vez que aparece un coeficiente.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 15 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/correlacion/\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": [
    "Última herramienta del libro, y la que más se usa mal: la\n",
    "**correlación** 📈\n",
    "\n",
    "Mide si dos variables se mueven juntas, en una escala de -1 a 1. Cerca de 1,\n",
    "cuando una sube la otra sube. Cerca de -1, cuando una sube la otra baja. Cerca de\n",
    "0, ni idea."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "r=∑(xi−x¯)(yi−y¯)∑(xi−x¯)2∑(yi−y¯)2\n",
    "\n",
    "mide si las dos variables se alejan de su media en la misma dirección, dividido entre lo que se alejan por separado"
   ]
  },
  {
   "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",
    "\n",
    "print(v[['unidades', 'monto', 'descuento', 'satisfaccion', 'compro']]\n",
    "      .corr().round(4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y ahí está el hallazgo que llevo prometiendo desde el capítulo 14 👀\n",
    "\n",
    "Y una pregunta antes de mirar ningún número: **¿qué esperarías que estuviera relacionado en tu negocio?** Escríbelo primero. Buscar después de mirar es la forma más rápida de encontrar cosas que no existen 📉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Vender más unidades no da más dinero"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r, p = stats.pearsonr(v['unidades'], v['monto'])\n",
    "\n",
    "print('correlacion unidades-monto: %.4f' % r)\n",
    "print('valor p: %.4f' % p)\n",
    "print('r al cuadrado: %.6f' % (r ** 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**0,0178.** Prácticamente cero, y con p = 0,3291, o sea que ni\n",
    "siquiera se distingue del ruido 🤯\n",
    "\n",
    "Piénsalo un segundo porque es contraintuitivo de verdad: *saber cuántas\n",
    "unidades lleva una venta no te dice absolutamente nada de cuánto dinero\n",
    "factura*.\n",
    "\n",
    "Ese **r al cuadrado** lo dice de la forma más clara que hay:\n",
    "0,000318, o sea que las unidades explican el **0,03%** de la\n",
    "variación del monto. Nada.\n",
    "\n",
    "¿Y por qué? Mira los precios:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "v['precio_unitario'] = v['monto'] / v['unidades']\n",
    "print(v.groupby('segmento')['precio_unitario'].mean().round(2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está 💡 El precio unitario va de 26,59 a 279,61 soles según el segmento.\n",
    "Diez veces de diferencia.\n",
    "\n",
    "Como cada segmento compra alrededor de las mismas 11 unidades por venta (lo\n",
    "vimos en el capítulo 4: la media era 11,60 en todos), lo que decide el monto no\n",
    "son las unidades sino **quién compra**.\n",
    "\n",
    "Y aquí viene la pregunta obligatoria: ¿será que la correlación existe dentro\n",
    "de cada segmento y se pierde al mezclar?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for seg, g in v.groupby('segmento'):\n",
    "    r, p = stats.pearsonr(g['unidades'], g['monto'])\n",
    "    print('%-11s r = %+.4f | p = %.4f | n = %d' % (seg, r, p, len(g)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pues no. Es cero también dentro de cada segmento 😅\n",
    "\n",
    "O sea que este es un cero de verdad, no un artefacto de mezclar. En estas\n",
    "ventas, las unidades y el dinero son variables independientes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La misma correlación con tres formas distintas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora el aviso que hay que tener grabado. Un coeficiente resume una nube de\n",
    "puntos en un número, y por el camino se pierde muchísimo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "generador = np.random.default_rng(7)\n",
    "n = 200\n",
    "\n",
    "x_recta = generador.uniform(0, 10, n)\n",
    "y_recta = 2 * x_recta + generador.normal(0, 4, n)\n",
    "\n",
    "x_curva = generador.uniform(-5, 5, n)\n",
    "y_curva = x_curva ** 2 + generador.normal(0, 3, n)\n",
    "\n",
    "x_nube = np.append(generador.normal(0, 1, n - 1), 15)\n",
    "y_nube = np.append(generador.normal(0, 1, n - 1), 15)\n",
    "\n",
    "print('recta con ruido:  r = %+.4f' % stats.pearsonr(x_recta, y_recta)[0])\n",
    "print('curva en U:       r = %+.4f' % stats.pearsonr(x_curva, y_curva)[0])\n",
    "print('nube + 1 atipico: r = %+.4f' % stats.pearsonr(x_nube, y_nube)[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los tres casos que hay que conocer 🎭\n",
    "\n",
    "**La curva en U da r = -0,0268**, o sea cero. Y sin embargo la\n",
    "relación es perfecta: *y* es exactamente *x* al cuadrado. Lo que\n",
    "pasa es que Pearson solo ve líneas rectas, y una U no lo es.\n",
    "\n",
    "**La nube con un atípico da r = 0,5045**, que en un informe se\n",
    "leería como \"correlación moderada\". Miremos qué pasa si quito esa fila:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('con el atipico:  r = %+.4f' % stats.pearsonr(x_nube, y_nube)[0])\n",
    "print('sin el atipico:  r = %+.4f' % stats.pearsonr(x_nube[:-1], y_nube[:-1])[0])\n",
    "print('Spearman (con el atipico): r = %+.4f' % stats.spearmanr(x_nube, y_nube)[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "De 0,5045 a -0,0917 por **una fila entre doscientas** 😱\n",
    "\n",
    "Y mira lo que hace Spearman: da -0,1160 incluso con el atípico dentro. Como\n",
    "trabaja con posiciones y no con valores, ese punto es solo \"el último de la\n",
    "fila\", igual que pasaba con Mann-Whitney en el capítulo 13."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pearson o Spearman"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "|  | Pearson | Spearman |\n",
    "|---|---|---|\n",
    "| Qué mide | Relación en línea recta | Si una sube cuando la otra sube, aunque no sea recta |\n",
    "| Usa | Los valores | Las posiciones |\n",
    "| Atípicos | Le afectan muchísimo | Casi nada |\n",
    "| Cuándo | Datos limpios y relación recta | Ante la duda, esta |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mi costumbre: **calcular las dos y comparar**. Si coinciden,\n",
    "reporta Pearson que se entiende mejor. Si discrepan mucho, hay atípicos o la\n",
    "relación no es recta, y toca mirar 👀"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La frase que hay que decir en voz alta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Correlación no implica causalidad.**\n",
    "\n",
    "Todo el mundo la sabe y casi nadie la aplica. Cuando veas dos cosas que se\n",
    "mueven juntas, hay cuatro explicaciones posibles y solo una es la que quieres:\n",
    "\n",
    "- ➡️ A causa B. Lo que esperabas.\n",
    "\n",
    "- ⬅️ B causa A. Al revés de lo que pensabas.\n",
    "\n",
    "- 🎭 Hay un C que causa las dos. El caso más común y el más traicionero.\n",
    "\n",
    "- 🎲 Casualidad. Si miraste muchas parejas, alguna sale.\n",
    "\n",
    "En este archivo tenemos un ejemplo perfecto del tercero. La correlación entre\n",
    "monto y compra es 0,1967, y podría contarse como \"las ventas grandes se cierran\n",
    "más\". Pero ya sabemos por los capítulos 3 y 13 que hay un C detrás: el\n",
    "**segmento**, que sube las dos cosas a la vez 🎭"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error del capítulo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito.** Se ejecuta dentro de un `try` para que puedas seguir con \"ejecutar todo\" y aun así ver la queja."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    stats.pearsonr(v['unidades'].head(10), v['monto'].head(5))\n",
    "except Exception as e:\n",
    "    print(f'{type(e).__name__}: {e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "ValueError: `x` and `y` must be broadcastable.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos columnas de largos distintos. Y este error es más común de lo que parece:\n",
    "sale en cuanto filtras una de las dos y la otra no 🙃\n",
    "\n",
    "Lo grave es el caso de al lado, que **no** te frena:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(stats.pearsonr(v['unidades'], v['satisfaccion']))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los 231 nulos de satisfacción otra vez, y otra vez en silencio 🕳️ Un\n",
    "`nan` en una matriz de correlaciones pasa desapercibido entre treinta\n",
    "números.\n",
    "\n",
    "Y pandas hace **una tercera cosa distinta**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('pandas .corr():  %.6f' % v['unidades'].corr(v['satisfaccion']))\n",
    "print('scipy sin nulos: %.6f'\n",
    "      % stats.pearsonr(v.dropna(subset=['satisfaccion'])['unidades'],\n",
    "                       v.dropna(subset=['satisfaccion'])['satisfaccion'])[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "pandas no devuelve `nan`: descarta las filas incompletas por su\n",
    "cuenta y te da el número, sin decírtelo.\n",
    "\n",
    "O sea que la misma pareja de columnas te da `nan` con scipy y un\n",
    "número con pandas, y ninguno de los dos te avisa de nada. El problema se ve\n",
    "entero en una matriz de correlaciones, donde **cada celda puede estar\n",
    "calculada con un número distinto de filas**. La del principio del capítulo\n",
    "mezcla celdas de 3.000 filas con celdas de 2.769 😑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y dos más, y el segundo se ve venir"
   ]
  },
  {
   "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.pearsonr(v['unidades'], v['monto'].head(10))\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: `x` and `y` must be broadcastable.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una correlación son **pares**: cada unidad va con su monto. Con 3.000 de una y 10 de la otra no hay pares que formar. Sale al filtrar una de las dos columnas y olvidarse de filtrar la otra, que es de los descuidos más comunes que hay."
   ]
  },
  {
   "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[['ciudad', 'monto']].corr(numeric_only=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: could not convert string to float: 'trujillo'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y este es el que se ve venir: la correlación de Pearson mide si dos cosas suben juntas, y \"trujillo\" no sube. Fíjate en que hubo que escribir `numeric_only=False` para llegar a verlo: por defecto pandas se salta las columnas de texto **sin decir nada**, que es cómodo y es justo lo que hace que a veces falte una columna de tu matriz y no lo notes 🚩"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Practica 💪"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. La correlación que se infla al agregar\n",
    "\n",
    "Calcula la correlación entre unidades y monto usando las\n",
    "medias de cada segmento en vez de las ventas sueltas. Compara."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "medias = v.groupby('segmento')[['unidades', 'monto']].mean()\n",
    "print(medias.round(2))\n",
    "print()\n",
    "print('con las 3000 ventas:      r = %+.4f'\n",
    "      % stats.pearsonr(v['unidades'], v['monto'])[0])\n",
    "print('con las 4 medias:         r = %+.4f'\n",
    "      % np.corrcoef(medias['unidades'], medias['monto'])[0, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "            unidades    monto\n",
    "segmento\n",
    "Bodega         11.35   178.70\n",
    "Horeca         11.54   755.18\n",
    "Mayorista      11.73  1856.34\n",
    "Minimarket     11.76   414.92\n",
    "\n",
    "con las 3000 ventas:      r = +0.0178\n",
    "con las 4 medias:         r = +0.5433\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "De 0,0178 a **0,5433** 🤯\n",
    "\n",
    "Los mismos datos. Lo único que hice fue promediar por segmento antes de\n",
    "correlacionar, y la correlación se multiplicó por treinta.\n",
    "\n",
    "Por qué pasa: al promediar, el ruido de las ventas individuales desaparece y\n",
    "quedan cuatro puntos que, por casualidad, están ordenaditos. Y con cuatro puntos\n",
    "cualquier cosa parece una tendencia.\n",
    "\n",
    "Esto tiene nombre, se llama **falacia ecológica**, y es de los\n",
    "errores más caros que hay. Consiste en sacar conclusiones sobre individuos a\n",
    "partir de datos agregados 🏘️\n",
    "\n",
    "Se ve mucho en informes por región, por sucursal o por mes: se agregan los\n",
    "datos, aparecen correlaciones preciosas, y ninguna vale para una persona\n",
    "concreta."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. ¿Se puede correlacionar algo que no es numérico?\n",
    "\n",
    "Correlaciona `compro` (que es 0/1) con el monto,\n",
    "y compara con lo que dio la prueba t."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r, p = stats.pearsonr(v['compro'], v['monto'])\n",
    "compradores = v.loc[v['compro'] == 1, 'monto']\n",
    "resto = v.loc[v['compro'] == 0, 'monto']\n",
    "\n",
    "print('correlacion compro-monto: r = %.4f | p = %.3g' % (r, p))\n",
    "print('r al cuadrado: %.4f' % (r ** 2))\n",
    "print()\n",
    "print('media si compro:    %.2f' % compradores.mean())\n",
    "print('media si no compro: %.2f' % resto.mean())\n",
    "print('prueba t: p = %.3g' % stats.ttest_ind(compradores, resto,\n",
    "                                             equal_var=False).pvalue)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "correlacion compro-monto: r = 0.1967 | p = 1.47e-27\n",
    "r al cuadrado: 0.0387\n",
    "\n",
    "media si compro:    930.24\n",
    "media si no compro: 630.77\n",
    "prueba t: p = 1.03e-28\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sí se puede, y tiene nombre propio: correlación biserial puntual 📌\n",
    "\n",
    "Lo interesante es que **la correlación y la prueba t están contestando\n",
    "la misma pregunta**. Las dos dicen que quien compra factura más: 930,24\n",
    "de media contra 630,77.\n",
    "\n",
    "Y el r al cuadrado añade lo que la prueba t no da: el monto explica solo el\n",
    "**3,87%** de si una venta se cierra. O sea que la relación es real\n",
    "(p = 10 elevado a -27) y es chiquita, que es exactamente la lección del capítulo 14 😌"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Correlación con el tiempo: ¿el negocio crece?\n",
    "\n",
    "Correlaciona el monto con el número de días desde la\n",
    "primera venta. ¿Hay tendencia?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "v['dias'] = (v['fecha'] - v['fecha'].min()).dt.days\n",
    "\n",
    "r, p = stats.pearsonr(v['dias'], v['monto'])\n",
    "print('monto vs dias: r = %+.4f | p = %.4f' % (r, p))\n",
    "\n",
    "por_mes = v.groupby(v['fecha'].dt.to_period('M'))['monto'].sum()\n",
    "meses = np.arange(len(por_mes))\n",
    "r2, p2 = stats.pearsonr(meses, por_mes.values)\n",
    "print('total mensual vs mes: r = %+.4f | p = %.4f' % (r2, p2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "monto vs dias: r = -0.0018 | p = 0.9208\n",
    "total mensual vs mes: r = -0.0420 | p = 0.8685\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las dos formas dicen lo mismo: cero 👀\n",
    "\n",
    "Venta a venta, r = -0,0018 con p = 0,9208. Mes a mes, r = -0,0420 con p =\n",
    "0,8685. No hay ninguna tendencia en año y medio.\n",
    "\n",
    "Y ojo con este resultado, porque en el capítulo 3 el negocio \"cayó 19,66%\"\n",
    "del primer al último mes. Las dos cosas son ciertas a la vez: hubo una caída\n",
    "entre esos dos meses concretos, y no hay tendencia en el periodo.\n",
    "\n",
    "La diferencia es que comparar el primero con el último usa dos puntos y la\n",
    "correlación usa los dieciocho. Cuando dos números honestos discrepan,\n",
    "**casi siempre es que uno usa más datos que el otro** 📉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Cuántas correlaciones falsas salen por mirar de más\n",
    "\n",
    "Genera veinte columnas al azar y mira todas las parejas.\n",
    "¿Cuántas salen significativas?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from itertools import combinations\n",
    "\n",
    "basura = pd.DataFrame({'c%d' % i: generador.normal(size=len(v)) for i in range(20)})\n",
    "parejas = list(combinations(basura.columns, 2))\n",
    "significativas = []\n",
    "\n",
    "for a, b in parejas:\n",
    "    r, p = stats.pearsonr(basura[a], basura[b])\n",
    "    if p < 0.05:\n",
    "        significativas.append((a, b, r, p))\n",
    "\n",
    "print('parejas revisadas: %d' % len(parejas))\n",
    "print('significativas:    %d' % len(significativas))\n",
    "for a, b, r, p in significativas:\n",
    "    print('  %s vs %s: r = %+.4f | p = %.4f' % (a, b, r, p))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "parejas revisadas: 190\n",
    "significativas:    5\n",
    "  c5 vs c14: r = +0.0364 | p = 0.0461\n",
    "  c8 vs c12: r = -0.0401 | p = 0.0279\n",
    "  c11 vs c16: r = +0.0556 | p = 0.0023\n",
    "  c11 vs c19: r = -0.0467 | p = 0.0105\n",
    "  c15 vs c16: r = +0.0363 | p = 0.0467\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cinco correlaciones \"significativas\" entre columnas de ruido puro 🗑️\n",
    "\n",
    "190 parejas al 5% dan unas 9,5 falsas alarmas esperadas, y salieron 5. Del\n",
    "orden previsto.\n",
    "\n",
    "Ahora imagina una tabla real con 30 columnas: son 435 parejas, o sea unas 22\n",
    "correlaciones falsas esperadas. Si alguien te trae \"las correlaciones más\n",
    "fuertes que encontré en los datos\", **esto es lo primero que hay que\n",
    "preguntar**: ¿cuántas miraste? 🔍\n",
    "\n",
    "Fíjate también en los valores de r: 0,0364, -0,0401, 0,0556, -0,0467 y\n",
    "0,0363. Son minúsculos. Con 3.000 filas hasta un ruido de 0,04 sale\n",
    "significativo, que es la lección del capítulo 14 otra vez."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Pearson contra Spearman en datos de verdad\n",
    "\n",
    "Calcula las dos matrices sobre las columnas numéricas y\n",
    "busca dónde discrepan más."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cols = ['unidades', 'monto', 'descuento', 'compro']\n",
    "pearson = v[cols].corr()\n",
    "spearman = v[cols].corr('spearman')\n",
    "\n",
    "for a, b in combinations(cols, 2):\n",
    "    pr, sp = pearson.loc[a, b], spearman.loc[a, b]\n",
    "    marca = '  <-- discrepan' if abs(pr - sp) > 0.03 else ''\n",
    "    print('%-10s vs %-12s pearson %+.4f | spearman %+.4f%s'\n",
    "          % (a, b, pr, sp, marca))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "unidades   vs monto        pearson +0.0178 | spearman +0.0224\n",
    "unidades   vs descuento    pearson -0.0078 | spearman -0.0051\n",
    "unidades   vs compro       pearson +0.0167 | spearman +0.0217\n",
    "monto      vs descuento    pearson -0.0265 | spearman -0.0201\n",
    "monto      vs compro       pearson +0.1967 | spearman +0.2351  <-- discrepan\n",
    "descuento  vs compro       pearson +0.0520 | spearman +0.0526\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La única que discrepa de verdad es monto contra compro: 0,1967 y\n",
    "0,2351 👀\n",
    "\n",
    "Spearman sale más alta, y eso tiene una lectura concreta: la relación existe\n",
    "pero **no es una línea recta**. Si lo fuera, las dos darían lo\n",
    "mismo.\n",
    "\n",
    "Y tiene sentido con lo que ya sabemos. La relación no es \"más soles, más\n",
    "probable que compre\" de forma proporcional: es que los mayoristas compran mucho\n",
    "y cierran mucho, así que la relación va a saltos por segmento.\n",
    "\n",
    "Cuando Spearman le gana a Pearson, casi siempre es esto: hay relación pero es\n",
    "curva o escalonada 🪜"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Tu chequeo de correlación honesto\n",
    "\n",
    "Escribe una función que calcule una correlación y avise de\n",
    "las tres cosas que pueden estar pasando."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def correlacion_honesta(x, y, nombre):\n",
    "    juntos = pd.DataFrame({'x': x, 'y': y}).dropna()\n",
    "    pr, pp = stats.pearsonr(juntos['x'], juntos['y'])\n",
    "    sp, spp = stats.spearmanr(juntos['x'], juntos['y'])\n",
    "\n",
    "    avisos = []\n",
    "    if len(juntos) < len(x):\n",
    "        avisos.append('se descartaron %d filas' % (len(x) - len(juntos)))\n",
    "    if abs(pr - sp) > 0.1:\n",
    "        avisos.append('pearson y spearman discrepan: mira atipicos o curvas')\n",
    "    if abs(pr) < 0.1 and pp < 0.05:\n",
    "        avisos.append('significativa pero minuscula')\n",
    "\n",
    "    return ('%-22s pearson %+.4f | spearman %+.4f | n=%d %s'\n",
    "            % (nombre, pr, sp, len(juntos),\n",
    "               '| ' + '; '.join(avisos) if avisos else ''))\n",
    "\n",
    "\n",
    "print(correlacion_honesta(v['unidades'], v['monto'], 'unidades vs monto'))\n",
    "print(correlacion_honesta(v['unidades'], v['satisfaccion'], 'unidades vs satisfaccion'))\n",
    "print(correlacion_honesta(pd.Series(x_nube), pd.Series(y_nube), 'la nube con atipico'))\n",
    "print(correlacion_honesta(v['monto'], v['compro'], 'monto vs compro'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "unidades vs monto      pearson +0.0178 | spearman +0.0224 | n=3000\n",
    "unidades vs satisfaccion pearson +0.0000 | spearman +0.0046 | n=2769 | se descartaron 231 filas\n",
    "la nube con atipico    pearson +0.5045 | spearman -0.1160 | n=200 | pearson y spearman discrepan: mira atipicos o curvas\n",
    "monto vs compro        pearson +0.1967 | spearman +0.2351 | n=3000\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los tres avisos funcionando 🎉\n",
    "\n",
    "El segundo caza las 231 filas descartadas en silencio, que era el problema de\n",
    "la matriz del principio del capítulo.\n",
    "\n",
    "El tercero caza el atípico que fabricaba una correlación de 0,5045 donde no\n",
    "había nada.\n",
    "\n",
    "Esta función es lo que a mí me gustaría que devolviera `.corr()`\n",
    "por defecto: el número, sí, pero con lo que hizo falta suponer para\n",
    "calcularlo 😌"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Pearson y Spearman, la matriz completa\n",
    "\n",
    "Saca las dos matrices de correlación de las cuatro numéricas y compáralas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "num = v[['unidades', 'monto', 'descuento', 'satisfaccion']].dropna()\n",
    "print('Pearson:')\n",
    "print(num.corr().round(3).to_string())\n",
    "print()\n",
    "print('Spearman:')\n",
    "print(num.corr(method='spearman').round(3).to_string())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Pearson:\n",
    "              unidades  monto  descuento  satisfaccion\n",
    "unidades         1.000  0.006     -0.004        -0.024\n",
    "monto            0.006  1.000     -0.024        -0.010\n",
    "descuento       -0.004 -0.024      1.000        -0.019\n",
    "satisfaccion    -0.024 -0.010     -0.019         1.000\n",
    "\n",
    "Spearman:\n",
    "              unidades  monto  descuento  satisfaccion\n",
    "unidades         1.000  0.013     -0.001        -0.019\n",
    "monto            0.013  1.000     -0.020        -0.006\n",
    "descuento       -0.001 -0.020      1.000        -0.019\n",
    "satisfaccion    -0.019 -0.006     -0.019         1.000\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Doce correlaciones y ninguna pasa de 0,03 😐\n",
    "\n",
    "Es un resultado aburridísimo y quiero que lo veas entero, porque los libros suelen enseñar solo las matrices bonitas. **En estos datos las numéricas no tienen nada que ver entre ellas.**\n",
    "\n",
    "Y fíjate en que Pearson y Spearman coinciden hasta el segundo decimal. Cuando se separan mucho es señal de que hay una relación que no es recta, y aquí no la hay porque no hay ninguna relación 🤷‍♀️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. La correlación dentro de cada segmento\n",
    "\n",
    "Calcula la correlación entre unidades y monto dentro de cada segmento y compárala con la global."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('%-12s %8s %8s' % ('segmento', 'Pearson', 'n'))\n",
    "for seg, t in v.groupby('segmento'):\n",
    "    t = t[['unidades', 'monto']].dropna()\n",
    "    print('%-12s %8.3f %8d' % (seg, t['unidades'].corr(t['monto']), len(t)))\n",
    "print()\n",
    "todo = v[['unidades', 'monto']].dropna()\n",
    "print('%-12s %8.3f %8d' % ('TODOS', todo['unidades'].corr(todo['monto']), len(todo)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "segmento      Pearson        n\n",
    "Bodega          0.016      707\n",
    "Horeca         -0.016      742\n",
    "Mayorista       0.030      750\n",
    "Minimarket     -0.011      801\n",
    "\n",
    "TODOS           0.018     3000\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cero por dentro y cero por fuera 🤓\n",
    "\n",
    "Este ejercicio es el control del capítulo 17: ahí vas a ver casos donde una correlación cambia de signo al partir por grupos, que es la paradoja de Simpson. Aquí no pasa nada de eso, y saberlo también vale.\n",
    "\n",
    "La costumbre que quiero dejarte: **cuando reportes una correlación, mírala también dentro de los grupos**. Cuesta tres líneas y es lo que te salva de la paradoja 🔍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La correlación que apareció al promediar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de cerrar, una trampa que se comete casi siempre con la mejor intención del mundo: la de querer quitarle ruido a los datos 🙃"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Buscas qué explica lo que factura un cliente. Corres la matriz de correlaciones y aparece una de 0,99. Encontraste el motor del negocio.\n",
    "\n",
    "```\n",
    "compraron = v[v['compro'] == 1]\n",
    "print(round(compraron[['monto',\n",
    "                       'monto_final_facturado']].corr().iloc[0, 1], 4))\n",
    "# 0.9899\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Claro que correlacionan: **son la misma columna** 🪞 `monto_final_facturado` es `monto` menos el descuento. Compruébalo tú: `monto * (1 - descuento)` reproduce la segunda columna con medio céntimo de error.\n",
    "\n",
    "Una correlación así de alta entre dos columnas de negocio casi nunca es un hallazgo. Es un aviso de que una sale de la otra, o de que las dos salen de una tercera. Y el 0,99 se ve tan bonito que se pega a una diapositiva sin que nadie pregunte de dónde sale cada columna.\n",
    "\n",
    "Y esto no es solo un descubrimiento vacío: si metes las dos en un modelo, los coeficientes se vuelven locos y dejan de significar nada, que es lo que en machine learning se llama fuga.\n",
    "\n",
    "Antes de celebrar una correlación alta, pregúntate cómo se calculó cada columna. **Si una se puede escribir en función de la otra, no descubriste nada**: comprobaste una resta."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Unidades y monto tienen correlación 0,0178 en las 3.000 ventas, y 0,5433 promediando por segmento. ¿Cuál reportas?\n",
    "\n",
    "a) La de las 3.000 ventas, y aviso de que agregar infla la correlación\n",
    "\n",
    "b) La de 0,5433, que usa datos más limpios\n",
    "\n",
    "c) El promedio de las dos\n",
    "\n",
    "d) Ninguna, porque se contradicen\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Más limpios sí, pero son cuatro puntos. Con cuatro puntos cualquier cosa parece una tendencia.\n",
    "\n",
    "*c)* Promediar dos números que miden cosas distintas no da un número que mida nada.\n",
    "\n",
    "*d)* No se contradicen: contestan preguntas distintas. Una es sobre ventas y la otra sobre segmentos.\n",
    "\n",
    "Sacar conclusiones sobre individuos a partir de datos agregados se llama falacia ecológica."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 📉 Unidades y monto tienen r = 0,0178, con r² de 0,000318. Vender más\n",
    "unidades no da más dinero, porque el precio unitario va de 26,59 a 279,61 soles\n",
    "según el segmento.\n",
    "\n",
    "- 🎭 Pearson solo ve líneas rectas. Una curva en U perfecta dio r = -0,0268.\n",
    "\n",
    "- 🪨 Un solo dato entre 200 llevó la correlación de -0,0917 a +0,5045. Spearman\n",
    "ni se enteró.\n",
    "\n",
    "- 🏘️ Agregar infla: los mismos datos dieron 0,0178 por venta y 0,5433\n",
    "promediando por segmento. Es la falacia ecológica.\n",
    "\n",
    "- 🔍 190 parejas de columnas de ruido dieron 5 correlaciones significativas.\n",
    "Siempre hay que preguntar cuántas se miraron.\n",
    "\n",
    "- 🗣️ Correlación no implica causalidad, y el caso más común es que haya un\n",
    "tercero causando las dos. Aquí el segmento sube el monto y la compra a la vez.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La correlación mide si dos cosas se mueven juntas. Nunca dice cuál mueve\n",
    "a cuál."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y si de acá quieres pasar a predecir de verdad en vez de solo describir, ese\n",
    "es el salto que hace el [libro de\n",
    "machine learning](https://missyera.com/guias/machine-learning-desde-cero/) 🤖"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué viene ahora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ya tienes todas las herramientas. En el capítulo 17 juntamos los errores que\n",
    "invalidan un análisis entero, varios de los cuales fueron saliendo por el camino,\n",
    "y le ponemos nombre al más famoso de todos: la paradoja de Simpson 🎩"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Qué es el coeficiente de correlación?Un número entre menos uno y uno que dice cuánto se mueven dos variables juntas. Cero no significa que no haya relación: significa que no hay relación en línea recta.\n",
    "\n",
    "¿Qué diferencia hay entre Pearson y Spearman?Pearson mide relación en línea recta y se lo comen los datos atípicos. Spearman trabaja sobre el orden de los datos, así que aguanta los atípicos y detecta relaciones que suben o bajan sin ser una recta.\n",
    "\n",
    "¿Correlación implica causalidad?No, y el ejemplo está en este capítulo con datos propios. Dos cosas pueden moverse juntas porque una tercera las mueve a las dos, que es lo que pasa casi siempre.\n",
    "\n",
    "¿Cuándo una correlación es alta?No hay un número universal, depende del campo. Lo que sí se puede decir: en datos de negocio, una correlación de 0,3 ya es algo que vale la pena mirar, y una de 0,95 suele significar que estás midiendo lo mismo dos veces."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 15 de **Estadística desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/estadistica-desde-cero/correlacion/\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
}
