{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2628674b-1946-46c3-a5a8-12c827b742f9",
   "metadata": {},
   "source": [
    "<figure>\n",
    "<img src=\"../Imagenes/logo-final-ap.png\"  width=\"80\" height=\"80\" align=\"left\"/> \n",
    "</figure>\n",
    "\n",
    "# <span style=\"color:#4361EE\"><left>Aprendizaje Profundo</left></span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24b92630-1839-4ec0-837f-1d4a5b604048",
   "metadata": {},
   "source": [
    "# <span style=\"color:red\"><center>Diplomado en Ciencia de Datos</center></span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3870a7f-0dd3-4416-84e3-b3575af46234",
   "metadata": {},
   "source": [
    "# <span style=\"color:green\"><center>Introducción al módulo `numpy`</center></span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d110aaea-4c5e-4a99-8cf6-9936571ec7f1",
   "metadata": {},
   "source": [
    "<figure>\n",
    "<center>\n",
    "<img src=\"https://raw.githubusercontent.com/AprendizajeProfundo/Alejandria/main/Archivos_Generales/Imagenes/Carretera.jpg\" width=\"600\" height=\"400\" align=\"center\" /> \n",
    "</center>   \n",
    "</figure>\n",
    "\n",
    "<center>Fuente: <a href=\"https://sp.depositphotos.com/stock-photos/architecture-buildings.html\">Dubai downtown cityscape - sp.depositphotos.com</a></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc431bad-bd0e-43ea-b29a-0e297544711f",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Profesores</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61fa22e6-88dd-4b37-97bc-2c29d322db5f",
   "metadata": {},
   "source": [
    "* Alvaro  Montenegro, PhD, <ammontenegrod@unal.edu.co>\n",
    "* Campo Elías Pardo, PhD, <cepardot@unal.edu.co>\n",
    "* Daniel  Montenegro, Msc, <dextronomo@gmail.com>\n",
    "* Camilo José Torres Jiménez, Msc, <cjtorresj@unal.edu.co>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8aefa1a7-4fe8-4e63-a62e-8b30240af69b",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Coordinador</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4df74eab-21e5-484d-8f2a-f92e766bfe8a",
   "metadata": {},
   "source": [
    "* Campo Elías Pardo, PhD, cepardot@unal.edu.co"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3311c6c6-82e1-40d9-88de-110e63fed224",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Coautores</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3613dda8-54dc-4523-9630-47900a43d9b4",
   "metadata": {},
   "source": [
    "* Camilo José Torres Jiménez, Msc, cjtorresj@unal.edu.co\n",
    "* Oleg Jarma, ojarmam@unal.edu.co\n",
    "* Julieth López, julalopezcas@unal.edu.co"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01167908-0ca4-498a-a302-3f7eb2a56038",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Asesora Medios y Marketing digital</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "249647e7-d759-4869-bd2b-a515b34e966a",
   "metadata": {},
   "source": [
    "* Maria del Pilar Montenegro, pmontenegro88@gmail.com\n",
    "* Jessica López Mejía, jelopezme@unal.edu.co"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11fee248-0d91-4e48-ab1a-3bd6650f072b",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Jefe Jurídica</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17156829-713b-4617-9fe9-bccdb57f93f3",
   "metadata": {},
   "source": [
    "* Paula Andrea Guzmán, guzmancruz.paula@gmail.com"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30c9411f-fef4-40f7-8253-9ad62dc616d8",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Coordinador Jurídico</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23522b0d-87ea-4c1c-963d-15b79db62f57",
   "metadata": {},
   "source": [
    "* David Fuentes, fuentesd065@gmail.com"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "800de78e-a3c1-4eca-a967-bb91658e3758",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Desarrolladores Principales</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c9108bd-bd57-46cc-9c8c-8c27c9a43a18",
   "metadata": {},
   "source": [
    "* Dairo Moreno, damoralesj@unal.edu.co\n",
    "* Joan Castro, jocastroc@unal.edu.co\n",
    "* Bryan Riveros, briveros@unal.edu.co\n",
    "* Rosmer Vargas, rovargasc@unal.edu.co\n",
    "* Venus Puertas, vpuertasg@unal.edu.co"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f72cda5-7f5f-42da-9d8c-b54f7a9eb908",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Expertos en Bases de Datos</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3def70d5-7148-4aa9-a5f6-262096698f4d",
   "metadata": {},
   "source": [
    "* Giovvani Barrera, udgiovanni@gmail.com\n",
    "* Camilo Chitivo, cchitivo@unal.edu.co"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9fb9210",
   "metadata": {
    "id": "--yl3E28USV0"
   },
   "source": [
    "## <span style=\"color:#4361EE\">¿Qué es `numpy`?</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4e658cc",
   "metadata": {},
   "source": [
    "En la documentación del módulo o paquete `numpy` dice:\n",
    "    \n",
    "```\n",
    "Provides\n",
    "  1. An array object of arbitrary homogeneous items\n",
    "  2. Fast mathematical operations over arrays\n",
    "  3. Linear Algebra, Fourier Transforms, Random Number Generation\n",
    "```\n",
    "\n",
    "`numpy` está escrito parcialmente en Python, pero en la mayoría de sus partes que requieren un computo lo más eficiente posible está escrita en C o C++."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46a8e471-7e59-4af0-935e-2609bf47f996",
   "metadata": {},
   "source": [
    "Para más detalles acerca del contenido del paquete `numpy` consultar:\n",
    "\n",
    "* [https://numpy.org/doc/stable/user/index.html](https://numpy.org/doc/stable/user/index.html)\n",
    "* **Funciones matemáticas**: [https://numpy.org/doc/stable/reference/routines.math.html](https://numpy.org/doc/stable/reference/routines.math.html)\n",
    "* **Estadística**: [https://numpy.org/doc/stable/reference/routines.statistics.html](https://numpy.org/doc/stable/reference/routines.statistics.html)\n",
    "* **Algebra lineal (numpy.linalg)**: [https://numpy.org/doc/stable/reference/routines.linalg.html](https://numpy.org/doc/stable/reference/routines.linalg.html)\n",
    "* **Generación de valores seudoaleatoriso (numpy.random)**: [https://numpy.org/doc/stable/reference/random/index.html](https://numpy.org/doc/stable/reference/random/index.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a73ce70-a263-4ed7-8896-1c914d23af19",
   "metadata": {},
   "source": [
    "`numpy` y `scipy` son dos paquetes de Python complementarios para computación científica y análisis de datos. `scipy` construye sobre `numpy` y proporciona una amplia gama de algoritmos y herramientas para la resolución de problemas en áreas como la optimización, la interpolación, la transformada de Fourier, el álgebra lineal y la estadística.\n",
    "\n",
    "Si ya conoce a profundidad todas las características del paquete `numpy`, puede consultar los detalles del paquete `scipy` en:\n",
    "\n",
    "* [https://docs.scipy.org/doc/scipy/reference/tutorial/index.html](https://docs.scipy.org/doc/scipy/reference/tutorial/index.html).\n",
    "* **Funciones especiales (scipy.special)**: [https://docs.scipy.org/doc/scipy/reference/special.html](https://docs.scipy.org/doc/scipy/reference/special.html).\n",
    "* **Integración (scipy.integrate)**: [https://docs.scipy.org/doc/scipy/reference/integrate.html](https://docs.scipy.org/doc/scipy/reference/integrate.html).\n",
    "* **Optimización (scipy.optimize)**: [https://docs.scipy.org/doc/scipy/reference/optimize.html](https://docs.scipy.org/doc/scipy/reference/optimize.html).\n",
    "* **Algebra lineal (scipy.linalg)**: [https://docs.scipy.org/doc/scipy/reference/linalg.html](https://docs.scipy.org/doc/scipy/reference/linalg.html).\n",
    "* **Estadística (scipy.stats)**: [https://docs.scipy.org/doc/scipy/reference/stats.html](https://docs.scipy.org/doc/scipy/reference/stats.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a079be2d-37ae-46ae-8c4b-597e888bd62b",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">¿Qué es un **arreglo** (_**array**_)?</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d7a94b7-fb2e-4e05-9334-0a2d518c541e",
   "metadata": {},
   "source": [
    "Un **arreglo** (_**array**_) es una **colección finita de datos del mismo tipo**, que se almacenan en posiciones consecutivas de memoria y reciben un nombre común. \n",
    "\n",
    "En el caso de un **arreglo unidimensional** estamos hablando de una estructura de datos que tendría cierta equivalencia o similaridad con el objeto matemático: **vector** $\\left( \\vec{v} = \\left(v_1, v_2, \\dots, v_n\\right) = \\left(v_i\\right)_{1 \\leq i \\leq n} \\right)$, ya que ambos corresponden a una secuencia de valores ordenados, que son indexados por un único índice asociado a una única dimensión.\n",
    "\n",
    "Por ejemplo, el arreglo unidimensional representado aquí:\n",
    "\n",
    "|         |         |     |     |     |     |\n",
    "|--------:|--------:|--------:|--------:|--------:|--------:|\n",
    "| índice: | **0**   | **1** | **2** | **3** | **4** |\n",
    "| valor:  | 1.2     | 3.4 | 5.6 | 7.8 | 9   |\n",
    "\n",
    "contiene la misma información o los mismos datos que el vector:\n",
    "\n",
    "$$\\vec{v} = \\left(v_1, v_2, \\dots, v_n\\right) = \\left(1.2, 3.4, 5.6, 7.8, 9\\right)$$\n",
    "\n",
    "Los **arreglos multidimensionales** son aquellos en donde cada elemento del arreglo está indexado por más de un índice (por más de un subíndice cuando los elementos del arreglo se denotan de la siguiente manera: $a_{i,j,k,\\dots}$\n",
    "\n",
    "Por ejemplo, el arreglo multidimensional (bidimensional) representado aquí:\n",
    "\n",
    "|             |           |     |     |     |\n",
    "|------------:|----------:|----:|----:|----:|\n",
    "|             | 2do índice: | **0** | **1** | **2** |\n",
    "| 1er índice: | **0**     | 1.2 | 3.4 | 5.6 |\n",
    "|             | **1**     | 7.8 | 9   | 0   |\n",
    "\n",
    "contiene la misma información o los mismos datos que la matriz:\n",
    "\n",
    "$$A = \\left(a_{ij}\\right)_{1 \\leq i \\leq n, 1 \\leq j \\leq m} = \\begin{bmatrix}\n",
    "1.2 & 3.4 & 5.6 \\\\\n",
    "7.8 & 9 & 0 \n",
    "\\end{bmatrix}$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d513ed8",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">¿Por qué usar arrays de numpy y no listas o tuplas de python?</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ebbff053",
   "metadata": {},
   "source": [
    "Las listas y tuplas nos podrían servir para el manejo de *arrays* pero las operaciones con listas o tuplas son demasiado lentas (lo que se gana en versatilidad se pierde en velocidad). Se ha dicho que las operaciones con arreglos de `numpy` es 50 veces más veloz que con listas."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c7d8268",
   "metadata": {},
   "source": [
    "La mayoría, sino todas, las librerías principales de Ciencia de Datos, Inteligencia Artíficial y Aprendizaje Profundo suelen trabajar con arrays de numpy u objetos similares."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15ebc1d8",
   "metadata": {
    "tags": []
   },
   "source": [
    "## <span style=\"color:#4361EE\">Creación básica de arrays</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8affc428-ddc4-466d-b4bd-906b53122746",
   "metadata": {},
   "source": [
    "Primero importamos (cargamos) el módulo `numpy` y le asociamos el alias `np`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "063e4b25",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Si no está instalado, lo instalamos primero:\n",
    "#!conda install numpy\n",
    "#!pip install numpy\n",
    "# Importamos:\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ff6fd69",
   "metadata": {
    "tags": []
   },
   "source": [
    "Vamos a crear arrays de distintas dimensiones. Para esto podemos usar números, listas o tuplas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0e2c124-ca36-41a3-b615-d2c05ca4f485",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.array(13) #array de dimensión 0\n",
    "print(\"type(a):\", type(a), \"\\n\")\n",
    "print(\"a:\", a, sep = \"\\n\")\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5856fb2b-922e-403b-9fbd-656767f860d9",
   "metadata": {},
   "outputs": [],
   "source": [
    "b = np.array([1, 2, 3, 4]) #array de dimensión 1\n",
    "print(\"b:\", b, sep = \"\\n\")\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53d19da3-2b55-4a97-b7f0-80a0bc29d794",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = np.array([[1, 2, 3, 4], [5, 6, 7, 8]]) #array de dimensión 2\n",
    "print(\"c:\", c, sep = \"\\n\")\n",
    "c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2784cd52-ade7-4ec2-a95f-365566d9d201",
   "metadata": {},
   "outputs": [],
   "source": [
    "d = np.array([[[1, 2, 3, 4], [5, 6, 7, 8]], [[9, 10, 11, 12], [13, 14, 15, 16]]]) #array de dimensión 3\n",
    "print(\"d:\", d, sep = \"\\n\")\n",
    "d"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52f9cff7",
   "metadata": {},
   "source": [
    "Los escalares son arreglos/tensores de dimensión 0, los vectores son arreglos/tensores de dimensión 1, las matrices son arreglos/tensores de dimensión 2, etc.\n",
    "\n",
    "El atributo `ndim` almacena la dimension del arreglo/tensor (no confundir con el tamaño de un vector o una matriz)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a60b3c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(a.ndim)\n",
    "print(b.ndim)\n",
    "print(c.ndim)\n",
    "print(d.ndim)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d882daf",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Indexación básica</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a04ab4cd",
   "metadata": {},
   "source": [
    "El proceso de indexación en numpy es similar al de las listas en python. Cada número implica buscar la posición en cada dimensión del array"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8019beb0",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"a =\", a)\n",
    "print(\"b[2] = \", b[2])\n",
    "print(\"c[1] =\", c[1])\n",
    "print(\"d[1] =\", d[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "894b28ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"a =\", a)\n",
    "print(\"b[2] =\", b[2])\n",
    "print(\"c[1,2] =\", c[1,2])\n",
    "print(\"d[1,0,2] =\", d[1,0,2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fcebd506",
   "metadata": {},
   "source": [
    "Similarmente podemos segmentar **arrays** como en las listas de python,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a0ee1fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(b[1:4]) #b = [1 2 3 4] \n",
    "print(c[1,1:3]) #c = [[1 2 3 4][5 6 7 8]] \n",
    "print(d[1:3,1:3,0:2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd589abc",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Diferentes tipos de datos en los arrays</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36a4ec09",
   "metadata": {},
   "source": [
    "Los arrays no están limitados a números enteros, pueden también tener cadenas, booleanos, números de punto flotante, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed2bb758",
   "metadata": {},
   "outputs": [],
   "source": [
    "frutas = np.array(['Manzana', 'Naranja', 'Uva']) #cadenas\n",
    "print(frutas.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b89c0e97",
   "metadata": {},
   "source": [
    "Podemos manipular el tipo de los datos del array dentro de la función de creación del array `np.array`, siempre y cuando el cambio sea posible. Por ejemplo podemos pasar enteros a cadenas (integers a strings)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75aadc1c",
   "metadata": {},
   "outputs": [],
   "source": [
    "number_to_string = np.array([6, 1, 2, 4, 623, 8], dtype=str) #b denota un string\n",
    "print(number_to_string.dtype)\n",
    "print(number_to_string)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1ebedf3",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    error_array = np.array(['a', '2', '3'], dtype=int)\n",
    "except:\n",
    "    print(\"hay un error en su lógica\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e2e1ac9",
   "metadata": {},
   "source": [
    "Podemos también cambiar el tipo en arrays ya existentes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7bd6b41a",
   "metadata": {},
   "outputs": [],
   "source": [
    "floating_array = np.array([1.1, 2.1, 3.1]) #punto flotante\n",
    "int_array = floating_array.astype(int)\n",
    "print(int_array)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63412d4a-7fee-44c1-8e4f-550b034902da",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector = np.array([127, -127, 32767, -32767], dtype=np.int8) # entero de 1 byte = 8 bits (1 bit se necesita para almacenar el signo)\n",
    "print('`vector`:\\t', vector)\n",
    "print('`vector.dtype`: ', vector.dtype)\n",
    "vector = np.array([127, -127, 32767, -32767], dtype=np.int16) # entero de 2 bytes = 16 bits (1 bit se necesita para almacenar el signo)\n",
    "print('`vector`:\\t', vector)\n",
    "print('`vector.dtype`: ', vector.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57d0475c",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Tamaño de los arrays</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "044a8738",
   "metadata": {},
   "source": [
    "El tamaño de un arreglos es diferente a su dimensión. El tamaño del arreglo es el número de elementos posibles por cada dimensión."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "232e7daf",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(a.shape) # a = 13 \n",
    "print(b.shape) # b = [1 2 3 4] \n",
    "print(c.shape) # c = [[1 2 3 4][5 6 7 8]] \n",
    "print(d.shape) # d = [[[ 1  2  3  4][ 5  6  7  8]] [[ 9 10 11 12][13 14 15 16]]] "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c250a66b-cec0-4dec-a644-d6d3ef2f09b3",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">Cambio de forma de los arreglos</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e218d0bb-4a33-47a0-8f9c-c3a0c1233abc",
   "metadata": {},
   "source": [
    "Podemos cambiar la forma de los arreglos. Esto significa aumentar el número de dimensiones o el número de elementos por dimensión."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d48ed33a-dc19-4a0d-aa37-d9e203ca0821",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"c =\", c, \"\\n\") # c = [[1 2 3 4][5 6 7 8]]\n",
    "c_1D = c.reshape(1, 8)\n",
    "print(\"c_1D =\", c_1D, \"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b103caa-0421-419b-9478-6ae4326ccc32",
   "metadata": {},
   "outputs": [],
   "source": [
    "c_4D = c.reshape(4, 2)\n",
    "print(\"c_4D =\", c_4D, \"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc0b1569",
   "metadata": {},
   "source": [
    "¿Podemos hacer cualquier cambio en la forma? Si, mientras la cantidad de elementos sea coincidente."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f37c4c94",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aeb96eb9",
   "metadata": {},
   "source": [
    "`d` es un array con 16 elementos, así que podemos hacer reshape con tamaños por ejemplo 1x16, 4x4, 8x2, 2x4x2."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4721bce",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    d.reshape(3, 5)\n",
    "except:\n",
    "    print('Hay un error al intentar cambiar la forma del arreglo')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "64a96f6c",
   "metadata": {},
   "outputs": [],
   "source": [
    "d.reshape(2, 2, 2, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3aef62e",
   "metadata": {},
   "source": [
    "Para no especificar explícitamente el número de elementos de una (solamente una) de las dimensiones se le puede dar el valor `-1` (solamente una vez) al método `reshape()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "adbaae7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "arr.reshape(3, -1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20bdffbb",
   "metadata": {},
   "source": [
    "Podemos usar dicho valor `-1` para \"aplanar\" el array a una dimensión."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fbbb4a61",
   "metadata": {},
   "outputs": [],
   "source": [
    "d.reshape(-1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10452895",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">Transpuesta</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "385e05ef",
   "metadata": {},
   "source": [
    "Para transponer el array utilizamos `.T`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c97dc11",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"c =\", c, \"\\n\")\n",
    "print(\"c.T =\", c.T)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9522f334",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Recorrer los valores de un arreglo</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1184880",
   "metadata": {},
   "source": [
    "Podemos usar los métodos tradicionales para recorrer los valores de un arreglo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6377f739",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "for x in b:\n",
    "    print(x, \"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ddf29d2-1c18-4062-9536-9c0498fe363b",
   "metadata": {},
   "outputs": [],
   "source": [
    "for x in c:\n",
    "    print(x, \"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eaadc2a3-d045-4cf0-b910-412e85c40ba9",
   "metadata": {},
   "outputs": [],
   "source": [
    "for x in d:\n",
    "    print(x, \"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d612edc4",
   "metadata": {},
   "source": [
    "Si queremos recorrer cada elemento para un arreglo de una o más dimensiones, necesitariamos un `for` por cada dimensión"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5f16a97",
   "metadata": {},
   "outputs": [],
   "source": [
    "for x in d:\n",
    "    for y in x:\n",
    "        for z in y:\n",
    "            print(z)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4f02b2d",
   "metadata": {},
   "source": [
    "Esto por supuesto no es óptimo. Así que numpy tiene la función `nditer()` para estos casos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f2f634c",
   "metadata": {},
   "outputs": [],
   "source": [
    "for x in np.nditer(d):\n",
    "    print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "279c4df0-e65d-421f-a01d-621fb206b03b",
   "metadata": {},
   "source": [
    "Se puede usar la función `ndenumerate`, en caso de que además de los valores dentro de un arreglo, también se requiera la indexación respectiva."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d454d38f-56ea-47c2-8a15-69ced48d7bf1",
   "metadata": {},
   "outputs": [],
   "source": [
    "for i, x in np.ndenumerate(d):\n",
    "    print('i:', i, '\\t\\tx:', x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3cf01179",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Generación de arreglos</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "263932d0",
   "metadata": {},
   "source": [
    "`numpy`permite crear arreglos específicos de forma rápida y fácil:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "142259ec-6507-49c7-a659-695cfb3b1c28",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`np.zeros((2,3,5))`:\\n{np.zeros((2,3,5))}\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ce190f30-7634-4aa7-9cc8-952b809ce44c",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`np.ones((2,3,5))`:\\n{np.ones((2,3,5))}\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "499a4c81-1c87-4a81-b404-6b959704242b",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`np.full((3,4), 1.23)`:\\n{np.full((2,3,5), 1.23)}\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37295843-46c9-4c15-ad42-520e5126a5c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`np.identity(3)`:\\n{np.identity(3)}\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "68d77c20-b1a8-4a14-8637-0f819356157b",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`np.eye(3)`:\\n{np.eye(3)}\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30843ce9-7517-497b-8e18-b13473e39331",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`np.empty((2,3,5))`:\\n{np.empty((2,3,5))}\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5f374d2-129e-4450-b6ad-6a492c680fa0",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`np.arange(0, 1, 0.1)`:\\n{np.arange(0, 1, 0.1)}\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3ecfc545-f41d-43fb-87b7-9c29338d865d",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`np.linspace(0, 1, 5)`:\\n{np.linspace(0, 1, 5)}\\n')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93ecf334",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Operaciones con arrays</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52f2041f",
   "metadata": {},
   "source": [
    "La función `where` devuelve los índices de los valores para los cuales se cumple una condición dada. Estos índices podrían ser utilizados para \"filtrar\". Sin embargo, si el único objetivo es \"filtrar\", se puede lograr usando una colección de valores booleanos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cdce4645-1cbd-4206-9400-0488ddaf7157",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "indx = np.where(vector%3 == 0)\n",
    "print(f'`type(indx)`:\\t{type(indx)}')\n",
    "print(f'`indx`:\\t{indx}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffadf055-90b7-4e24-930d-94d2cf3c9c44",
   "metadata": {},
   "outputs": [],
   "source": [
    "filtrado = vector[indx]\n",
    "print(f'`filtrado`:\\t{filtrado}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "513206d4-082e-430f-8d0e-1564b04cb51f",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector_bool = vector%3 == 0\n",
    "print(f'`type(vector_bool)`:\\t{type(vector_bool)}')\n",
    "print(f'`vector_bool`:\\t\\t{vector_bool}')\n",
    "filtrado = vector[vector_bool]\n",
    "print(f'`filtrado`:\\t\\t{filtrado}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b02ab9b1-fb42-480d-9d4e-ee5eeb7beec6",
   "metadata": {},
   "source": [
    "Además de proveer la clase `ndarray`, `numpy` contiene un conjunto de funciones (que suelen llamar **ufuncs**, *universal functions*) que se ejecutan elemento a elemento sin necesidad de utilizar un `for` o un `while` (cuando una función se ejecuta de esa manera se dice que está **vectorizada** o que es una **función vectorizada**). Es más eficiente usar una función vectorizada (si está muy bien hecha, probada, etc.) que iterar mediante un `while` o un `for`.\n",
    "\n",
    "* Operaciones aritmeticas: `add()`, `subtract()`, `multiply()`, `divide()`, `power()`, `mod()`, `remainder()`, `divmod()`, `absolute()`.\n",
    "* \"Redondeo\": `trunc()`, `fix()`, `around()`, `floor()`, `ceil()`.\n",
    "* Mínimo común múltiplo y máximo común divisor: `lcm()`, `lcm.reduce()`, `gcd()`, `gcd.reduce()`\n",
    "* Logaritmos y exponencial: `log2()`, `log10()`, `log()`, `exp()`.\n",
    "* Trigonométricas y relacionadas: `sin()`, `cos()`, `tan()`, `arcsin()`, `arccos()`, `arctan()`, `sinh()`, `cosh()`, `tanh()`, `arcsinh()`, `arccosh()`, `arctanh()`, `deg2rad()`, `rad2deg()`.\n",
    "* Conjuntos: `unique()`, `union1d()`, `intersect1d()`, `setdiff1d()`, `setxor1d()`.\n",
    "\n",
    "Todas las ufuncs tienen como parámetros adicionales:\n",
    "* `dtype` para dar el tipo de elementos para la salida.\n",
    "* `out` para dar un arreglo en donde la salida será copiada.\n",
    "* `where` para dar un arreglo booleano o una condición para seleccionar los elementos sobre los que se ejecutará la función."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5261ec38-5f2b-45b6-8ba2-219cb8b0fddd",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "# en vez de algo como:\n",
    "opt1 = [math.sin(i) for i in [1, 2, 3]]\n",
    "print(opt1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96a0d9e3-c55f-4dfe-9211-24f8bf357c76",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "# en vez de algo como:\n",
    "opt1 = [math.sin(i) for i in [1, 2, 3]]\n",
    "print(f'`opt1`: \\t{opt1}')\n",
    "print(f'`opt1[0]`: \\t{opt1[0]:.30f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93cd8548-7f04-43e1-8eb0-6812e7e867cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# es mucho más eficiente hacer algo como:\n",
    "opt2 = np.sin(np.array([1, 2, 3], dtype=np.uint8), dtype=np.float64)\n",
    "print(f'`opt2`: \\t{opt2}')\n",
    "print(f'`opt2[0]`:\\t{opt2[0]:.30f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6d7b47b3-b183-4dff-aeda-fed975b26fe0",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = np.array([1, 2, 3])\n",
    "y = np.array([0.0, 0.0, 0.0])\n",
    "np.sin(x, where = x%2==1, out = y)\n",
    "print(f'`y`: \\t{y}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bbcecfd9-2b2d-498f-9ed7-7b9fb4df11cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = np.array([1, 2, 3, 4])\n",
    "y = np.array([4, 5, 6, 7])\n",
    "z = np.add(x, y)\n",
    "print(f'`z`:\\t{z}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a85e98b6-f315-4c96-8f44-0845bd113db8",
   "metadata": {},
   "outputs": [],
   "source": [
    "z = x + y\n",
    "print(f'`z`:\\t{z}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66875d01-ce8e-4b6a-8c94-0cca4033637c",
   "metadata": {},
   "outputs": [],
   "source": [
    "z = np.add(x, y, dtype=float)\n",
    "print(f'`z`:\\t{z}', )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99ba0da0-9d07-463d-bae6-7879fc76eecd",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(np.array([1, 2, 3, 4]) + np.array([5, 6]))\n",
    "except:\n",
    "    print(\"No se pudo hacer la operación\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "438ede9a-271a-41aa-a6d8-37c33c9a4a61",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(np.array([1, 2, 3, 4]) + 0.9)\n",
    "except:\n",
    "    print(\"No se pudo hacer la operación\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5483e409-44b4-48d1-891a-d9d17acd9fd9",
   "metadata": {},
   "source": [
    "Adicionalmente tenemos las funciones: `sum()`, `cumsum()`, `prod()`, `cumprod()`, `diff()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2e9a42aa-b5ca-440c-8f2e-9ff208387632",
   "metadata": {},
   "outputs": [],
   "source": [
    "matriz = np.array([[3, 2, 4], [5, 2, 1], [8, 7, 6]])\n",
    "print(f'`matriz`:\\n{matriz}\\n')\n",
    "print(f'`np.prod(matriz)`:\\n{np.prod(matriz)}\\n')\n",
    "print(f'`np.prod(matriz, axis=0)`:\\n{np.prod(matriz, axis=0)}\\n')\n",
    "print(f'`np.prod(matriz, axis=1)`:\\n{np.prod(matriz, axis=1)}\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "140e7d61-d8a7-4eb9-8131-cf3c3a74d5f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'`matriz`:\\n{matriz}\\n')\n",
    "print(f'`np.cumprod(matriz)`:\\n{np.cumprod(matriz)}\\n')\n",
    "print(f'`np.cumprod(matriz, axis=0)`:\\n{np.cumprod(matriz, axis=0)}\\n')\n",
    "print(f'`np.cumprod(matriz, axis=1)`:\\n{np.cumprod(matriz, axis=1)}\\n')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b02f9ac-346d-478f-9c42-4b45d79d7d30",
   "metadata": {},
   "source": [
    "Además de las funciones para operaciones matemáticas, `numpy` también tiene funciones para realizar algunas operaciones que se suelen requerir desde la estadística (<https://numpy.org/doc/stable/reference/routines.statistics.html>)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eed0236f-15ab-43fd-a284-630ff365fd5e",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'media: {np.mean(matriz)}')\n",
    "print(f'desviación estándar muestral: {np.std(matriz, ddof=1)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c375aab5-1374-44df-b853-77c0108fb546",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">Multiplicación Matricial</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "147d0eab",
   "metadata": {},
   "source": [
    "Para la multiplicación de matrices usamos `@`, `matmul()`, o incluso `dot()`. Naturalmente, es necesario tener una especial atención a los tamaños de los arreglos a operar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7bf3c875-061f-4a97-9658-d38006ccac7e",
   "metadata": {},
   "outputs": [],
   "source": [
    "arr1 = np.array([[1,2,3],\n",
    "                [4,5,6]]) # tamaño 2,3\n",
    "arr2 = np.array([[6,5,4],\n",
    "                [3,2,1]]) # tamaño 2,3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8418705c-9936-4d18-87c5-37f90a9e8d4b",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(arr1 @ arr2.T) # 2,3 y 3,2. Resultado: 2,2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e22a87c-dc1a-4fe6-b40c-cb6fc01386f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(np.matmul(arr1, arr2.T))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "333b7abc",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(np.dot(arr1, arr2.T))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77a1b728-0d9e-4dd9-b566-8e3b284d63c8",
   "metadata": {},
   "source": [
    "Con `matmul` no se puede hacer la multiplicación $cA$, en donde $c$ es un escalar y $A$ una matriz."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a2090f9d-efbe-4932-8f59-c3fdb0421c6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(0.9 @ arr1) # float y arreglo de tamaño 2,3\n",
    "except:\n",
    "    print(\"No se pudo hacer la operación\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "90c290a3-1556-44c7-9210-e3d78e8e1224",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(np.array([0.9]) @ arr1) # tamaño 1 y tamaño 2,3\n",
    "except:\n",
    "    print(\"No se pudo hacer la operación\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d2c657e0-f92f-4d5e-af0c-800cf8199e0a",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(np.array([200, 100]) @ arr1) # tamaño 2 y tamaño 2,3\n",
    "except:\n",
    "    print(\"No se pudo hacer la operación\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "20a41f51-d8df-4a45-aa3e-d38e665e3436",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(np.matmul(arr1, np.array([10, 20, 30]))) # 2,3 y 3\n",
    "except:\n",
    "    print(\"No se pudo hacer la operación\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4553852c-dde1-4c86-b227-f707e1a8bedc",
   "metadata": {},
   "source": [
    "Aunque el resultado de `matmul()` y `dot()` coincide para arreglos de dimensión dos (matrices), `dot()` en general no hace lo mismo que `matmul()`. Para más información acerca de la función `dot()`, consultar <https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87e519a6-efe6-49f8-9053-456b7b6210cc",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Generación de valores pseudoaleatorios</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2af4516c-9367-4f10-8f5e-80221caf65aa",
   "metadata": {},
   "source": [
    "El módulo `random` del módulo `numpy` permite generar valores pseudoaleatorios (*¿cuál será la diferencia entre pseudoaleatorio y aleatorio?*)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ff5b887-de80-4d73-b8fd-0e8558a11901",
   "metadata": {},
   "outputs": [],
   "source": [
    "from numpy.random import default_rng"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4ad92f3-9a05-49c1-ac4c-ee30d0a4da6a",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">Generación de números enteros igualmente probables</span>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "542a8748-ec37-428b-9578-36513fbf9343",
   "metadata": {},
   "outputs": [],
   "source": [
    "semilla = 20230421\n",
    "rng = default_rng(semilla)\n",
    "enteros = rng.integers(low=1, high=10, size=(2,3,5))\n",
    "print(f'`type(enteros)`: {type(enteros)}')\n",
    "print(f'`enteros`:\\n{enteros}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe050d25-ec76-48bb-89ad-de25f7bf0875",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">Generación de números reales, entre cero y uno, igualmente probables</span>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2679f371-768a-4846-8b9a-148e377f84b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "flotantes = rng.random((3,4))\n",
    "print(f'`type(flotantes)`: {type(flotantes)}')\n",
    "print(f'`flotantes`:\\n{flotantes}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93e8618a-3833-48ae-96e0-b01367cf7866",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">Generación de una muestra aleatoria</span>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1db1718-a5f3-4897-9708-a377f02f5428",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(rng.choice.__doc__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ecdc6026-8878-48b6-ba93-07c2569e98b1",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(flotantes, \"\\n\")\n",
    "print(rng.choice(flotantes, 5, True, axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47ef495b-293c-4b77-97f0-7acee8cea533",
   "metadata": {},
   "source": [
    "### <span style=\"color:#4CC9F0\">Generación de valores pseudoaleatorios asociados a una distribución de probabilidad dada</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3258ce03-c6e6-4b4f-87f2-e42afd9e530b",
   "metadata": {},
   "source": [
    "Para conocer las distribuciones incluidas consultar: <https://numpy.org/doc/stable/reference/random/generator.html#distributions>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1435799e-8fc5-4162-8dbb-3a0896a23d88",
   "metadata": {},
   "source": [
    "Utilicemos la distribución normal (gaussiana):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "effe3fed-9601-4d4f-adff-ceed2831c23f",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = rng.normal(loc=0, scale=1, size=(3,3))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0a14ad9-3621-4995-ad81-1f7744163e64",
   "metadata": {},
   "source": [
    "Por ejemplo, supongamos que estamos interesados en la variable estatura de una población de personas y por alguna razón sabemos que dichas estaturas se distribuyen normal con media poblacional 170 cm y desviación estándar poblacional 8 cm. Generemos una muestra de 1000 estaturas pseudoaleatorias asociadas a dicha población:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49210789-2073-4c4b-8d01-35f50afc3c33",
   "metadata": {},
   "outputs": [],
   "source": [
    "muestra_estaturas = rng.normal(loc=170, scale=8, size=1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50577f6f-fbff-4bff-b2dc-60ada95e3423",
   "metadata": {},
   "source": [
    "Calculemos la media y la desviación (muestrales) de los datos generados:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e74f9595-7aa4-45d6-89fa-aa6761196dcd",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'media: {np.mean(muestra_estaturas)}')\n",
    "print(f'desviación estándar muestral: {np.std(muestra_estaturas, ddof = 1)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57c9077d-a58e-44b2-83b4-ad2502db735e",
   "metadata": {},
   "source": [
    "¿y si graficamos los datos generados?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b7ada72-2b7e-4596-ab80-e971cf3684ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "plot = sns.histplot(data = muestra_estaturas, kde = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2cbc5397-125a-4b2c-9d9b-a787acda15f9",
   "metadata": {},
   "source": [
    "## <span style=\"color:#4361EE\">Adicionales</span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "982a575d-8a8b-473e-8d2f-bf3a585ceab7",
   "metadata": {},
   "source": [
    "La función `concatenate()` nos sirve para \"pegar\" uno o más arreglos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2e0bc6a0-e237-439a-ad62-d2f5b7c221a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "matriz1 = np.array([[1, 2], [3, 4]])\n",
    "matriz2 = np.array([[5, 6], [7, 8]])\n",
    "res1 = np.concatenate((matriz1, matriz2), axis=0)\n",
    "print(f'`res1`:\\n{res1}')\n",
    "res2 = np.concatenate((matriz1, matriz2), axis=1)\n",
    "print(f'`res2`:\\n{res2}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4111f10b-4624-49cf-b9d3-f385484629eb",
   "metadata": {},
   "source": [
    "Las funciones `stack()`, `hstack()`, `vstack()` y `dstack()` también sirven para \"pegar\" uno o más arreglos (para más detalles consultar la documentación, por ejemplo: `np.stack.__doc__`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac613e71-79d6-4512-977d-b31429aa7203",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'{np.stack((matriz1, matriz2), axis=0)}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3e9bc8a-a6b9-4c7a-b308-33aae6ce3463",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'{np.stack((matriz1, matriz2), axis=1)}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "90731a0a-1cd1-4016-b5ca-144a8cb92c3d",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'{np.stack((matriz1, matriz2), axis=2)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f21c990-062f-481c-8ff9-c2c47962ef3a",
   "metadata": {},
   "source": [
    "La función `array_split()` sirve para \"dividir\"/\"partir\" un arreglo. También existen las funciones `vsplit()` y `dsplit()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63c67f6b-56dd-4907-b1c0-4f9e6877904c",
   "metadata": {},
   "outputs": [],
   "source": [
    "matriz = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]])\n",
    "print(f'`matriz`:\\n{matriz}')\n",
    "res = np.array_split(matriz, 3, axis=0)\n",
    "print(f'`type(res)`:\\t{type(res)}')\n",
    "for e in res:\n",
    "    print(f'`e`:\\n{e}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f741e987-582b-4f76-9645-ebee4a1eb48b",
   "metadata": {},
   "outputs": [],
   "source": [
    "matriz = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]])\n",
    "print(f'`matriz`:\\n{matriz}')\n",
    "res = np.array_split(matriz, 2, axis=1)\n",
    "print(f'`type(res)`:\\t{type(res)}')\n",
    "for e in res:\n",
    "    print(f'`e`:\\n{e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b49a48f-5bc8-49fa-b830-3150977fe304",
   "metadata": {},
   "source": [
    "Es posible convertir funciones propias en \"ufuncs\" mediante el uso de la función `frompyfunc()`. \n",
    "\n",
    "`frompyfunc()` es una función de orden superior que recibe tres parámetros: la función a convertir, el número de arreglos de entrada y el número de arreglos de salida que tendrá la función."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "baac9692-3078-4944-ad5e-330fbb6470fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = np.array([[1, 2], [3, 4]])\n",
    "y = np.array([[4, 5], [6, 7]])\n",
    "\n",
    "def mi_funcion_ejemplo(a, b):\n",
    "  return a**b / b**a\n",
    "\n",
    "ufunc_mi_funcion_ejemplo = np.frompyfunc(mi_funcion_ejemplo, 2, 1)\n",
    "print(f'`type(ufunc_mi_funcion_ejemplo)`:\\t{type(ufunc_mi_funcion_ejemplo)}', )\n",
    "print(f'`ufunc_mi_funcion_ejemplo(x, y)`:\\n{ufunc_mi_funcion_ejemplo(x, y)}', )"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  },
  "vscode": {
   "interpreter": {
    "hash": "6e8a17df5fde925ed372ff6e56ad9fe068706fee6b0f2460442b395fc94eb3eb"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}