{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Las matrices con nombre propio, y por qué les pusieron uno\n",
    "\n",
    "Cuadrada, diagonal, simétrica, identidad y ortogonal. Qué tienen de especial y dónde te las vas a encontrar trabajando.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 7 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/tipos-de-matriz/\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": [
    "## Los nombres no son para el examen 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el colegio te hacen memorizar diez tipos de matriz y nunca te dicen para\n",
    "qué. Y la verdad es que sí sirven, pero no como lista: sirven porque cuando\n",
    "reconoces una, **te ahorras trabajo**. Hay operaciones que en una\n",
    "matriz cualquiera son carísimas y en una diagonal son una división.\n",
    "\n",
    "Así que vamos con los cinco que te vas a encontrar trabajando, y con dónde\n",
    "aparece cada uno en lo que ya hicimos en este libro."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Volvemos a abrir el archivo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las mismas líneas de siempre, las del capítulo 1."
   ]
  },
  {
   "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",
    "\n",
    "X = df[['unidades', 'monto', 'satisfaccion']].dropna().to_numpy()\n",
    "print('X.shape:', X.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matriz cuadrada: la que tiene el mismo número de filas que de columnas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tus datos casi nunca son cuadrados. X tiene 2.238 filas y 3 columnas, y así\n",
    "va a ser siempre: muchas más filas que columnas.\n",
    "\n",
    "Pero mira lo que pasa cuando la multiplicas por su traspuesta, que es la\n",
    "operación del capítulo 6:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "G = X.T @ X\n",
    "print('X.shape:', X.shape)\n",
    "print('G.shape:', G.shape, '| cuadrada:', G.shape[0] == G.shape[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y ahí está la primera razón por la que esto importa: **casi todo lo\n",
    "interesante del álgebra lineal solo existe para matrices cuadradas**. La\n",
    "inversa, el determinante, los autovalores. Nada de eso se le puede pedir a una\n",
    "tabla de 2.238 por 3.\n",
    "\n",
    "Por eso el truco de multiplicar por la traspuesta aparece en todos lados: es\n",
    "la forma de pasar de una tabla enorme a una matriz chiquita y cuadrada sobre la\n",
    "que sí puedes trabajar 🧮"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matriz simétrica: la que es igual a su traspuesta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una matriz es simétrica cuando `A` y `A.T` son la misma\n",
    "cosa. Dicho de otra forma: lo que está arriba de la diagonal es el espejo de lo\n",
    "que está abajo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('G ='); print(np.round(G, 2))\n",
    "print('simetrica:', np.allclose(G, G.T))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa G que acabamos de armar sale simétrica **siempre**, y no es\n",
    "casualidad: la casilla de la fila unidades con la columna monto se calcula\n",
    "exactamente igual que la de monto con unidades. Es la misma cuenta escrita al\n",
    "revés.\n",
    "\n",
    "Y por eso la matriz de correlaciones también es simétrica: que unidades\n",
    "correlacione con monto es lo mismo que monto correlacione con unidades. Cuando\n",
    "veas una matriz simétrica, casi siempre está midiendo una relación entre pares\n",
    "que no tiene dirección."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matriz diagonal: ceros en todos lados menos en la diagonal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una matriz diagonal tiene números en la diagonal principal y cero en todo lo\n",
    "demás:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "D = np.diag([2., 5., 3.])\n",
    "print('D ='); print(D)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y esto es lo que la hace útil: multiplicar por una diagonal es\n",
    "**escalar cada fila por su propio número**, nada más. Ni sumas ni\n",
    "mezclas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(np.round(D @ X[:2].T, 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las unidades se duplicaron, el monto se multiplicó por 5 y la satisfacción\n",
    "por 3. Cada fila por lo suyo.\n",
    "\n",
    "Cuando alguien normaliza columnas, o pondera variables, o escala datos antes\n",
    "de un modelo, por debajo casi siempre hay una diagonal. Y su inversa es la cosa\n",
    "más barata del mundo: uno partido cada número de la diagonal.\n",
    "\n",
    "La **matriz identidad** del capítulo anterior es una diagonal con\n",
    "puros unos, y la **matriz nula** es la que tiene cero en todas\n",
    "partes. Las dos son casos de esta."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matriz triangular: media matriz en cero"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Triangular superior es la que tiene ceros debajo de la diagonal, y triangular\n",
    "inferior al revés:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "M = np.arange(1., 10.).reshape(3, 3)\n",
    "print('superior ='); print(np.triu(M))\n",
    "print('inferior ='); print(np.tril(M))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "No la vas a construir tú a mano casi nunca, pero te la vas a encontrar: es lo\n",
    "que devuelven por dentro los métodos que resuelven sistemas de ecuaciones. Con\n",
    "una triangular se despeja de arriba hacia abajo sin invertir nada, y por eso\n",
    "`solve` es más rápido que `inv` 🐣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matriz ortogonal: la que gira sin deformar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esta es la más bonita de las cinco. Una matriz es ortogonal cuando **su\n",
    "traspuesta es su inversa**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Q = np.array([[0., -1.],\n",
    "              [1.,  0.]])\n",
    "\n",
    "print('Q.T @ Q ='); print(Q.T @ Q)\n",
    "print('la traspuesta hace de inversa:', np.allclose(Q.T, np.linalg.inv(Q)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Multiplicar por una ortogonal es **girar**: cambia la\n",
    "orientación pero no estira ni encoge nada. Las distancias entre tus puntos\n",
    "quedan exactamente iguales antes y después.\n",
    "\n",
    "Y por eso importa, aunque suene abstracto: cuando en el capítulo\n",
    "12 rotemos los datos para mirarlos desde otro ángulo, la matriz\n",
    "que hace ese giro es ortogonal. Es lo que garantiza que PCA reordene la\n",
    "información sin inventarse ni perder nada por el camino.\n",
    "\n",
    "Bonus práctico: invertir una ortogonal no cuesta nada, porque invertirla es\n",
    "trasponerla. Reconocer una te ahorra la operación más cara del capítulo\n",
    "anterior."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Tienes X con forma (2238, 3) y calculas G = X.T @ X. ¿Qué puedes afirmar de G sin mirar un solo número?\n",
    "\n",
    "a) Que es cuadrada de 3 por 3 y además simétrica\n",
    "\n",
    "b) Que es cuadrada de 2238 por 2238\n",
    "\n",
    "c) Que tiene inversa seguro, porque es cuadrada\n",
    "\n",
    "d) Que es diagonal, porque las columnas son distintas\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Ese es X @ X.T, el otro orden. Fíjate en las formas: (3, 2238) por (2238, 3) deja los 3 por fuera.\n",
    "\n",
    "*c)* Cuadrada es condición necesaria, no suficiente. Si dos columnas de X traen la misma información, G sale singular igual.\n",
    "\n",
    "*d)* Solo saldría diagonal si las columnas fueran perpendiculares entre sí, y unidades con monto claramente no lo son.\n",
    "\n",
    "Cuadrada porque (3, 2238) por (2238, 3) da (3, 3), y simétrica porque la casilla (i, j) y la (j, i) son el mismo producto punto escrito al revés 🐣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. La correlación también es simétrica\n",
    "\n",
    "Comprueba que la matriz de correlaciones sale simétrica, y\n",
    "mira qué tiene en la diagonal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "C = np.corrcoef(X.T)\n",
    "print('simetrica:', np.allclose(C, C.T))\n",
    "print('diagonal :', np.round(np.diag(C), 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "simetrica: True\n",
    "diagonal : [1. 1. 1.]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Simétrica porque que unidades correlacione con monto es lo mismo que monto\n",
    "correlacione con unidades. Y unos en la diagonal porque ahí cada columna se está\n",
    "correlacionando consigo misma, y eso siempre da 1."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Invertir una diagonal cuesta una división\n",
    "\n",
    "Invierte una matriz diagonal a mano, sin usar\n",
    "`inv`, y comprueba que da lo mismo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "D = np.diag([2., 5., 4.])\n",
    "\n",
    "a_mano = np.diag(1 / np.diag(D))\n",
    "print('a mano:'); print(a_mano)\n",
    "print('iguales:', np.allclose(a_mano, np.linalg.inv(D)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "a mano:\n",
    "[[0.5  0.   0.  ]\n",
    " [0.   0.2  0.  ]\n",
    " [0.   0.   0.25]]\n",
    "iguales: True\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Uno partido cada número de la diagonal y ya está. Por eso reconocer una\n",
    "diagonal ahorra tanto: la operación más cara del capítulo anterior aquí es una\n",
    "división por cada fila."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 🔲 **Cuadrada**: mismas filas que columnas. Sin esto no hay\n",
    "inversa, ni determinante, ni autovalores.\n",
    "\n",
    "- 🪞 **Simétrica**: igual a su traspuesta. Aparece siempre que\n",
    "mides relaciones entre pares, como la correlación o X traspuesta por X.\n",
    "\n",
    "- ➗ **Diagonal**: ceros fuera de la diagonal. Multiplicar por\n",
    "ella es escalar cada fila, y su inversa es trivial.\n",
    "\n",
    "- 📐 **Triangular**: media matriz en cero. Es lo que usan por\n",
    "dentro los solucionadores de sistemas.\n",
    "\n",
    "- 🔄 **Ortogonal**: su traspuesta es su inversa. Gira sin\n",
    "deformar, y es la que hace el trabajo en PCA.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Reconocer el tipo de matriz que tienes delante no es trivia de examen: es saber qué operación te va a salir barata."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué viene ahora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ya sabemos cuándo una matriz tiene inversa y cuándo no, y ahora también cómo\n",
    "se llama cada forma. Falta medir: cuánto mide un vector, cuánto se parecen dos.\n",
    "Eso es el capítulo 8 📏"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 7 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/tipos-de-matriz/\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
}
