{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Las columnas entre ellas\n",
    "\n",
    "La matriz que delata una fuga sin entrenar nada, y la correlación de 0,2351 que se evapora al partir por segmento.\n",
    "\n",
    "Cuaderno de práctica del capítulo 8 de **Machine learning 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/machine-learning-desde-cero/eda-multivariado/\n",
    "\n",
    "Los ejercicios están al final y traen una celda vacía debajo de cada uno. Las\n",
    "respuestas viven en el cuaderno de soluciones, y merece la pena pelearse un\n",
    "rato antes de abrirlo 💛"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Antes de empezar\n",
    "\n",
    "Esta celda baja el ayudante que corrige tus ejercicios. Después, en cada\n",
    "ejercicio que se pueda corregir solo, vas a ver `%%revisa` arriba de la celda:\n",
    "escribe tu respuesta debajo, ejecuta, y te digo si te salió 💛"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# El ayudante de los cuadernos. Trae la corrección de los ejercicios y, en los\n",
    "# capítulos de consola, la celda mágica que ejecuta los comandos. Se baja en\n",
    "# vez de venir pegado aquí para que siempre sea el último.\n",
    "urllib.request.urlretrieve(\n",
    "    \"https://missyera.com/static/cuadernos/revisa.py\", \"revisa.py\")\n",
    "import revisa\n",
    "revisa.carga({\n",
    "    1: \"Y29uIGxpc3TDs24gMC41OiAzIHBhcmVzCiAgICBtb250byB5IHByZWNpb191bml0YXJpbzogMC44MTQyCiAgICBtb250byB5IG1vbnRvX2ZpbmFsX2ZhY3R1cmFkbzogMC41Mzk3CiAgICBtb250b19maW5hbF9mYWN0dXJhZG8geSBjb21wcm86IDAuODg5Nwpjb24gbGlzdMOzbiAwLjc6IDIgcGFyZXMKICAgIG1vbnRvIHkgcHJlY2lvX3VuaXRhcmlvOiAwLjgxNDIKICAgIG1vbnRvX2ZpbmFsX2ZhY3R1cmFkbyB5IGNvbXBybzogMC44ODk3CmNvbiBsaXN0w7NuIDAuOTogMCBwYXJlcw==\",\n",
    "    2: \"bGFzIGRvcyAgICAgICAgQVVDIDAuNjU5NCArLy0gMC4wMTY0CnNvbG8gbW9udG8gICAgIEFVQyAwLjY1NzYgKy8tIDAuMDE3Mgpzb2xvIHByZWNpbyAgICBBVUMgMC42Mzg0ICsvLSAwLjAxMTI=\",\n",
    "    3: \"bW9udG8gICAgICAgICAgICAgIGdsb2JhbCArMC4yMzUxICAgZGVudHJvIGRlIHNlZ21lbnRvOiBtaW4gLTAuMDEyOCBtYXggKzAuMDMyNCAgIG1lZGlhICswLjAxMjkKcHJlY2lvX3VuaXRhcmlvICAgIGdsb2JhbCArMC4xODU4ICAgZGVudHJvIGRlIHNlZ21lbnRvOiBtaW4gLTAuMDExMSBtYXggKzAuMDI1NyAgIG1lZGlhICswLjAwOTAKc2F0aXNmYWNjaW9uICAgICAgIGdsb2JhbCArMC4xNzk0ICAgZGVudHJvIGRlIHNlZ21lbnRvOiBtaW4gKzAuMTI4MiBtYXggKzAuMjIyOSAgIG1lZGlhICswLjE4MDgKdW5pZGFkZXMgICAgICAgICAgIGdsb2JhbCArMC4wMjE3ICAgZGVudHJvIGRlIHNlZ21lbnRvOiBtaW4gLTAuMDIxNCBtYXggKzAuMDQxMyAgIG1lZGlhICswLjAxNTUKZGVzY3VlbnRvICAgICAgICAgIGdsb2JhbCArMC4wNTI2ICAgZGVudHJvIGRlIHNlZ21lbnRvOiBtaW4gKzAuMDEwNyBtYXggKzAuMDkyNSAgIG1lZGlhICswLjA2NzM=\",\n",
    "    4: \"Y29sdW1uYXMgZGUgZW50cmFkYTogMjQKY29tcG9uZW50ZXMgcGFyYSBlbCA5MCU6IDE3CmxhcyB0cmVzIHByaW1lcmFzIGV4cGxpY2FuOiAyMi4xMCU=\",\n",
    "    5: \"ZmFsdGFfZGVzYyAgICAgMC4xOTgzCmZhbHRhX3NhdGlzICAgIDAuMDc3MApmYWx0YV9mZWNoYSAgICAwLjE4MTMKCiAgICAgICAgICAgICBmYWx0YV9kZXNjICBmYWx0YV9zYXRpcyAgZmFsdGFfZmVjaGEKZmFsdGFfZGVzYyAgICAgICAxLjAwMDAgICAgICAtMC4wMzM5ICAgICAgLTAuMDAxOQpmYWx0YV9zYXRpcyAgICAgLTAuMDMzOSAgICAgICAxLjAwMDAgICAgICAtMC4wMDI5CmZhbHRhX2ZlY2hhICAgICAtMC4wMDE5ICAgICAgLTAuMDAyOSAgICAgICAxLjAwMDAKCmZpbGFzIGEgbGFzIHF1ZSBsZXMgZmFsdGEgbcOhcyBkZSB1bmEgY29zYTogMTY5\",\n",
    "    6: \"ICAgICAgICAgICAgICAgICAgICAgICAgIHVuaWRhZCAgbW9udG8gZGVzY3VlIHNhdGlzZiBwcmVjaW8gbW9udG9fIGNvbXBybwp1bmlkYWRlcyAgICAgICAgICAgICAgICAgICAgQEBAICAgICAgICAgICAgICAgICAgICAgICAgID09PSAgICAgICAgICAgICAgCm1vbnRvICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQEBAICAgICAgICAgICAgICAgICAgIyMjICAgID09PSAgICA6OjoKZGVzY3VlbnRvICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQEBAICAgICAgICAgICAgICAgICAgICAgICAgICAgIApzYXRpc2ZhY2Npb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQEBAICAgICAgICAgICAuLi4gICAgLi4uCnByZWNpb191bml0YXJpbyAgICAgICAgICAgICA9PT0gICAgIyMjICAgICAgICAgICAgICAgICAgQEBAICAgIC0tLSAgICAuLi4KbW9udG9fZmluYWxfZmFjdHVyYWRvICAgICAgICAgICAgICA9PT0gICAgICAgICAgIC4uLiAgICAtLS0gICAgQEBAICAgICUlJQpjb21wcm8gICAgICAgICAgICAgICAgICAgICAgICAgICAgIDo6OiAgICAgICAgICAgLi4uICAgIC4uLiAgICAlJSUgICAgQEBA\",\n",
    "    7: \"ICBnZW1lbGFzOiBtb250byB5IHByZWNpb191bml0YXJpbyB2YW4gYSAwLjgxNAogIEFMQVJNQTogbW9udG9fZmluYWxfZmFjdHVyYWRvIHkgY29tcHJvIHZhbiBhIDAuODkwLiBTb3NwZWNoYSBkZSBmdWdhLgogIGNvbmZ1c2lvbjogbW9udG8gY29ycmVsYWNpb25hICswLjIzNSBlbiB0b3RhbCB5ICswLjAxMyBkZW50cm8gZGUgc2VnbWVudG8KICBjb25mdXNpb246IHByZWNpb191bml0YXJpbyBjb3JyZWxhY2lvbmEgKzAuMTg2IGVuIHRvdGFsIHkgKzAuMDA5IGRlbnRybyBkZSBzZWdtZW50bw==\",\n",
    "}, lenguaje=\"python\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El capítulo anterior dejó dos preguntas colgando, y las dos se contestan\n",
    "igual: **mirando las columnas entre ellas** 🕸️\n",
    "\n",
    "`sin_descuento` tenía doce puntos y no le va a aportar nada al\n",
    "modelo. `monto` era la mejor de las numéricas y en el capítulo\n",
    "22 se queda en p de 0,1561.\n",
    "\n",
    "Las dos cosas pasan por lo mismo: una columna no vive sola. Y este capítulo\n",
    "termina con un resultado que a mí me sigue pareciendo el más bonito del libro\n",
    "🤯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El punto de partida"
   ]
  },
  {
   "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",
    "def carga_limpia(url):\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', errors='coerce')\n",
    "        v[col] = f\n",
    "    return v\n",
    "\n",
    "ventas = carga_limpia(URL)\n",
    "ventas['precio_unitario'] = ventas['monto'] / ventas['unidades']\n",
    "\n",
    "NUMERICAS = ['unidades', 'monto', 'descuento', 'satisfaccion',\n",
    "             'precio_unitario', 'monto_final_facturado', 'compro']\n",
    "\n",
    "print('columnas que entran en la maraña:', len(NUMERICAS))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error de la primera matriz"
   ]
  },
  {
   "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",
    "    ventas[['segmento', 'monto']].corr()\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: 'Horeca'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una correlación necesita números y `segmento` es texto. Para\n",
    "cruzar dos categóricas entre ellas hace falta otra cosa, y la vemos más abajo\n",
    "🔤"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La matriz, con Spearman y no con Pearson"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ρ=1−6∑di2n(n2−1)\n",
    "\n",
    "la correlación calculada sobre los puestos y no sobre los valores, así que mide si una sube cuando la otra sube sin pedirle que sea una recta"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(ventas[NUMERICAS].corr(method='spearman').round(3).to_string())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de leerla, la pregunta que casi nadie hace: **¿por qué\n",
    "Spearman?**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "spearman = ventas[NUMERICAS].corr(method='spearman')\n",
    "pearson = ventas[NUMERICAS].corr(method='pearson')\n",
    "\n",
    "pares = []\n",
    "for i, a in enumerate(NUMERICAS):\n",
    "    for j, b in enumerate(NUMERICAS):\n",
    "        if i < j:\n",
    "            pares.append((abs(spearman.iloc[i, j] - pearson.iloc[i, j]),\n",
    "                          a, b, spearman.iloc[i, j], pearson.iloc[i, j]))\n",
    "\n",
    "for diferencia, a, b, s, p in sorted(pares, reverse=True)[:4]:\n",
    "    print(f'{a:22s} {b:22s} spearman {s:+.3f}  pearson {p:+.3f}  '\n",
    "          f'se llevan {diferencia:.3f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está el motivo 👀\n",
    "\n",
    "`monto` y `precio_unitario` dan 0,814 con Spearman y\n",
    "0,446 con Pearson. Casi el doble.\n",
    "\n",
    "Pearson mide si la relación es una **recta**, y esas dos\n",
    "columnas suben juntas pero no en línea recta: en el capítulo\n",
    "6 vimos que `precio_unitario` tiene asimetría\n",
    "6,734, así que cuatro ventas enormes le doblan la recta a Pearson y le esconden\n",
    "la relación.\n",
    "\n",
    "Spearman trabaja con los puestos, no con los valores. La venta más cara es la\n",
    "número uno, valga 3.000 o 300.000. Por eso no se deja arrastrar 📊"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Los pares que hay que mirar sí o sí"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "c = ventas[NUMERICAS].corr(method='spearman').abs()\n",
    "for i, a in enumerate(NUMERICAS):\n",
    "    for j, b in enumerate(NUMERICAS):\n",
    "        if i < j and c.iloc[i, j] > 0.5:\n",
    "            print(f'{a:22s} {b:22s} {c.iloc[i, j]:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres pares, y cada uno cuenta una cosa distinta:\n",
    "\n",
    "- 🪞 `monto` con `precio_unitario`, 0,8142. Normal: la\n",
    "segunda sale de dividir la primera. Son casi la misma columna y por eso en el\n",
    "capítulo 22 se repartían el efecto.\n",
    "\n",
    "- 💰 `monto` con `monto_final_facturado`, 0,5397.\n",
    "También esperable, son dos formas de contar el mismo dinero.\n",
    "\n",
    "- 🚨 `monto_final_facturado` con `compro`,\n",
    "**0,8897**. Y esa no es normal en absoluto.\n",
    "\n",
    "Una columna que correlaciona 0,89 con lo que quieres predecir **no es\n",
    "una buena columna: es una alarma**. Es la fuga del capítulo\n",
    "12, y aquí sale sin entrenar ningún modelo, mirando una\n",
    "matriz 🎯\n",
    "\n",
    "La regla que uso: por encima de 0,8 contra el objetivo, primero desconfío y\n",
    "después celebro. Casi siempre resulta que la columna se rellena después de que\n",
    "pase lo que quiero predecir."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y las categóricas entre ellas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para dos categóricas no hay correlación, hay V de Cramér, que es la misma del\n",
    "capítulo 7 usada entre columnas en vez de contra el\n",
    "objetivo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "CATEGORICAS = ['ciudad', 'segmento', 'canal', 'categoria']\n",
    "\n",
    "def cramer(tabla):\n",
    "    chi2 = stats.chi2_contingency(tabla)[0]\n",
    "    n = tabla.values.sum()\n",
    "    return np.sqrt(chi2 / (n * (min(tabla.shape) - 1)))\n",
    "\n",
    "for i, a in enumerate(CATEGORICAS):\n",
    "    for j, b in enumerate(CATEGORICAS):\n",
    "        if i < j:\n",
    "            print(f'{a:12s} {b:12s} V {cramer(pd.crosstab(ventas[a], ventas[b])):.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las seis por debajo de 0,05, o sea prácticamente independientes 😌\n",
    "\n",
    "Eso significa que saber el segmento no te dice nada del canal, y que el\n",
    "one-hot de las cuatro no va a crear columnas redundantes. Aburrido otra vez, y\n",
    "otra vez es buena noticia."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y ahora sí: la relación que se evapora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esta es la que cierra el hilo de tres capítulos 🎬"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "global_ = stats.spearmanr(ventas['monto'], ventas['compro'])\n",
    "print(f'global      rho {global_.statistic:+.4f}  p {global_.pvalue:.2e}  '\n",
    "      f'n {len(ventas)}')\n",
    "\n",
    "for segmento in sorted(ventas['segmento'].unique()):\n",
    "    trozo = ventas[ventas['segmento'] == segmento]\n",
    "    r = stats.spearmanr(trozo['monto'], trozo['compro'])\n",
    "    print(f'{segmento:12s} rho {r.statistic:+.4f}  p {r.pvalue:.4f}  n {len(trozo)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Míralo despacio, porque es de esas cosas que hay que ver dos veces 🤯\n",
    "\n",
    "En las 3.000 ventas juntas, el monto correlaciona +0,2351 con la compra, con\n",
    "una p de 6,01e-39. Un número que en cualquier informe se escribe como hallazgo.\n",
    "\n",
    "Y **dentro de cada segmento no queda nada**: +0,0324, −0,0128,\n",
    "+0,0061, +0,0262. Las cuatro con p por encima de 0,39, y con setecientas filas\n",
    "cada una, que son de sobra para detectar algo de ese tamaño.\n",
    "\n",
    "La explicación está en dos tablas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(ventas.groupby('segmento')['monto'].agg(['mean', 'median', 'count'])\n",
    "      .round(2).to_string())\n",
    "print()\n",
    "print(ventas.groupby('segmento')['compro'].mean().round(4).to_string())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El mayorista compra por 1.856 soles de media y cierra el 72,40%. La bodega\n",
    "compra por 178 y cierra el 37,48%.\n",
    "\n",
    "O sea que **el monto no predice la compra: predice el segmento**,\n",
    "y el segmento sí predice la compra. Cuando miras las 3.000 juntas, el monto se\n",
    "lleva un mérito que no es suyo 🎭\n",
    "\n",
    "Esto tiene nombre, se llama *confusión* o variable confusora, y es la\n",
    "razón por la que el capítulo 22 daba p de 0,1561\n",
    "para el monto: allí el segmento estaba dentro del modelo, así que ya no le\n",
    "dejaba llevarse nada prestado.\n",
    "\n",
    "Y la razón por la que un análisis bivariado, él solo, **puede llevarte\n",
    "a una conclusión completamente equivocada**. \"Sube el ticket medio y\n",
    "cerrarás más\" es exactamente lo que se deduce del +0,2351, y aquí está la prueba\n",
    "de que no 🚫"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reducir trece columnas a unas pocas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuando hay muchas columnas que dicen lo mismo, se pueden resumir. El PCA\n",
    "busca las direcciones donde los datos más varían y las pone primero."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.decomposition import PCA\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "SOLO_NUMERICAS = ['unidades', 'monto', 'descuento', 'satisfaccion',\n",
    "                  'precio_unitario']\n",
    "M = ventas[SOLO_NUMERICAS].fillna(ventas[SOLO_NUMERICAS].median())\n",
    "Z = StandardScaler().fit_transform(M)\n",
    "\n",
    "pca = PCA().fit(Z)\n",
    "acumulado = np.cumsum(pca.explained_variance_ratio_)\n",
    "for i, (parte, total) in enumerate(zip(pca.explained_variance_ratio_, acumulado), 1):\n",
    "    print(f'componente {i}: explica {100 * parte:5.2f}%   acumulado {100 * total:6.2f}%')\n",
    "print()\n",
    "print('componentes para llegar al 90%:', int(np.argmax(acumulado >= 0.9) + 1),\n",
    "      'de', len(SOLO_NUMERICAS))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y aquí el PCA **no sirve para nada**, que también es un\n",
    "resultado 😅\n",
    "\n",
    "Hacen falta cuatro componentes de cinco para llegar al 90%. Cambiar cinco\n",
    "columnas que se entienden por cuatro que no se entienden es un mal negocio.\n",
    "\n",
    "Tiene todo el sentido: la matriz de correlaciones ya decía que estas columnas\n",
    "son casi independientes. El PCA solo comprime lo que está repetido, y aquí no\n",
    "hay casi nada repetido.\n",
    "\n",
    "El escalado antes del PCA no es opcional, por cierto. Sin él,\n",
    "`monto` mandaría en la primera componente solo por ir en miles\n",
    "mientras `satisfaccion` va de 1 a 5 📏"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué mira cada componente"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "carga = pd.DataFrame(pca.components_[:2].T, index=SOLO_NUMERICAS,\n",
    "                     columns=['CP1', 'CP2'])\n",
    "print(carga.round(3).to_string())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La primera componente es `precio_unitario` (0,712) contra\n",
    "`unidades` (−0,472): comprar caro y poco, o barato y mucho. Eso es\n",
    "una idea de negocio con nombre, y es lo único interpretable que sale de aquí.\n",
    "\n",
    "La segunda mezcla unidades, monto y descuento con signos distintos, y ya no\n",
    "se puede decir en voz alta. Ese es el precio del PCA: ganas compresión y pierdes\n",
    "la capacidad de explicar 🔇"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. El buscador de columnas gemelas\n",
    "\n",
    "Automatiza lo de arriba: una función que devuelva los pares\n",
    "que hay que revisar antes de modelar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Qué pasa si quitas una de las gemelas\n",
    "\n",
    "`monto` y `precio_unitario` van a\n",
    "0,81. Mide qué se pierde quitando una."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. La confusión, buscada a propósito\n",
    "\n",
    "Repite lo de `monto` con todas las numéricas:\n",
    "correlación global contra correlación dentro de los segmentos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. El PCA con las categóricas dentro\n",
    "\n",
    "Arriba solo entraron cinco numéricas. Mete también el\n",
    "one-hot de las cuatro categóricas y mira si cambia algo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. La matriz, pero de nulos\n",
    "\n",
    "Las columnas también se relacionan por dónde les faltan\n",
    "datos. Cruza los patrones de nulos entre ellas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. El mapa de calor, en texto\n",
    "\n",
    "Sin librería de gráficos: pinta la matriz con caracteres\n",
    "para verla de un vistazo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. El informe multivariado, en avisos\n",
    "\n",
    "Cierra el bloque de EDA con la función que resume las tres\n",
    "comprobaciones de este capítulo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 7\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Una columna correlaciona +0,24 con el objetivo en las 3.000 filas, y dentro de cada segmento la correlación se va a cero. ¿Qué está pasando?\n",
    "\n",
    "a) Que lo que correlaciona con el objetivo es el segmento, no la columna\n",
    "\n",
    "b) Que hacen falta más datos dentro de cada segmento\n",
    "\n",
    "c) Que la relación es real pero no lineal\n",
    "\n",
    "d) Que hay que quitar el segmento del modelo para verla"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El filtro automático que no filtra nada"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "El filtro de columnas redundantes de toda la vida: se calcula la matriz de correlaciones, y lo que pase de 0,8 con otra se cae. Dos líneas y a otra cosa.\n",
    "\n",
    "```\n",
    "c = ventas.corr().abs()\n",
    "redundantes = [col for col in c\n",
    "               if (c[col] > 0.8).sum() > 1]\n",
    "\n",
    "# redundantes: []  -> 'no hay ninguna'\n",
    "```\n",
    "\n",
    "**¿Qué está mal?** La respuesta está en el cuaderno de soluciones. Míralo tú primero."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 📊 La matriz se lee con Spearman, no con Pearson. `monto` y\n",
    "`precio_unitario` dan 0,814 con una y 0,446 con la otra, porque\n",
    "Pearson pide que la relación sea una recta.\n",
    "\n",
    "- 🚨 `monto_final_facturado` correlaciona 0,8897 con el objetivo.\n",
    "Por encima de 0,8 contra lo que quieres predecir, primero se desconfía y\n",
    "después se celebra.\n",
    "\n",
    "- 🪞 `monto` y `precio_unitario` a 0,81 son casi la\n",
    "misma columna, y por eso en la regresión se repartían el efecto.\n",
    "\n",
    "- 🔤 Las cuatro categóricas están por debajo de 0,05 de V entre ellas, o sea\n",
    "prácticamente independientes.\n",
    "\n",
    "- 🤯 Y la grande: el monto correlaciona +0,2351 con la compra en las 3.000\n",
    "filas y **dentro de cada segmento se va a cero**. El monto no\n",
    "predice la compra, predice el segmento.\n",
    "\n",
    "- 🎭 Eso se llama variable confusora, explica la p de 0,1561 del capítulo\n",
    "22 y demuestra que un análisis bivariado solo\n",
    "puede llevarte a una conclusión completamente equivocada.\n",
    "\n",
    "- 📉 El PCA aquí no sirve: hacen falta cuatro componentes de cinco para el\n",
    "90%. Comprime lo repetido, y estas columnas no se repiten.\n",
    "\n",
    "- 🔇 Y lo que sí se lee de él, la primera componente, es una idea de negocio:\n",
    "comprar caro y poco contra barato y mucho.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dos columnas que dicen lo mismo no son el doble de información. Son la mitad de sitio para pensar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El PCA que sale al final de este capítulo tiene su explicación despacio en el [libro de estadística desde cero](https://missyera.com/guias/estadistica-desde-cero/) 📊"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué viene ahora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nueve capítulos de exploración y ya sabemos qué columna sirve, cuál engaña y\n",
    "cuál es una fuga. Toca convertir todo eso en decisiones 🧱\n",
    "\n",
    "El capítulo 9 saca cinco columnas nuevas de las que\n",
    "ya hay, y es donde de verdad se gana: cambiar de modelo te da décimas y\n",
    "construir una columna buena te da puntos enteros.\n",
    "\n",
    "Lo que verás allí:\n",
    "\n",
    "- 🕳️ El hueco convertido en dato, que es el hallazgo más grande de todo el\n",
    "bloque de exploración.\n",
    "\n",
    "- 💵 El precio por unidad, que aquí salió correlacionado con el monto y que\n",
    "aun así aporta.\n",
    "\n",
    "- 📊 El monto partido en tramos, para que un modelo lineal pueda ver lo que\n",
    "solo veía el árbol.\n",
    "\n",
    "- 🗑️ Una que no sirve, para ver cómo se descarta con argumentos.\n",
    "\n",
    "- 🪤 Y una trampa que sube el AUC treinta puntos y no vale nada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 8 de **Machine learning desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/machine-learning-desde-cero/eda-multivariado/\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
}
