{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# La operación que hace todo: el producto punto\n",
    "\n",
    "Qué es el producto punto, por qué se llama así, y por qué es literalmente la línea que hay dentro de cualquier modelo lineal.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 4 de **Matemática para machine learning**, 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/matematica-para-machine-learning/producto-punto/\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": [
    "## Antes de empezar"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# Este capitulo abre el archivo por su nombre, sin URL, que es lo que se hace\n",
    "# cuando lo tienes al lado. Aqui se baja primero para que exista.\n",
    "for nombre in ['ventas-miss-yera.csv']:\n",
    "    urllib.request.urlretrieve(f\"https://missyera.com/static/datasets/{nombre}\", nombre)\n",
    "    print(nombre, \"lista\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ya lo hiciste en el capítulo anterior 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el capítulo 3 multiplicamos cada característica de\n",
    "una venta por su peso y sumamos todo. Salió 448.13.\n",
    "\n",
    "Esa operación tiene nombre propio, y es el nombre que vas a ver en todos los\n",
    "papers: **producto punto**. También le dicen producto escalar, y en\n",
    "inglés *dot product*. Las tres son lo mismo.\n",
    "\n",
    "Este capítulo es corto y es el más importante del bloque, porque casi todo lo\n",
    "que viene después es esta operación repetida muchas veces."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Volvemos a abrir el archivo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cada capítulo arranca de cero, así que estas líneas se repiten. Son las\n",
    "mismas del capítulo 1 y las vas a ver en todos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "df = pd.read_csv('ventas-miss-yera.csv')\n",
    "for c in ('unidades', 'monto', 'satisfaccion'):\n",
    "    df[c] = pd.to_numeric(df[c], errors='coerce')\n",
    "X = df[['unidades', 'monto', 'satisfaccion']].dropna().to_numpy()\n",
    "print('X.shape:', X.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tres formas de escribir lo mismo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = X[0]\n",
    "pesos = np.array([12.0, 0.35, 40.0])\n",
    "\n",
    "print('uno a uno :', np.round(a * pesos, 2))\n",
    "print('sumado    :', round(float((a * pesos).sum()), 2))\n",
    "print('np.dot    :', round(float(np.dot(a, pesos)), 2))\n",
    "print('a @ pesos :', round(float(a @ pesos), 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las tres últimas dan lo mismo porque son lo mismo. El símbolo\n",
    "`@` es el que vas a ver en código moderno, y se lee \"por\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "𝐚·𝐛=a1b1+a2b2+…+adbd=∑i=1daibi\n",
    "\n",
    "el producto punto multiplica cada componente con la que le toca en el otro vector y suma todo, así que de dos listas sale un solo número"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Símbolo | Qué es |\n",
    "|---|---|\n",
    "| **a** · **b** | el producto punto de los dos |\n",
    "| **ai** | la componente número i del primero |\n",
    "| **bi** | la del mismo sitio en el segundo |\n",
    "| **Σ** | la sigma, que solo quiere decir \"suma todo esto\" |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si la sigma te asustaba, mírala otra vez ahora que sabes qué hay debajo: es\n",
    "un bucle que suma. Nada más 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dos cosas que hay que tener claras"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Uno: de dos listas sale un número.** Entran tres componentes y\n",
    "tres pesos, y sale 448.13. Esto es lo que hace que el producto punto sirva para\n",
    "predecir: colapsa una fila entera en un solo valor.\n",
    "\n",
    "**Dos: el orden da igual.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "b = X[1]\n",
    "print('a . b :', round(float(a @ b), 2))\n",
    "print('b . a :', round(float(b @ a), 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Guárdate esto, porque con matrices **deja de ser cierto** y es\n",
    "uno de los errores más comunes. Lo vemos en el capítulo 5."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y aquí está el modelo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esta es la fórmula que hay dentro de una regresión lineal:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "y^=𝐰·𝐱+b\n",
    "\n",
    "la predicción de un modelo lineal es el producto punto de los pesos con la fila de datos, más un número suelto que ajusta la altura"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Símbolo | Qué es |\n",
    "|---|---|\n",
    "| **ŷ** | la predicción, que se lee \"y sombrero\" |\n",
    "| **w** | los pesos, uno por columna |\n",
    "| **x** | una fila de tus datos |\n",
    "| **b** | el sesgo o intercepto, que sube o baja todo por igual |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso es todo. Un producto punto y una suma. Cuando alguien te diga que un\n",
    "modelo lineal es simple, esto es lo que quiere decir: cabe en una línea.\n",
    "\n",
    "La **b** es lo único nuevo. Es un número suelto que se suma al\n",
    "final, y sirve para cuando la predicción tiene que arrancar de un sitio que no\n",
    "es cero. Si todas tus ventas facturan al menos 100 soles, ese 100 lo pone la\n",
    "**b** y no los pesos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Todas las filas de golpe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pred = X @ pesos\n",
    "print('pred.shape :', pred.shape)\n",
    "print('primeras 5 :', np.round(pred[:5], 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una sola línea y 2.238 predicciones. Fíjate en las formas: entra\n",
    "`(2238, 3)` con `(3,)` y sale `(2238,)`.\n",
    "\n",
    "El 3 desapareció, y desapareció porque es el que se gasta en la suma. Esa es\n",
    "la regla que tienes que aprender a leer de un vistazo, y es el capítulo que\n",
    "viene."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error que te va a salir"
   ]
  },
  {
   "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",
    "    a @ X\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: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2238 is different from 3)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El mensaje es feo pero dice exactamente lo que pasa: \"2238 es distinto de 3\".\n",
    "El vector tiene 3 componentes y la matriz tiene 2.238 filas, así que no hay\n",
    "manera de emparejarlas.\n",
    "\n",
    "Y date cuenta de que `X @ a` sí funciona. El orden importa 🙃"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- El producto punto multiplica componente con componente y suma. De dos\n",
    "listas sale un número.\n",
    "\n",
    "- Se escribe `a @ b`, `np.dot(a, b)` o\n",
    "**a** · **b**, y las tres son lo mismo.\n",
    "\n",
    "- La sigma es un bucle que suma. Ya está.\n",
    "\n",
    "- ŷ = **w** · **x** + b es una\n",
    "regresión lineal entera.\n",
    "\n",
    "- Entre vectores el orden da igual. Con matrices no, y eso viene ahora."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Tienes v con forma (3,) y una matriz X con forma (2238, 3). ¿Qué te devuelve X @ v?\n",
    "\n",
    "a) Un vector de 2.238 números: un producto punto por cada fila\n",
    "\n",
    "b) Un solo número, porque el producto punto siempre da un número\n",
    "\n",
    "c) Una matriz de 2238 por 3, del mismo tamaño que X\n",
    "\n",
    "d) Un error, porque las formas no coinciden\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Un producto punto sí da un número, pero aquí hay 2.238 filas y cada una hace el suyo. Lo que sale es un número por fila.\n",
    "\n",
    "*c)* Eso sería multiplicar elemento a elemento con el asterisco. El arroba colapsa la dimensión que comparten y por eso sale una sola lista.\n",
    "\n",
    "*d)* Sí coinciden: el 3 de X es el mismo 3 de v, y es el que desaparece. La regla es que el último eje del primero tiene que valer lo mismo que el primero del segundo.\n",
    "\n",
    "La regla de la forma: (2238, 3) contra (3,) deja (2238,). El 3 se gasta en la suma y no vuelve 🐣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Hazlo a mano con un bucle\n",
    "\n",
    "Para que veas que no hay magia debajo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = X[0]\n",
    "pesos = np.array([12.0, 0.35, 40.0])\n",
    "\n",
    "total = 0.0\n",
    "for i in range(len(a)):\n",
    "    total = total + a[i] * pesos[i]\n",
    "\n",
    "print('con bucle :', round(total, 2))\n",
    "print('con @     :', round(float(a @ pesos), 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "con bucle : 448.13\n",
    "con @     : 448.13\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso es literalmente lo que hace NumPy, solo que en C y sobre todas las filas\n",
    "a la vez. El bucle está para que lo veas una vez; en tu código de trabajo usa\n",
    "siempre `@`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. La forma antes de multiplicar\n",
    "\n",
    "Mira las formas y predice el resultado antes de correrlo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = X[0]\n",
    "pesos = np.array([12.0, 0.35, 40.0])\n",
    "print('a.shape', a.shape, 'X.shape', X.shape)\n",
    "print('X @ pesos ->', (X @ pesos).shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "a.shape (3,) X.shape (2238, 3)\n",
    "X @ pesos -> (2238,)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El hábito que quiero que te lleves de este libro: antes de multiplicar, mira\n",
    "las dos formas y di en voz alta qué forma va a salir. Si no lo sabes, todavía no\n",
    "estás listo para escribir esa línea."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Un peso en cero borra la columna\n",
    "\n",
    "Comprueba que el producto punto ignora lo que pesa cero."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = X[0]\n",
    "con    = np.array([12.0, 0.35, 40.0])\n",
    "sin    = np.array([12.0, 0.00, 40.0])\n",
    "print('con monto :', round(float(a @ con), 2))\n",
    "print('sin monto :', round(float(a @ sin), 2))\n",
    "print('diferencia:', round(float(a @ con - a @ sin), 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "con monto : 448.13\n",
    "sin monto : 280.0\n",
    "diferencia: 168.13\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esos 168.13 son exactamente lo que aportaba el monto. Así es como se lee la\n",
    "importancia de una característica en un modelo lineal, con la trampa de que si\n",
    "las columnas están en escalas distintas los pesos no se pueden comparar entre\n",
    "sí."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 4 de **Matemática para machine learning**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/matematica-para-machine-learning/producto-punto/\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
}
