├── df ├── __init__.py ├── number_internals │ ├── __init__.py │ └── binary.py ├── array_internals │ ├── __init__.py │ └── array_details.py └── display_algebra │ └── __init__.py ├── df_tools ├── __init__.py └── __main__.py ├── assets ├── 04 │ ├── misc.jpg │ ├── signed.jpg │ ├── spotify.png │ ├── unsigned.jpg │ ├── categories.jpg │ ├── chart-types.png │ ├── pie-chart.jpeg │ ├── unsigned-2d.jpg │ ├── landscape-colors.png │ ├── top2500feelings.png │ ├── grammar-of-graphics.png │ ├── technology-stocks.png │ ├── Playfair_TimeSeries-2.png │ └── sphx_glr_anatomy_001.webp ├── 05 │ ├── flower.png │ ├── vectores.png │ └── experiment.png ├── 07 │ ├── convexas.png │ ├── locality.png │ ├── modulair.jpg │ ├── adaptative.png │ ├── continuidad.png │ ├── non_convex.png │ ├── hard_and_soft.png │ ├── missing_grid.png │ ├── Keith_Emerson_&_Moog_15May10.jpg │ └── Hill_Climbing_with_Simulated_Annealing.gif ├── 08 │ └── BasicNN.png ├── 03 │ ├── flat_2d@1x.png │ ├── 2d_with_strides@1x.png │ └── flat_with_strides@1x.png ├── general │ ├── EnVivo@1x.png │ ├── GitHub@1x.png │ ├── Sharp@1x.png │ ├── Twitch@1x.png │ ├── Discord@1x.png │ ├── Twitter@1x.png │ └── YouTube@1x.png └── 02 │ ├── vector-tensor-matriz.jpg │ ├── stack-concatenate-valid-joining.png │ ├── filip-gielda-VPavA7BBxK0-unsplash.jpg │ ├── stack-concatenate-invalid-joining.png │ ├── alfred-kenneally-UsgLeLorRuM-unsplash.jpg │ └── NumPy.svg ├── docker-compose.yml ├── Dockerfile ├── Pipfile ├── Makefile ├── .gitignore ├── readme.md ├── 00-fundamentos-de-datos.ipynb ├── 01-jupyter-lab.ipynb ├── 08-optimizacion-parte-2.ipynb ├── 02a-numpy.ipynb ├── 03-numpy-tecnicalidades.ipynb ├── 05-algebra-lineal.ipynb └── 06-algebra-lineal-matrices.ipynb /df/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /df_tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/04/misc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/misc.jpg -------------------------------------------------------------------------------- /df/number_internals/__init__.py: -------------------------------------------------------------------------------- 1 | from df.number_internals.binary import show_float 2 | -------------------------------------------------------------------------------- /assets/04/signed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/signed.jpg -------------------------------------------------------------------------------- /assets/05/flower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/05/flower.png -------------------------------------------------------------------------------- /assets/04/spotify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/spotify.png -------------------------------------------------------------------------------- /assets/04/unsigned.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/unsigned.jpg -------------------------------------------------------------------------------- /assets/05/vectores.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/05/vectores.png -------------------------------------------------------------------------------- /assets/07/convexas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/convexas.png -------------------------------------------------------------------------------- /assets/07/locality.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/locality.png -------------------------------------------------------------------------------- /assets/07/modulair.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/modulair.jpg -------------------------------------------------------------------------------- /assets/08/BasicNN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/08/BasicNN.png -------------------------------------------------------------------------------- /df/array_internals/__init__.py: -------------------------------------------------------------------------------- 1 | from df.array_internals.array_details import show_array_details 2 | -------------------------------------------------------------------------------- /assets/03/flat_2d@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/03/flat_2d@1x.png -------------------------------------------------------------------------------- /assets/04/categories.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/categories.jpg -------------------------------------------------------------------------------- /assets/04/chart-types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/chart-types.png -------------------------------------------------------------------------------- /assets/04/pie-chart.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/pie-chart.jpeg -------------------------------------------------------------------------------- /assets/04/unsigned-2d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/unsigned-2d.jpg -------------------------------------------------------------------------------- /assets/05/experiment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/05/experiment.png -------------------------------------------------------------------------------- /assets/07/adaptative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/adaptative.png -------------------------------------------------------------------------------- /assets/07/continuidad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/continuidad.png -------------------------------------------------------------------------------- /assets/07/non_convex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/non_convex.png -------------------------------------------------------------------------------- /assets/07/hard_and_soft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/hard_and_soft.png -------------------------------------------------------------------------------- /assets/07/missing_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/missing_grid.png -------------------------------------------------------------------------------- /assets/general/EnVivo@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/general/EnVivo@1x.png -------------------------------------------------------------------------------- /assets/general/GitHub@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/general/GitHub@1x.png -------------------------------------------------------------------------------- /assets/general/Sharp@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/general/Sharp@1x.png -------------------------------------------------------------------------------- /assets/general/Twitch@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/general/Twitch@1x.png -------------------------------------------------------------------------------- /assets/04/landscape-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/landscape-colors.png -------------------------------------------------------------------------------- /assets/04/top2500feelings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/top2500feelings.png -------------------------------------------------------------------------------- /assets/general/Discord@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/general/Discord@1x.png -------------------------------------------------------------------------------- /assets/general/Twitter@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/general/Twitter@1x.png -------------------------------------------------------------------------------- /assets/general/YouTube@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/general/YouTube@1x.png -------------------------------------------------------------------------------- /assets/03/2d_with_strides@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/03/2d_with_strides@1x.png -------------------------------------------------------------------------------- /assets/04/grammar-of-graphics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/grammar-of-graphics.png -------------------------------------------------------------------------------- /assets/04/technology-stocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/technology-stocks.png -------------------------------------------------------------------------------- /assets/02/vector-tensor-matriz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/02/vector-tensor-matriz.jpg -------------------------------------------------------------------------------- /assets/03/flat_with_strides@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/03/flat_with_strides@1x.png -------------------------------------------------------------------------------- /assets/04/Playfair_TimeSeries-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/Playfair_TimeSeries-2.png -------------------------------------------------------------------------------- /assets/04/sphx_glr_anatomy_001.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/04/sphx_glr_anatomy_001.webp -------------------------------------------------------------------------------- /assets/07/Keith_Emerson_&_Moog_15May10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/Keith_Emerson_&_Moog_15May10.jpg -------------------------------------------------------------------------------- /assets/02/stack-concatenate-valid-joining.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/02/stack-concatenate-valid-joining.png -------------------------------------------------------------------------------- /assets/02/filip-gielda-VPavA7BBxK0-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/02/filip-gielda-VPavA7BBxK0-unsplash.jpg -------------------------------------------------------------------------------- /assets/02/stack-concatenate-invalid-joining.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/02/stack-concatenate-invalid-joining.png -------------------------------------------------------------------------------- /assets/02/alfred-kenneally-UsgLeLorRuM-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/02/alfred-kenneally-UsgLeLorRuM-unsplash.jpg -------------------------------------------------------------------------------- /assets/07/Hill_Climbing_with_Simulated_Annealing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatcsharpguy/df/HEAD/assets/07/Hill_Climbing_with_Simulated_Annealing.gif -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | df: 4 | build: . 5 | ports: 6 | - "8888:8888" 7 | volumes: 8 | - .:/workspace 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jupyter/base-notebook:python-3.8.8 as base 2 | 3 | WORKDIR /workspace 4 | 5 | COPY Pipfile.lock Pipfile ./ 6 | 7 | RUN pip install pipenv && \ 8 | pipenv install --system --deploy --ignore-pipfile 9 | 10 | CMD ["jupyter", "lab"] 11 | 12 | FROM base AS self-contained 13 | 14 | COPY *.ipynb ./ 15 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | ipympl = "*" 8 | jupyterlab = "*" 9 | matplotlib = "*" 10 | seaborn = "*" 11 | pandas = "*" 12 | scikit-learn = "*" 13 | 14 | [dev-packages] 15 | click = "*" 16 | isort = "*" 17 | black = ">18.*" 18 | jupyterlab-spellchecker = "*" 19 | colour = "*" 20 | 21 | [requires] 22 | python_version = "3.8" 23 | -------------------------------------------------------------------------------- /df/array_internals/array_details.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def show_array_details(arr: np.array): 5 | details = [ 6 | ("Shape", arr.shape), 7 | ("Stride", arr.strides), 8 | ("Type", arr.dtype), 9 | ("Elements", arr.size), 10 | ("Bytes", arr.nbytes), 11 | ("Bits", arr.itemsize * 8), 12 | ] 13 | 14 | for label, value in details: 15 | print(f"{(label+':').ljust(12)}{str(value)}") 16 | print() 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NB_FILES=$(shell find . -path "./*.ipynb" -not -path "./.ipynb_checkpoints/*") 2 | PY_FILES=$(shell find . -path "./*.py" -not -path "./.ipynb_checkpoints/*") 3 | MD_FILES=$(shell find . -type f -regex '.*(m|h)$') 4 | 5 | build: 6 | docker build --target self-contained -t data-fundamentals:self-contained . 7 | docker build --target base -t data-fundamentals . 8 | 9 | md: 10 | pipenv run python -m df_tools markdown 11 | 12 | style: 13 | pipenv run isort df df_utils 14 | pipenv run black $(PY_FILES) 15 | 16 | clean: style 17 | pipenv run python -m df_tools clean 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # PyCharm 2 | .idea/* 3 | 4 | # pyenv configuration file 5 | .python-version 6 | 7 | ### JupyterNotebooks ### 8 | .ipynb_checkpoints 9 | */.ipynb_checkpoints/* 10 | 11 | # IPython 12 | profile_default/ 13 | ipython_config.py 14 | ### End JupyterNotebooks ### 15 | 16 | .DS_Store 17 | 18 | # Generated files 19 | temporary.py 20 | multidimensional.txt 21 | 22 | # Markdown 23 | *.md 24 | *_files/* 25 | 26 | ### Python ### 27 | # Byte-compiled / optimized / DLL files 28 | __pycache__/ 29 | *.py[cod] 30 | *$py.class 31 | 32 | # Private assests 33 | *.drawio 34 | *.pxm 35 | *.ai 36 | *.sketch 37 | *.svg 38 | -------------------------------------------------------------------------------- /df/display_algebra/__init__.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib.patches import Ellipse 3 | 4 | 5 | def show_vector(ax, start, end, label, color=None, **kwargs): 6 | lines = ax.plot(end[0], end[1], "o", label=label, color=color, **kwargs) 7 | if color is None: 8 | color = lines[0].get_color() 9 | ax.arrow( 10 | start[0], 11 | start[1], 12 | end[0] - start[0], 13 | end[1] - start[1], 14 | head_width=0.1, 15 | width=0.02, 16 | overhang=0.2, 17 | length_includes_head=True, 18 | color=color, 19 | **kwargs 20 | ) 21 | 22 | 23 | def show_normed_vects(ax, vects, norm=2): 24 | unit_vects = ( 25 | vects.T / np.linalg.norm(vects, norm, axis=1) 26 | ).T # a random unit vector 27 | 28 | for a1, b1 in zip(vects, unit_vects): 29 | ax.plot( 30 | [a1[0], b1[0]], [a1[1], b1[1]], linestyle="dotted", color="black", lw=0.5 31 | ) 32 | 33 | ax.scatter(vects[:, 0], vects[:, 1], s=5) 34 | ax.scatter(unit_vects[:, 0], unit_vects[:, 1], s=5, color="green") 35 | ax.plot([0], [0], ".", color="red") 36 | 37 | ax.set_ylim((-3, 3)) 38 | ax.set_xlim((-3, 3)) 39 | ax.set_aspect(1.0) 40 | 41 | 42 | def eigsorted(cov): 43 | vals, vects = np.linalg.eigh(cov) 44 | order = vals.argsort()[::-1] 45 | return vals[order], vects[:, order] 46 | 47 | 48 | def draw_covariance_ellipse(ax, x, n_std): 49 | cov = np.cov(x.T) 50 | vals, vects = eigsorted(cov) 51 | angle = np.degrees(np.arctan2(*vects[:, 0][::-1])) 52 | width, height = 2 * n_std * np.sqrt(vals) 53 | ellipse = Ellipse( 54 | xy=(x[:, 0].mean(), x[:, 1].mean()), 55 | width=width, 56 | height=height, 57 | edgecolor="black", 58 | facecolor="none", 59 | angle=angle, 60 | ) 61 | 62 | ax.add_artist(ellipse) 63 | -------------------------------------------------------------------------------- /df/number_internals/binary.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import numpy as np 4 | 5 | SIZE_TYPES = {np.float64: (11, 53), np.float32: (8, 24), np.float128: (15, 113)} 6 | 7 | 8 | def join(*args): 9 | return " | ".join(args) 10 | 11 | 12 | def show_float(number: float, type_=np.float64): 13 | show_binary_float( 14 | number, number_to_bitstring(np.array(number, dtype=type_)), *SIZE_TYPES[type_] 15 | ) 16 | 17 | 18 | def bit_word_to_mantissa_val(bitword: str) -> float: 19 | mantissa = int(bitword, base=2) 20 | return mantissa / (2.0 ** len(bitword)) 21 | 22 | 23 | def show_binary_float( 24 | expected_number: float, bit_word: str, exponent_size: int, mantissa_size: int 25 | ): 26 | mantissa_size -= 1 27 | bias = (2 ** (exponent_size - 1)) - 1 28 | total_width = exponent_size + mantissa_size + 1 29 | 30 | bit_chunks = slice_string(bit_word, 1, exponent_size, mantissa_size) 31 | 32 | sign = -1 if bit_word[0:1] == "1" else 1 33 | exponent = int(bit_word[1 : 1 + exponent_size], base=2) - bias 34 | mantissa = bit_word_to_mantissa_val(bit_word[1 + exponent_size : total_width]) 35 | infinite = exponent == 2 ** (exponent_size - 1) 36 | 37 | print( 38 | join( 39 | "Sign".ljust(4), "Exp".ljust(exponent_size), "Mantissa".ljust(mantissa_size) 40 | ) 41 | ) 42 | print(f" {join(*bit_chunks)}") 43 | print( 44 | join(f"{sign:4d}", f"{exponent:{exponent_size}}", f"{mantissa:{mantissa_size}}") 45 | ) 46 | if infinite: 47 | print("Infinite/NaN") 48 | else: 49 | print(f" = {sign} * {1 + mantissa} * 2^{exponent}") 50 | print(f" = {sign} * {1.0 + mantissa} * {2 ** exponent}") 51 | print(f" = {expected_number:.20f}") 52 | 53 | print() 54 | 55 | 56 | def slice_string(word: str, *steps: List[int]) -> List[str]: 57 | out = [] 58 | word_length = len(word) 59 | original_length = sum(steps) 60 | steps = list(steps) 61 | 62 | if original_length < word_length: 63 | steps.append(word_length - original_length) 64 | 65 | i = 0 66 | for step in steps: 67 | out.append(word[i : i + step]) 68 | i = i + step 69 | 70 | return out 71 | 72 | 73 | def number_to_bitstring(x: np.array) -> str: 74 | endianess = x.dtype.newbyteorder("B") 75 | return "".join(f"{d:08b}" for d in x.astype(endianess).tobytes()) 76 | -------------------------------------------------------------------------------- /assets/02/NumPy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /df_tools/__main__.py: -------------------------------------------------------------------------------- 1 | import click 2 | from pathlib import Path 3 | import nbformat 4 | import sys 5 | import asyncio 6 | from nbconvert import MarkdownExporter, HTMLExporter, NotebookExporter, preprocessors 7 | from black import format_str, FileMode, InvalidInput 8 | import re 9 | 10 | from traitlets.config import Config 11 | from nbconvert.preprocessors import ClearOutputPreprocessor 12 | 13 | FILE_PATTERN = re.compile(r"^[0-9]{2}\w?-[a-zA-Z-]+$") 14 | 15 | # See https://bugs.python.org/issue37373 :( 16 | if ( 17 | sys.version_info[0] == 3 18 | and sys.version_info[1] >= 8 19 | and sys.platform.startswith("win") 20 | ): 21 | asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) 22 | 23 | 24 | @click.group() 25 | def cli(): 26 | pass 27 | 28 | 29 | def get_files(extension: str): 30 | for file in Path(".").glob(f"*.{extension}"): 31 | if not FILE_PATTERN.match(file.stem): 32 | continue 33 | yield file 34 | 35 | 36 | @cli.command() 37 | def markdown(): 38 | executor = preprocessors.ExecutePreprocessor() 39 | exporter = MarkdownExporter() 40 | for notebook in get_files("ipynb"): 41 | with open(notebook, encoding="utf8") as nb_file: 42 | nb = nbformat.read(nb_file, as_version=4) 43 | executor.preprocess(nb) 44 | 45 | if not nb.cells[-1]["source"]: 46 | nb.cells.pop() 47 | 48 | markdown, _ = exporter.from_notebook_node(nb) 49 | with open(f"{notebook.stem}.md", "w", encoding="utf8") as writable: 50 | writable.write(markdown) 51 | 52 | 53 | @cli.command() 54 | def html(): 55 | executor = preprocessors.ExecutePreprocessor() 56 | exporter = HTMLExporter() 57 | for notebook in get_files("ipynb"): 58 | with open(notebook, encoding="utf8") as nb_file: 59 | nb = nbformat.read(nb_file, as_version=4) 60 | executor.preprocess(nb) 61 | 62 | if not nb.cells[-1]["source"]: 63 | nb.cells.pop() 64 | html, _ = exporter.from_notebook_node(nb) 65 | with open(f"{notebook.stem}.html", "w", encoding="utf8") as writable: 66 | writable.write(html) 67 | 68 | 69 | def _delete_generated_files(): 70 | for md in get_files("md"): 71 | md.unlink() 72 | for html in get_files("html"): 73 | html.unlink() 74 | 75 | 76 | def format_code_cells(notebobk): 77 | file_mode = FileMode() 78 | for cell in notebobk.cells: 79 | if cell["cell_type"] == "code": 80 | tags = cell.get("metadata", dict()).get("tags", []) 81 | if "not-formatted" not in tags: 82 | try: 83 | result = format_str(cell["source"], mode=file_mode) 84 | if result[-1] == "\n" and cell["source"] != "\n": 85 | result = result[:-1] 86 | cell["source"] = result 87 | except InvalidInput: 88 | pass 89 | 90 | 91 | @cli.command() 92 | def clean(): 93 | _delete_generated_files() 94 | config = Config() 95 | config.NotebookExporter.preprocessors = [ClearOutputPreprocessor()] 96 | exporter = NotebookExporter(config=config) 97 | 98 | for notebook in get_files("ipynb"): 99 | with open(notebook, encoding="utf8") as nb_file: 100 | nb = nbformat.read(nb_file, as_version=4) 101 | 102 | if not nb.cells[-1]["source"]: 103 | nb.cells.pop() 104 | 105 | format_code_cells(nb) 106 | 107 | for cell_id, cell in enumerate(nb.cells): 108 | cell["id"] = f"{notebook.stem}-{cell_id}" 109 | 110 | ipynb, _ = exporter.from_notebook_node(nb) 111 | with open(f"{notebook.stem}.ipynb", "w", encoding="utf8") as writable: 112 | writable.write(ipynb) 113 | 114 | 115 | if __name__ == "__main__": 116 | cli() 117 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 00: Fundamentos de datos 🤔 2 | 3 | > Una breve introducción a las herramientas para hacer ciencia de datos. 4 | 5 | Como el nombre lo dice: son los fundamentos. No vamos a hacer **nada 👏 NADA 👏** de *machine learning*, pero vamos a ver las herramientas que ayudan a entender y a implementarlo. 6 | 7 | El curso tiene un enfoque netamente orientado a las ciencias de la computación con muchísimo énfasis en programación; hablaré un poco de matemáticas, pero nada demasiado teórico puesto que tampoco me considero un experto. 8 | 9 | ## ¿Por qué? 🤔🤔 10 | 11 | Quiero hacer algo diferente a solo los tutoriales que uno ve por aquí y por allá y hablar sobre los fundamentos que hay detrás de las herramientas que usamos. Este curso está inspirado en la asignatura del mismo nombre que tomé en la Universidad de Glasgow ([COMPSCI4073](https://www.gla.ac.uk/undergraduate/degrees/computingscience/?card=course&code=COMPSCI4073)). 12 | 13 | ## Prerequisitos 14 | 15 | - ¡Programación! - Python 🐍: Idealmente tienes conocimiento sobre programación, el lenguaje es relativamente sencillo de aprender y en general no vamos a usar características oscuras del lenguaje (las bibliotecas que vamos a usar lo hacen por nosotros) 16 | - Matemáticas: Como ya lo mencioné, el curso está orientado primordialmente a resolver problemas desde un enfoque computacional, haré mi mejor esfuerzo por explicar los temas de álgebra lineal, y el poco cálculo y probabilidad que veamos, si tú tienes conocimiento sobre estas tres áreas, la cosa te será más fácil. Además de que compartiré recursos adicionales que te pueden resultar de utilidad). 17 | 18 | ## Herramientas principales ⚙ 19 | 20 | - Python 3.8 21 | - [Jupyter Lab](https://jupyter.org/install) 22 | 23 | **Opcional: Docker.** Si tienes problemas instalando las dependencias pondré una imagen de *Docker* lista para ser usada, junto con instrucciones sobre cómo usarla. 24 | 25 | ## Formato 📃 26 | 27 | - Todo se verá en forma de *notebooks*, cada semana pondré el (probablemente en viernes por la tarde) antes de la "clase"; para enterarte ve la sección de contacto. 28 | - Sesiones sabatinas en vivo de ~1.5 horas, todos los sábados 10 AM tiempo del Centro de México (la primera siendo el día 6 de marzo de 2021) en las cuales voy a explicar los temas 29 | - Muy a mi pesar, no podré tomar tantas preguntas directamente sobre lo que estamos viendo, pero seguir discutiendo en el Discord (si hay posibilidad puedo organizar un directo para resolver preguntas que me hayan llegado previamente). 30 | - Las grabaciones quedan en YouTube 31 | - No hay tarea (no tengo tiempo de revisarla 😂) 32 | 33 | ## Temario 🗂 34 | 35 | 1. Jupyter Lab 36 | 2. Cómputo vectorizado 37 | 3. Visualización de datos 38 | 4. Álgebra Lineal 39 | 5. Optimización numérica 40 | 6. Probabilidad 41 | 7. Procesamiento de señales (???) 42 | 43 | ## Sesiones 🏫 44 | 45 | - 00: Introducción al curso, [https://youtu.be/RT1VpNAwEWg](https://youtu.be/RT1VpNAwEWg) 46 | - 00a: Jupyter y Docker, [https://youtu.be/ppl2y4iP2yE](https://youtu.be/ppl2y4iP2yE) 47 | - 01: Jupyter Lab, [https://youtu.be/QclZayjU8K8](https://youtu.be/QclZayjU8K8) 48 | - 02: Cómputo vectorizado, [https://youtu.be/iLIQykiASs4](https://youtu.be/iLIQykiASs4) 49 | - 02a: Más funcionalidades de NumPy - Data Fundamentals, [https://youtu.be/0_QIYyMBZN0](https://youtu.be/0_QIYyMBZN0) 50 | - 03: Números y NumPy, [https://youtu.be/YwHQC4KEtBE](https://youtu.be/YwHQC4KEtBE) 51 | - 04: Haciendo buenas gráficas, [https://youtu.be/7vZY6JdswKs](https://youtu.be/7vZY6JdswKs) 52 | - 05: Vectores y Álgebra Lineal, [https://youtu.be/bgEML8Re2ks](https://youtu.be/bgEML8Re2ks) 53 | - 06: Matrices y Álgebra Lineal, [https://youtu.be/Vdn-8j8yFSk](https://youtu.be/Vdn-8j8yFSk) 54 | - 07: Optimización I, [https://youtu.be/tzKtffhJ31Y](https://youtu.be/tzKtffhJ31Y) 55 | - 08: Optimización II, [https://youtu.be/J4eCD38WF5k](https://youtu.be/J4eCD38WF5k) 56 | 57 | ## Libros 📚 58 | 59 | En este curso vamos a hablar de muchos temas y por tanto no hay un solo libro que te pueda decir "este, este tiene todo", sin embargo, si tienes ganas locas de leer algo para complementar tu aprendizaje, te recomiendo estos: 60 | 61 | ⚠ PRÓXIMAMENTE ⚠ 62 | 63 | ## Contacto ☎ 64 | 65 | - Mi cuenta de Twitter: [@io_exception](https://twitter.com/io_exception) 66 | - Mi canal de YouTube: [That C# guy](https://www.youtube.com/channel/UC8KCb358oioQMcJ5pUfs8UQ) 67 | - El discord para preguntas y la comunidad: [https://tcsg.dev/discord](https://tcsg.dev/discord) 68 | - Playlist en YouTube sobre el curso: [Fundamentos de datos en YouTube](https://youtube.com/playlist?list=PL6cBnnS2SIgrIUumF2WDTDIiH_lODkLGq) 69 | - Repositorio de las *notebooks*: [thatcsharpguy/df](https://github.com/thatcsharpguy/df) 70 | 71 | ## ¿Costo? 🤑 72 | 73 | **¡Totalmente gratis!** pero habiendo dicho esto, si quieres puedes suscribirte como miembro a mi canal de YouTube acá [youtube.com/thatcsharpguy/join](https://www.youtube.com/thatcsharpguy/join) (mil gracias a los miembros actuales) o aventarme dinero al sombrero acá: https://www.buymeacoffee.com/thatcsharpguy 74 | 75 | Creative Commons Licence
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 76 | -------------------------------------------------------------------------------- /00-fundamentos-de-datos.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "00-fundamentos-de-datos-0", 6 | "metadata": {}, 7 | "source": [ 8 | "# 00: Fundamentos de datos 🤔 \n", 9 | " \n", 10 | "\n", 11 | " \n", 12 | " \n", 13 | " \n", 18 | " \n", 23 | " \n", 28 | " \n", 33 | " \n", 38 | " \n", 43 | " \n", 48 | " \n", 49 | " \n", 50 | "
\n", 14 | " \n", 15 | " \n", 16 | " \n", 17 | " \n", 19 | " \n", 20 | " \n", 21 | " \n", 22 | " \n", 24 | " \n", 25 | " \n", 26 | " \n", 27 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | "
\n", 51 | "\n", 52 | " > Una breve introducción a las herramientas para hacer ciencia de datos.\n", 53 | "\n", 54 | "Como el nombre lo dice: son los fundamentos. No vamos a hacer **nada 👏 NADA 👏** de *machine learning*, pero vamos a ver las herramientas que ayudan a entender y a implementarlo.\n", 55 | "\n", 56 | "El curso tiene un enfoque netamente orientado a las ciencias de la computación con muchísimo énfasis en programación; hablaré un poco de matemáticas, pero nada demasiado teórico puesto que tampoco me considero un experto. \n", 57 | "\n", 58 | "## ¿Por qué? 🤔🤔\n", 59 | "\n", 60 | "Quiero hacer algo diferente a solo los tutoriales que uno ve por aquí y por allá y hablar sobre los fundamentos que hay detrás de las herramientas que usamos. Este curso está inspirado en la asignatura del mismo nombre que tomé en la Universidad de Glasgow ([COMPSCI4073](https://www.gla.ac.uk/undergraduate/degrees/computingscience/?card=course&code=COMPSCI4073)). \n", 61 | "\n", 62 | "## Prerequisitos\n", 63 | "\n", 64 | "- ¡Programación! - Python 🐍: Idealmente tienes conocimiento sobre programación, el lenguaje es relativamente sencillo de aprender y en general no vamos a usar características oscuras del lenguaje (las bibliotecas que vamos a usar lo hacen por nosotros)\n", 65 | "- Matemáticas: Como ya lo mencioné, el curso está orientado primordialmente a resolver problemas desde un enfoque computacional, haré mi mejor esfuerzo por explicar los temas de álgebra lineal, y el poco cálculo y probabilidad que veamos, si tú tienes conocimiento sobre estas tres áreas, la cosa te será más fácil. Además de que compartiré recursos adicionales que te pueden resultar de utilidad).\n", 66 | "\n", 67 | "## Herramientas principales ⚙\n", 68 | "\n", 69 | "- Python 3.8\n", 70 | "- [Jupyter Lab](https://jupyter.org/install)\n", 71 | "\n", 72 | "**Opcional: Docker.** Si tienes problemas instalando las dependencias pondré una imagen de *Docker* lista para ser usada, junto con instrucciones sobre cómo usarla.\n", 73 | "\n", 74 | "## Formato 📃\n", 75 | "\n", 76 | " - Todo se verá en forma de *notebooks*, cada semana pondré el (probablemente en viernes por la tarde) antes de la \"clase\"; para enterarte ve la sección de contacto.\n", 77 | " - Sesiones sabatinas en vivo de ~1.5 horas, todos los sábados 10 AM tiempo del Centro de México (la primera siendo el día 6 de marzo de 2021) en las cuales voy a explicar los temas\n", 78 | " - Muy a mi pesar, no podré tomar tantas preguntas directamente sobre lo que estamos viendo, pero seguir discutiendo en el Discord (si hay posibilidad puedo organizar un directo para resolver preguntas que me hayan llegado previamente).\n", 79 | " - Las grabaciones quedan en YouTube \n", 80 | " - No hay tarea (no tengo tiempo de revisarla 😂)\n", 81 | " \n", 82 | "## Temario 🗂\n", 83 | "\n", 84 | "1. Jupyter Lab\n", 85 | "2. Cómputo vectorizado\n", 86 | "3. Visualización de datos\n", 87 | "4. Álgebra Lineal\n", 88 | "5. Optimización numérica\n", 89 | "6. Probabilidad\n", 90 | "7. Procesamiento de señales (???)\n", 91 | "\n", 92 | "## Sesiones 🏫\n", 93 | "\n", 94 | " - 01: Jupyter Lab (Sábado 13 de marzo 2021)\n", 95 | " - 02: Cómputo vectorizado (Sábado 20 de marzo de 2021)\n", 96 | " \n", 97 | "## Libros 📚\n", 98 | "\n", 99 | "En este curso vamos a hablar de muchos temas y por tanto no hay un solo libro que te pueda decir \"este, este tiene todo\", sin embargo, si tienes ganas locas de leer algo para complementar tu aprendizaje, te recomiendo estos:\n", 100 | "\n", 101 | "\n", 102 | " - **Elegant SciPy: The Art of Scientific Python**: [México](https://amzn.to/3c0ZNZM) · [España](https://amzn.to/30WWmge) · [US](https://amzn.to/2PbkbhC) \n", 103 | " - **High Performance Python: Practical Performant Programming for Humans**: [México](https://amzn.to/310hEtt) · [España](https://amzn.to/3cVZsXD) · [US](https://amzn.to/3s9eUFW) \n", 104 | "\n", 105 | "\n", 106 | "## Contacto ☎\n", 107 | "\n", 108 | " - Mi cuenta de Twitter: [@io_exception](https://twitter.com/io_exception)\n", 109 | " - Mi canal de YouTube: [That C# guy](https://www.youtube.com/channel/UC8KCb358oioQMcJ5pUfs8UQ)\n", 110 | " - El discord para preguntas y la comunidad: [https://tcsg.dev/discord](https://tcsg.dev/discord)\n", 111 | " - Playlist en YouTube sobre el curso: [Fundamentos de datos en YouTube](https://youtube.com/playlist?list=PL6cBnnS2SIgrIUumF2WDTDIiH_lODkLGq)\n", 112 | " - Repositorio de las *notebooks*: [thatcsharpguy/df](https://github.com/thatcsharpguy/df)\n", 113 | " \n", 114 | "## ¿Costo? 🤑\n", 115 | "\n", 116 | "**¡Totalmente gratis!** pero habiendo dicho esto, si quieres puedes suscribirte como miembro a mi canal de YouTube acá [youtube.com/thatcsharpguy/join](https://www.youtube.com/thatcsharpguy/join) (mil gracias a los miembros actuales) o aventarme dinero al sombrero acá: https://www.buymeacoffee.com/thatcsharpguy o acá https://creadores.fans/thatcsharpguy 😎\n", 117 | "\n", 118 | "\"Creative
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.\n" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "id": "00-fundamentos-de-datos-1", 124 | "metadata": {}, 125 | "source": [ 126 | " \n", 127 | "\n", 128 | " \n", 129 | " \n", 130 | " \n", 135 | " \n", 140 | " \n", 145 | " \n", 150 | " \n", 155 | " \n", 160 | " \n", 165 | " \n", 166 | " \n", 167 | "
\n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | "
" 168 | ] 169 | } 170 | ], 171 | "metadata": { 172 | "kernelspec": { 173 | "display_name": "Python 3", 174 | "language": "python", 175 | "name": "python3" 176 | }, 177 | "language_info": { 178 | "codemirror_mode": { 179 | "name": "ipython", 180 | "version": 3 181 | }, 182 | "file_extension": ".py", 183 | "mimetype": "text/x-python", 184 | "name": "python", 185 | "nbconvert_exporter": "python", 186 | "pygments_lexer": "ipython3", 187 | "version": "3.8.6" 188 | } 189 | }, 190 | "nbformat": 4, 191 | "nbformat_minor": 5 192 | } 193 | -------------------------------------------------------------------------------- /01-jupyter-lab.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "01-jupyter-lab-0", 6 | "metadata": {}, 7 | "source": [ 8 | "# 01: Jupyter Lab \n", 9 | "\n", 10 | "\n", 11 | " \n", 12 | " \n", 13 | " \n", 18 | " \n", 23 | " \n", 28 | " \n", 33 | " \n", 38 | " \n", 43 | " \n", 48 | " \n", 49 | " \n", 50 | "
\n", 14 | " \n", 15 | " \n", 16 | " \n", 17 | " \n", 19 | " \n", 20 | " \n", 21 | " \n", 22 | " \n", 24 | " \n", 25 | " \n", 26 | " \n", 27 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | "
" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "01-jupyter-lab-1", 56 | "metadata": {}, 57 | "source": [ 58 | "## Entornos virtuales - conda vs. pipenv \n", 59 | "\n", 60 | "Idealmente todo el código se ejecuta dentro de un entorno aislado del resto de los programas y aplicaciones instalados en tu computadora, evitando así que existan conflictos entre dependencias. Esto nos ayuda a dos cosas: nos facilita la vida evitando conflictos entre dependencias y, lo más importante en la ciencia de datos, **ayuda en la reproducibilidad de nuestros experimentos y análisis**. \n", 61 | "\n", 62 | "He hablado sobre entornos virtuales en el pasado: un [video sobre ellos](https://youtu.be/GM-RcOaGN4w) y [un directo también](https://youtu.be/ihG0Ya_h188).\n", 63 | "\n", 64 | "En el mundo científico-pythonista el gestor de entornos y paquetes por excelencia es [Conda](https://docs.conda.io/en/latest/); sin embargo, yo tengo una preferencia personal por [Pipenv](https://pipenv.pypa.io/en/latest/) pues es más ligero, minimalista y completamente enfocado en Python. *Potatos, potatoes* cualquier gestor de paquetes que decidas usar es bueno.\n", 65 | "\n", 66 | "**Docker**: Puedes ejecutar estos notebooks en un [contenedor de Docker](https://youtu.be/ppl2y4iP2yE) también." 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "id": "01-jupyter-lab-2", 72 | "metadata": {}, 73 | "source": [ 74 | "## Paquetes \n", 75 | "\n", 76 | "(Entre las primeras cosas que mencionaré en cada uno de los notebooks estará una sección llamada \"Paquetes\" en la cual listaré los paquetes que vamos a usar durante la sesión, esta información es solo como referencia, los paquetes ya deberían estar instalados si instalaste los requerimientos correctamente desde el inicio)\n", 77 | "\n", 78 | " - `jupyterlab` \n", 79 | " - `matplotlib`\n", 80 | " - `ipympl`\n", 81 | " - `numpy`\n", 82 | "\n", 83 | "(Dependiendo del gestor de paquetes que uses tendrás que instalarlos manualmente, por ejemplo, `pipenv install jupyterlab` instala `jupyterlab` usando *pipenv*) " 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "id": "01-jupyter-lab-3", 89 | "metadata": {}, 90 | "source": [ 91 | "## Qué es Jupyter Lab\n", 92 | "\n", 93 | "*Jupyter Lab* es la nueva interfaz gráfica que nos permite interactuar con los *Jupyter Notebooks*. Estos *notebooks* son una versión mas ... para interactuar con la consola de Python. Cada *notebook* tiene asociada una consola de Python (también conocido como *kernel*) que está activa y esperando a que nosotros le mandemos comandos usando la interfaz gráfica de nuestros *notebooks*.\n", 94 | "\n", 95 | "Las *Notebooks* son una implementación del paradigma de [programación letrada](https://en.wikipedia.org/wiki/Literate_programming) ideado por Donald Knuth. Esto se logra mediante el uso de **celdas (cells)** en donde cada una de estas celdas puede ser de dos tipos: **texto** escrito con [*markdown*](https://en.wikipedia.org/wiki/Markdown) o **código** que en este caso vamos a escribir en Python. \n", 96 | "\n", 97 | "### Tecnicalidades\n", 98 | "\n", 99 | "Jupyter funciona con una arquitectura cliente-servidor, lo que ustedes están viendo en esta pantalla es el cliente representado por una aplicación web y el servidor es una aplicación que se está ejecutando en su computadora. Esto significa que podríamos tener el servidor de Jupyter ejecutándose en una computadora más poderosa (por ejemplo, en AWS) y nosotros poder acceder desde cualquier navegador y programar desde ahí. A pesar de esto también es muy común tener cliente y servidor en la misma computadora.\n", 100 | "\n", 101 | "**Otros *kernels***: Es posible configurar nuestro servidor de Jupyter para que acepte otros *kernels* de distintos lenguajes o distintas versiones de Python, entre ellos Julia, R y hasta C#.\n" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "id": "01-jupyter-lab-4", 107 | "metadata": {}, 108 | "source": [ 109 | "## Ventajas \n", 110 | "\n", 111 | " - Son perfectos para mostrar tu trabajo\n", 112 | " - Facilitan la experimentación\n", 113 | " - Tienen una curva de aprendizaje relativamente baja\n", 114 | " - Su arquitectura las hace ideales para uso remoto" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "id": "01-jupyter-lab-5", 120 | "metadata": {}, 121 | "source": [ 122 | "## Desventajas \n", 123 | " \n", 124 | " - Es fácil escribir código espagueti\n", 125 | " - No es tan sencillo colaborar en el mismo *notebook*\n", 126 | " - Sin todas las herramientas de un IDE\n", 127 | " - Dificultad a la hora de depurar" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "id": "01-jupyter-lab-6", 133 | "metadata": {}, 134 | "source": [ 135 | "## Navegando las notebooks\n", 136 | "\n", 137 | "El funcionamiento de las *notebooks* se centra en una entidad conocida como \"celda\", a nosotros nos interesan dos tipos de celdas: de código y de *markdown*, ésta y todas las celdas anteriores son celdas de markdown. \n", 138 | "\n", 139 | " - Doble click (o Enter) en una celda para entrar en modo de edición\n", 140 | " - Esc para salir de modo de edición y entrar en modo comandos\n", 141 | " - Ctrl + Enter ejecuta la celda actual (y permanece en esta)\n", 142 | " - Shift + Enter ejecuta la celda actual y avanza a la siguiente\n", 143 | " - Alt + Enter ejecuta la celda actual y crea una nueva justo debajo de la actual\n", 144 | " - En modo comandos:\n", 145 | " - y para navegar entre celdas\n", 146 | " - M para cambiar una celda a *markdown*\n", 147 | " - Y para cambiar una celda a código\n", 148 | " - D + D para eliminar una celda\n", 149 | " - Z para deshacer cambios a las celdas\n", 150 | " - A agrega una celda arriba de la actual\n", 151 | " - B agrega una celda debajo de la actual\n", 152 | " " 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "id": "01-jupyter-lab-7", 158 | "metadata": {}, 159 | "source": [ 160 | "## \"!\" en las celdas \n", 161 | "\n", 162 | "Puedes poner un `!` como prefijo para indicarle al intérprete que el código a ejecutar después del `i` no es un comando para python, sino algo que debe ejecutar en la terminal del sistema operativo del host:" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "id": "01-jupyter-lab-8", 169 | "metadata": { 170 | "tags": [ 171 | "not-formatted" 172 | ] 173 | }, 174 | "outputs": [], 175 | "source": [ 176 | "!echo \"Hola!\"" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "id": "01-jupyter-lab-9", 183 | "metadata": { 184 | "tags": [ 185 | "not-formatted" 186 | ] 187 | }, 188 | "outputs": [], 189 | "source": [ 190 | "# Podemos combinar comentarios y comandos de consola...\n", 191 | "!echo \"import jupyterlab; print(jupyterlab.__version__)\" > temporary.py" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": null, 197 | "id": "01-jupyter-lab-10", 198 | "metadata": { 199 | "tags": [ 200 | "not-formatted" 201 | ] 202 | }, 203 | "outputs": [], 204 | "source": [ 205 | "# ¡Podemos combinar Python y comandos de consola!\n", 206 | "for i in range(2):\n", 207 | " resultado_consola = !echo \"Hello $i\"\n", 208 | " print(resultado_consola)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "id": "01-jupyter-lab-11", 214 | "metadata": {}, 215 | "source": [ 216 | "## \"?\" en las celdas\n", 217 | "\n", 218 | "Podemos usar el símbolo \"?\" para obtener ayuda sobre el código en Python, podemos poner el símbolo antes o después de la instrucción sobre la que queremos saber más." 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "id": "01-jupyter-lab-12", 225 | "metadata": { 226 | "tags": [ 227 | "not-formatted" 228 | ] 229 | }, 230 | "outputs": [], 231 | "source": [ 232 | "?str" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "id": "01-jupyter-lab-13", 239 | "metadata": { 240 | "tags": [ 241 | "not-formatted" 242 | ] 243 | }, 244 | "outputs": [], 245 | "source": [ 246 | "list?" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "id": "01-jupyter-lab-14", 252 | "metadata": {}, 253 | "source": [ 254 | "## Comandos mágicos (?)\n", 255 | "\n", 256 | "En adición a los signos anteriores, dentro de las celdas de las *notebooks* también podemos usar elementos conocidos como \"comandos mágicos\" que son atajos a funciones útiles." 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "id": "01-jupyter-lab-15", 262 | "metadata": {}, 263 | "source": [ 264 | "### Ejecutar un script" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": null, 270 | "id": "01-jupyter-lab-16", 271 | "metadata": { 272 | "tags": [ 273 | "not-formatted" 274 | ] 275 | }, 276 | "outputs": [], 277 | "source": [ 278 | "# Ejecutando un script de python con %run\n", 279 | "%run temporary.py" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "id": "01-jupyter-lab-17", 285 | "metadata": {}, 286 | "source": [ 287 | "### Cargar un script" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "id": "01-jupyter-lab-18", 294 | "metadata": {}, 295 | "outputs": [], 296 | "source": [ 297 | "# %load temporary.py\n", 298 | "import jupyterlab\n", 299 | "\n", 300 | "print(jupyterlab.__version__)" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "id": "01-jupyter-lab-19", 306 | "metadata": {}, 307 | "source": [ 308 | "### Cronometrando nuestro código \n", 309 | "\n", 310 | "Siempre es bueno tener una idea de cuánto tiempo es que nuestro código se va a tardar en ejecutar, podemos usar dos formas de medir el tiempo con los comandos mágicos: \n", 311 | "\n", 312 | " - `%%time`: para calcular el tiempo que tarda una celda completa en ejecutarse\n", 313 | " - `%timeit`: para calcular el tiempo de ejecución de una línea de código (ejecuta la línea múltiples veces para analizar el resultado:" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "id": "01-jupyter-lab-20", 320 | "metadata": { 321 | "tags": [ 322 | "not-formatted" 323 | ] 324 | }, 325 | "outputs": [], 326 | "source": [ 327 | "%%time\n", 328 | "import time\n", 329 | "for i in range(4):\n", 330 | " time.sleep(i)\n", 331 | "print(\"done\")" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "id": "01-jupyter-lab-21", 338 | "metadata": { 339 | "tags": [ 340 | "not-formatted" 341 | ] 342 | }, 343 | "outputs": [], 344 | "source": [ 345 | "import random \n", 346 | "\n", 347 | "# %timeit random.randint(10, 15)\n", 348 | "%timeit -n 10 random.randint(10, 15)" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "id": "01-jupyter-lab-22", 354 | "metadata": {}, 355 | "source": [ 356 | "### Gráficando en las *notebooks* \n", 357 | "\n", 358 | "Una de las grandes ventajas que tenemos con las *notebooks* es que podemos crear gráficas dentro de ellas, mientras que podemos emplear el módulo `matplotlib`, podemos también cambiar el [backend](https://matplotlib.org/stable/tutorials/introductory/usage.html#what-is-a-backend) que estamos usando para graficar; por default el backend es `inline`, pero gracias a [`ipympl`](https://github.com/matplotlib/ipympl) podemos tener gráficas interactivas si cambiamos el backend a `widget` con el comando mágico `%matplotlib widget`" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": null, 364 | "id": "01-jupyter-lab-23", 365 | "metadata": {}, 366 | "outputs": [], 367 | "source": [ 368 | "import matplotlib.pyplot as plt\n", 369 | "import numpy as np" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": null, 375 | "id": "01-jupyter-lab-24", 376 | "metadata": { 377 | "tags": [ 378 | "not-formatted" 379 | ] 380 | }, 381 | "outputs": [], 382 | "source": [ 383 | "%matplotlib inline\n", 384 | "# %config InlineBackend.figure_format ='retina'\n", 385 | "\n", 386 | "fig = plt.figure()\n", 387 | "plt.plot(np.sin(np.linspace(0, 20, 100)));" 388 | ] 389 | }, 390 | { 391 | "cell_type": "code", 392 | "execution_count": null, 393 | "id": "01-jupyter-lab-25", 394 | "metadata": { 395 | "tags": [ 396 | "not-formatted" 397 | ] 398 | }, 399 | "outputs": [], 400 | "source": [ 401 | "%matplotlib widget\n", 402 | "\n", 403 | "fig = plt.figure()\n", 404 | "plt.plot(np.sin(np.linspace(0, 20, 100)));" 405 | ] 406 | }, 407 | { 408 | "cell_type": "markdown", 409 | "id": "01-jupyter-lab-26", 410 | "metadata": {}, 411 | "source": [ 412 | " > 🚨 Es importante mencionar que tanto \"!\", \"?\" y \"%\" son elementos disponibles en los *Juypyter Notebooks*, de ninguna manera pertenecen a la sintaxis del lenguaje Python." 413 | ] 414 | }, 415 | { 416 | "cell_type": "markdown", 417 | "id": "01-jupyter-lab-27", 418 | "metadata": {}, 419 | "source": [ 420 | "## Bonus: $\\mu\\alpha\\tau\\epsilon\\mu\\alpha\\tau\\iota\\kappa\\alpha\\sigma$ \n", 421 | "\n", 422 | "Además de *markdown* las celdas de las *notebooks* permiten usar símbolos matemáticos, mediante el uso del símbolo `$`, hay dos formas de hacerlo\n", 423 | "\n", 424 | " - *En línea*: Tenemos que colocar la expresión matemática entre `$`; ejemplo `$e = m \\times c^2$` se verá así: $e = m \\times c^2$\n", 425 | " - *En bloque*: Tenemos que colocar la expresión matemática entre `$$`; ejemplo `$$e = m \\times c^2$$` se verá así: $$e = m \\times c^2$$ \n", 426 | " \n", 427 | "En lecciones subsecuentes vamos a ver algunas fórmulas 🧐" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "id": "01-jupyter-lab-28", 433 | "metadata": {}, 434 | "source": [ 435 | "## Playground" 436 | ] 437 | }, 438 | { 439 | "cell_type": "markdown", 440 | "id": "01-jupyter-lab-29", 441 | "metadata": {}, 442 | "source": [ 443 | "-----\n", 444 | "## Referencias\n", 445 | "\n", 446 | "### Sitios web \n", 447 | "\n", 448 | "\n", 449 | " \n", 450 | " \n", 451 | " \n", 456 | " \n", 461 | " \n", 466 | " \n", 471 | " \n", 476 | " \n", 481 | " \n", 486 | " \n", 487 | " \n", 488 | "
\n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | "
\n", 489 | "\n", 490 | " - **28 Jupyter Notebook Tips, Tricks, and Shortcuts** - [https://www.dataquest.io/blog/jupyter-notebook-tips-tricks-shortcuts/](https://www.dataquest.io/blog/jupyter-notebook-tips-tricks-shortcuts/)\n", 491 | " - **Jupyter Notebook Cheat Sheet : A Beginner’s Guide to Jupyter Notebook** - [https://www.edureka.co/blog/cheatsheets/jupyter-notebook-cheat-sheet](https://www.edureka.co/blog/cheatsheets/jupyter-notebook-cheat-sheet)\n", 492 | " " 493 | ] 494 | } 495 | ], 496 | "metadata": { 497 | "kernelspec": { 498 | "display_name": "Python 3", 499 | "language": "python", 500 | "name": "python3" 501 | }, 502 | "language_info": { 503 | "codemirror_mode": { 504 | "name": "ipython", 505 | "version": 3 506 | }, 507 | "file_extension": ".py", 508 | "mimetype": "text/x-python", 509 | "name": "python", 510 | "nbconvert_exporter": "python", 511 | "pygments_lexer": "ipython3", 512 | "version": "3.8.6" 513 | }, 514 | "toc-autonumbering": false, 515 | "toc-showcode": false, 516 | "toc-showmarkdowntxt": false, 517 | "toc-showtags": false 518 | }, 519 | "nbformat": 4, 520 | "nbformat_minor": 5 521 | } 522 | -------------------------------------------------------------------------------- /08-optimizacion-parte-2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "ce735c4d", 6 | "metadata": {}, 7 | "source": [ 8 | "# 08: Optimización - Parte 2\n", 9 | "\n", 10 | "\n", 11 | " \n", 12 | " \n", 13 | " \n", 18 | " \n", 23 | " \n", 28 | " \n", 33 | " \n", 38 | " \n", 43 | " \n", 48 | " \n", 49 | " \n", 50 | "
\n", 14 | " \n", 15 | " \n", 16 | " \n", 17 | " \n", 19 | " \n", 20 | " \n", 21 | " \n", 22 | " \n", 24 | " \n", 25 | " \n", 26 | " \n", 27 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | "
" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "0a5fa56e-6bb9-43f8-9c10-6093f5e607a2", 56 | "metadata": {}, 57 | "source": [ 58 | "## Paquetes \n", 59 | "\n", 60 | " - `numpy` \n", 61 | " - `matplotlib` " 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "id": "d0c1b0ac", 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "import numpy as np\n", 72 | "import matplotlib.pyplot as plt" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "id": "4494b17b", 78 | "metadata": {}, 79 | "source": [ 80 | "## Piensa en las redes neuronales \n", 81 | "\n", 82 | "Seguramente habrás escuchado muchas, muchísimas cosas sobre las redes neuronales; no es por nada, sus logros en diversos ámbitos: visión artificial, procesamiento de lenguaje natural, traducciones, clasificación de imágenes... \n", 83 | "\n", 84 | "\n", 85 | "Como ya lo vimos la clase pasada, el problema fundamental de las redes neuronales es el de encontrar la solución aproximada a una función. Piensa en un modelo simple, en donde dado un conjunto de observaciones $\\vec{x}_1, \\vec{x}_2, \\dots, \\vec{x}_n$ y un conjunto de valores esperados $y_1, y_2, \\dots, y_n$, tratamos de encontrar una función $y^\\prime = f(\\vec{x};\\theta)$ y sus parámetros $\\theta$, de tal modo que:\n", 86 | "\n", 87 | "$$\\theta^* = \\text{arg min}_{\\theta} \\sum_i ||f(\\vec{x}_i;\\theta)- y_i||$$\n", 88 | "\n", 89 | "En donde $||f(\\vec{x}_i;\\theta)- y_i||$ representa la distancia de la salida de $f$ y los valores esperados para $y_i$. \n", 90 | "\n", 91 | "**Este es un problema de optimización**.\n", 92 | "\n", 93 | "Sin embargo, las redes neuronales... en particular las pertenencientes al aprendizaje profundo o (*deep learning*), pueden tener decenas, cientos... billones de parámetros; imagínate el tamaño de $\\theta$... ¿qué forma de optimización emplearias?" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "id": "d7d1aa2d-4922-4a28-a2e9-2a921ee5c826", 99 | "metadata": {}, 100 | "source": [ 101 | "## Retropropagación \n", 102 | "\n", 103 | "Antes de explicarte cómo es que funciona la retropropagación, veamos cómo es que están construidas las redes neuronales, en su forma más básica:\n", 104 | "\n", 105 | "Una serie de capas, cada una de ellas representa una aplicación lineal (una multiplicación matricial 😉) seguida de una función no lineal. La salida de una capa es la entrada de otra; así hasta llegar a un punto en el cual llegar a un resultado $y^\\prime$:\n", 106 | "\n", 107 | "\n", 108 | "\n", 109 | "Cada una de las $W_n$ en la imagen anterior representan una matriz distinta, cada una de ellas representa una **matriz de ponderación** (matriz de pesos o *weight matrix*). En conjunto, todos los valores de estas $W$ forman los parámetros de nuestra red neuronal (nuestro $\\theta$), es decir, los valores que estamos tratando de encontrar. Las funciones $G$ se mantienen \"constantes\". \n", 110 | "\n", 111 | "Esta forma de construir las redes neuronales nos calcular la derivada de la función objetivo con respecto a cada uno de los parámetros; esta derivada nos ayuda a guiar el camino a seguir para encontrar un mínimo de esta función objetivo. Esto funciona porque la derivada con respecto a los parámetros nos \"dice\" qué tanto efecto tiene cada parámetro en el resultado final. \n", 112 | "\n", 113 | "Los recursos al final del post lo explican mucho, mucho mejor.\n", 114 | "\n", 115 | "Esto funciona aún cuando hay múltiples capas en nuestra red neuronal, es un algoritmo muy especial conocido como **retropropagación** o *backpropagation*. " 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "id": "92dc1cb4-c36a-447a-8194-3fbae0a848f2", 121 | "metadata": {}, 122 | "source": [ 123 | "## ¿Por qué usar este método? \n", 124 | "\n", 125 | "A diferencia de los métodos que vimos la sesión pasada, estos...\n", 126 | "\n", 127 | " - Tienden a ser lentos,\n", 128 | " - No nos garantizan encontrar un punto mínimo,\n", 129 | " - Tienen muchos híperparametros que elegir de antemano\n", 130 | " \n", 131 | "Cuando se trata de optimizar problemas, como los presentados por las redes neuronales, el usar uno de los métodos discutidos antetiormente es ingenuo; en su lugar se usa algo conocido como **optimización de primer orden**." 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "id": "24fad971-f16e-454a-86c6-38c4d57f4563", 137 | "metadata": {}, 138 | "source": [ 139 | "## Diferenciabilidad \n", 140 | "\n", 141 | "Ya habíamos hablado sobre lo que significa que una función sea continua. Ahora vamos a hablar de una **función suave**; una función de este tipo tiene derivadas continuas hasta cierto orden. Una función suave es más fácil de optimizar porque esto significa que cambios pequeños en los parámetros provocaran cambios pequeños en el resultado de la función objetivo. \n", 142 | "\n", 143 | "Si nosotros logramos calcular la derivada exacta de la función objetivo la optimización es mucho, mucho más sencilla porque podemos usarla para tomar buenas decisiones sobre hacia dónde \"mover\" los parámetros, acercándolos al mínimo. Piensa en una función $L(\\theta) = \\theta^2$ objetivo en donde el vector objetivo está formado por un solo valor $\\theta \\in \\mathbb{R}$, \n", 144 | "\n", 145 | "$$L(\\theta) = \\theta^2$$ \n", 146 | "\n", 147 | "$$L^{\\prime}(\\theta) = 2\\theta$$ \n", 148 | "\n", 149 | "La cosa se complica cuando tenemos que lidiar con vectores parámetro de más de una entrada, es ahí en donde entramos en el terreno de funciones objetivo multidimensionales; en este caso tenemos algo conocido como vector gradiente ($\\nabla L(\\theta)$), en lugar de un solo valor multiplicado por $\\theta$. Aún así, los mismos principios aplican.\n", 150 | "\n", 151 | "\n", 152 | "## Optimización con derivadas \n", 153 | "\n", 154 | "Si sabemos (o podemos calcular) el gradiente de una función objetivo, también sabremos la \"pendiente\" de la función en un punto determinado... esta pendiente nos dicta la dirección hacia la cual la función crece más \"rápidamente\". Si sabemos la derivada de la función que estamos tratando de optimizar, podemos incrementar en órdenes de magnitud la eficiencia de nuestra optimización.\n", 155 | "\n", 156 | "### Primer orden \n", 157 | "\n", 158 | "Hay métodos de optimización que usan las primeras derivadas como herramienta principal, estos se conocen como **métodos de primer orden**. La semana pasada hablamos de **métodos de orden cero** en donde lo único que necesitamos saber es la función objetivo, y también hay métodos de segundo orden que requieren la segunda derivada de la función y se llaman (creativamente) **métodos de segundo orden**...\n", 159 | "\n", 160 | "La derivada que utilizan estos métodos es la derivda de la función objetivo con respecto los parámetros. Esto encesita que conozcamos los gradientes de la función, en otras palabras, nosotros podemos aplicar solamente esta función es: **continua** y **diferenciable**. \n", 161 | "\n", 162 | "En un problema multidimensional, podemos escribir el vector de derivadas de la siguiente manera:\n", 163 | "\n", 164 | "$$\n", 165 | "\\begin{equation}\n", 166 | "\\nabla L(\\vec{\\theta}) = \\left[ \\frac{\\partial L(\\vec{\\theta})}{\\partial \\theta_1}, \n", 167 | "\\frac{\\partial L(\\vec{\\theta})}{\\partial \\theta_2}, \\dots, \\frac{\\partial L(\\vec{\\theta})}{\\partial \\theta_n},\\right]\n", 168 | "\\end{equation}\n", 169 | "$$\n", 170 | "\n", 171 | "En donde $\\frac{\\partial L(\\vec{\\theta})}{\\partial \\theta_1}$ respresenta el cambio en $L$ en la dirección $\\theta_1$ en el punto $\\vec{\\theta}$.\n", 172 | "\n", 173 | "El vector $\\nabla L(\\vec{\\theta})$ es conocido como el **gradiente**; en cualquier punto, este gradiente apunta en la dirección en la que la función cambia más rápidamenmte y su magnitud nos dice la \"velocidad\" con la que este cambio está sucediendo.\n", 174 | "\n", 175 | "### El gradiente descendiente \n", 176 | "\n", 177 | "El algoritmo más básico de primer orden es conocido como el algoritmo del **gradiente descendiente**, es relativamente simple comenzando de una $\\theta^{(0)}$, el valor de $\\theta^{(i+1)}$ estará determinado por: \n", 178 | "\n", 179 | "$$\\vec{\\theta^{(i+1)}} = \\vec{\\theta^{(i)}} - \\delta \\nabla L(\\vec{\\theta^{(i)}})$$ \n", 180 | "\n", 181 | "Ahí se ve una $\\delta$, que en adelante conoceremos como el **tamaño del paso**, este es... sí un híperparametro; ni aquí nos salvamos de los híperparametros. \n", 182 | "\n", 183 | "De forma platicada, el algoritmo es el siguiente: \n", 184 | "\n", 185 | " - Elegimos un punto de inicio $\\theta^{(0)}$ \n", 186 | " - Repetimos...\n", 187 | " - Calculamos la inclinación en ese punto y para todas las direcciones $v = \\nabla L(\\vec{\\theta^{(i)}})$ \n", 188 | " - Damos un pequeño pasito de tamaño $\\delta$ en la dirección que nos indique $v$ \n", 189 | " - Después de ese paso habremos llegado a nuestro nuevo punto de inicio $\\theta^{(1)}$ \n", 190 | " \n", 191 | "Este método es un compromiso entre una solución rápida y una solución genérica. \n", 192 | "\n", 193 | "#### Implementación " 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "id": "2afe469c-0a99-4b41-a27f-5b9108098b5e", 199 | "metadata": {}, 200 | "source": [ 201 | "\n", 202 | "$$L(\\theta) = \\theta^2$$ \n", 203 | "\n", 204 | "$$L^{\\prime}(\\theta) = 2\\theta$$ " 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "id": "c0201071-98c4-4b2f-9c14-eea83ec0a51f", 211 | "metadata": {}, 212 | "outputs": [], 213 | "source": [ 214 | "def L(theta):\n", 215 | " return theta**2\n", 216 | "\n", 217 | "def dL(theta):\n", 218 | " return 2*theta" 219 | ] 220 | }, 221 | { 222 | "cell_type": "markdown", 223 | "id": "64e902d6-8383-4821-b854-6af10e58cbde", 224 | "metadata": {}, 225 | "source": [ 226 | " - Elegimos un punto de inicio $\\theta^{(0)}$ \n", 227 | " - Repetimos...\n", 228 | " - Calculamos la inclinación en ese punto y para todas las direcciones $v = \\nabla L(\\vec{\\theta^{(i)}})$ \n", 229 | " - Damos un pequeño pasito de tamaño $\\delta$ en la dirección que nos indique $v$ \n", 230 | " - Después de ese paso habremos llegado a nuestro nuevo punto de inicio $\\theta^{(1)}$ " 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "id": "10a63310-3dec-4752-b58a-7efc022590cb", 237 | "metadata": {}, 238 | "outputs": [], 239 | "source": [ 240 | "tolerancia = 1e-4\n", 241 | "\n", 242 | "def gradiente(loss_fn, derivada_loss_fn, theta_inicial, paso):\n", 243 | " theta = theta_inicial\n", 244 | " \n", 245 | " current_loss = loss_fn(theta)\n", 246 | " last_loss = np.inf\n", 247 | " historial = [(theta, current_loss)]\n", 248 | " \n", 249 | " while np.abs(current_loss-last_loss) > tolerancia and len(historial) < 100000:\n", 250 | " last_loss = current_loss\n", 251 | " \n", 252 | " theta = theta - (paso * derivada_loss_fn(theta))\n", 253 | " \n", 254 | " current_loss = loss_fn(theta)\n", 255 | " historial.append((theta, current_loss))\n", 256 | " history = np.array(historial)\n", 257 | " return history[:,0], history[:,1]\n", 258 | " " 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": null, 264 | "id": "6b969cd1-1908-496c-90c8-071971de4538", 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "def plot_gradient_descent(xs, L, dL, x_0, paso, ax):\n", 269 | " fig = plt.figure(figsize=(7, 3), dpi=200)\n", 270 | " ax = fig.gca()\n", 271 | " \n", 272 | " x, y = gradiente(L, dL, x_0, paso)\n", 273 | " \n", 274 | " ax.fill_between(xs, L(xs), label=\"Función objetivo\") \n", 275 | " ax.set_title(f\"Paso {paso} - Mínimo en {len(x)} pasos\")\n", 276 | " ax.set_xlabel(\"$\\\\theta$\")\n", 277 | " ax.set_ylabel(\"Loss\")\n", 278 | " ax.plot(x, y, 'k-x', label='Pasos')\n", 279 | " ax.legend()\n", 280 | "\n", 281 | "xs = np.linspace(-5,5,200)\n", 282 | "\n", 283 | "step_sizes = [0.0001, 0.01, 0.1, 0.3, 0.99]" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "id": "a20e8b30-80a9-4bc5-b757-571a2fd68554", 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "plot_gradient_descent(xs, L, dL, -5, 0.0001, ax)" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "id": "5d95d4c0-45ef-4163-b0b4-a2ec4a9f39dc", 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [ 303 | "plot_gradient_descent(xs, L, dL, -5, 0.001, ax)" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": null, 309 | "id": "2cda3bcb-df56-43af-b1e7-b15d95b108b2", 310 | "metadata": {}, 311 | "outputs": [], 312 | "source": [ 313 | "plot_gradient_descent(xs, L, dL, -5, 0.01, ax)" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "id": "b9052122-aa1b-4b66-983b-e297782dd6cb", 320 | "metadata": {}, 321 | "outputs": [], 322 | "source": [ 323 | "plot_gradient_descent(xs, L, dL, -5, 0.1, ax)" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": null, 329 | "id": "9eefba77-71ed-45bd-b8a0-2b114ad9d180", 330 | "metadata": {}, 331 | "outputs": [], 332 | "source": [ 333 | "plot_gradient_descent(xs, L, dL, -5, 0.99, ax)" 334 | ] 335 | }, 336 | { 337 | "cell_type": "code", 338 | "execution_count": null, 339 | "id": "d3e3591d-c059-4ad7-b6ff-48287b9c4c6c", 340 | "metadata": {}, 341 | "outputs": [], 342 | "source": [ 343 | "plot_gradient_descent(xs, L, dL, -5, 1.000002, ax)" 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "id": "592b72fe-a584-44c7-868c-f232c4023918", 349 | "metadata": {}, 350 | "source": [ 351 | "#### Los \"peros\" del gradiente descendiente\n", 352 | "\n", 353 | " - Debemos poder calcular el gradiente de la función ($L^{\\prime}(\\theta)$) en cualquier punto. \n", 354 | " - La velocidad (y convergencia) de la optimización depende del tamaño de paso elegido. \n", 355 | " - Funciona mejor en funciones \"suaves\" sin variaciones grandes. \n", 356 | " - Aún se puede atorar en mínimos locales o \"[puntos de silla](https://es.wikipedia.org/wiki/Punto_de_silla)\". \n", 357 | " - Debemos seguir evaluando la función a cada paso, si esta función es costosa, la optimización es costosa.\n" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "id": "465a04e7-e9ce-46c8-9aa5-f4bbb1d30788", 363 | "metadata": {}, 364 | "source": [ 365 | "### El gradiente descendiente estocástico \n", 366 | "\n", 367 | "De nuestro último punto, evaluar la función puede ser muy *\"costoso\"*, en particular cuando tenemos datasets grandes (ehem... *machine learning*) o una función con muchos parámetros (ehem... *machine learning*).\n", 368 | "\n", 369 | "Si logramos separar la función en sub partes, podemos configurar el optimizador para optimizar una parcialidad de estas independientemente, agregando estas *\"pequeñas\"* optimizaciones. Este es el principio detrás detrás del algoritmo del **gradiente descendiente estocástico**. \n", 370 | "\n", 371 | "Si la función de pérdida se puede escribir de la siguiente manera: \n", 372 | "\n", 373 | "$$L(\\theta) = \\sum_i L_i(\\theta)$$\n", 374 | "\n", 375 | "Es decir, si la función se puede expresar como una suma $L_1(\\theta), L_2(\\theta), \\dots, L_n(\\theta)$. \n", 376 | "\n", 377 | "Para nuestra suerte, cuando estamos tratando de entrenar un modelo de *machine learning* podemos descomponer la función de pérdida de esta manera; y es que en este tipo de problemas tenemos diversos ejemplos de entrenamiento $\\vec{x}_i$ y su correspondiente $y_i$. En este caso, nuestra función de pérdida está dada por: \n", 378 | "\n", 379 | "\n", 380 | "$$L(\\theta) = \\sum_i \\|f(\\vec{x}_i;\\vec{\\theta}) - y_i\\|$$ \n", 381 | "\n", 382 | "(una suma sobre todos los ejemplos de entrenamiento)\n", 383 | "\n", 384 | "Cuando hablamos de las diferenciales:\n", 385 | "\n", 386 | "$$\\nabla \\sum_i \\|f(\\vec{x}_i;\\vec{\\theta}) - y_i\\| = \\sum_i \\nabla ||f(\\vec{x}_i;\\vec{\\theta}) - y_i||$$ \n", 387 | "\n", 388 | "Propuesto de esta manera, podemos tomar un subconjunto de ejemplos de entrenamiento y sus salidas, computar el gradiente para este subconjunto y dar el paso; conforme \"entrenamos\" más y más, en teoría, nos iremos acercando al mínimo. \n", 389 | "\n", 390 | "A cada uno de estos subconjuntos se les conoce como **minibatch**. Cuando un algoritmo ha visto ya todos los ejemplos de un conjunto de entrenamiento se le conoce como **epoch**." 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "id": "cc7a61e5", 396 | "metadata": {}, 397 | "source": [ 398 | "---------\n", 399 | "## Recursos\n", 400 | "\n", 401 | "\n", 402 | " \n", 403 | " \n", 404 | " \n", 409 | " \n", 414 | " \n", 419 | " \n", 424 | " \n", 429 | " \n", 434 | " \n", 439 | " \n", 440 | " \n", 441 | "
\n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 410 | " \n", 411 | " \n", 412 | " \n", 413 | " \n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | "
\n", 442 | "\n", 443 | "- **Neural Networks and Deep Learning** - [http://neuralnetworksanddeeplearning.com/](http://neuralnetworksanddeeplearning.com/) \n", 444 | " \n", 445 | " - **Calculus on Computational Graphs: Backpropagation** - [http://colah.github.io/posts/2015-08-Backprop/](http://colah.github.io/posts/2015-08-Backprop/)\n", 446 | " \n", 447 | " - **Neural Networks, Manifolds, and Topology** - [http://colah.github.io/posts/2014-03-NN-Manifolds-Topology/](http://colah.github.io/posts/2014-03-NN-Manifolds-Topology/)\n", 448 | " \n", 449 | " - **Ecuación diferencial ordinaria de primer orden** - [https://es.wikipedia.org/wiki/Ecuaci%C3%B3n_diferencial_ordinaria_de_primer_orden](https://es.wikipedia.org/wiki/Ecuaci%C3%B3n_diferencial_ordinaria_de_primer_orden)\n", 450 | " \n", 451 | " - **smooth functions or continuous** - [https://math.stackexchange.com/questions/472148/smooth-functions-or-continuous](https://math.stackexchange.com/questions/472148/smooth-functions-or-continuous)\n", 452 | " \n", 453 | " - **does it make sense to talk about third-order (or higher order) optimization methods?** - [https://math.stackexchange.com/questions/2838083/does-it-make-sense-to-talk-about-third-order-or-higher-order-optimization-meth](https://math.stackexchange.com/questions/2838083/does-it-make-sense-to-talk-about-third-order-or-higher-order-optimization-meth)" 454 | ] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "execution_count": null, 459 | "id": "bdd89794", 460 | "metadata": {}, 461 | "outputs": [], 462 | "source": [] 463 | } 464 | ], 465 | "metadata": { 466 | "kernelspec": { 467 | "display_name": "Python 3", 468 | "language": "python", 469 | "name": "python3" 470 | }, 471 | "language_info": { 472 | "codemirror_mode": { 473 | "name": "ipython", 474 | "version": 3 475 | }, 476 | "file_extension": ".py", 477 | "mimetype": "text/x-python", 478 | "name": "python", 479 | "nbconvert_exporter": "python", 480 | "pygments_lexer": "ipython3", 481 | "version": "3.8.2" 482 | } 483 | }, 484 | "nbformat": 4, 485 | "nbformat_minor": 5 486 | } 487 | -------------------------------------------------------------------------------- /02a-numpy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "02a-numpy-0", 6 | "metadata": {}, 7 | "source": [ 8 | "# 02a: Más sobre *NumPy* \n", 9 | "\n", 10 | "\n", 11 | " \n", 12 | " \n", 13 | " \n", 18 | " \n", 23 | " \n", 28 | " \n", 33 | " \n", 38 | " \n", 43 | " \n", 48 | " \n", 49 | " \n", 50 | "
\n", 14 | " \n", 15 | " \n", 16 | " \n", 17 | " \n", 19 | " \n", 20 | " \n", 21 | " \n", 22 | " \n", 24 | " \n", 25 | " \n", 26 | " \n", 27 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | "
" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "02a-numpy-1", 56 | "metadata": {}, 57 | "source": [ 58 | "## Paquetes \n", 59 | "\n", 60 | " - `matplotlib`\n", 61 | " - `numpy`" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "id": "02a-numpy-2", 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "import matplotlib.pyplot as plt\n", 72 | "import matplotlib.image as mpimg\n", 73 | "import numpy as np" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "id": "02a-numpy-3", 79 | "metadata": {}, 80 | "source": [ 81 | "### *Playground*\n", 82 | "\n", 83 | "\n", 84 | "Photo by Alfred Kenneally on Unsplash" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "id": "02a-numpy-4", 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "eagle = mpimg.imread(\"assets/02/alfred-kenneally-UsgLeLorRuM-unsplash.jpg\")\n", 95 | "plt.imshow(eagle)" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "id": "02a-numpy-5", 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "eagle.shape" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "id": "02a-numpy-6", 111 | "metadata": {}, 112 | "source": [ 113 | "## Flip, rotate" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "id": "02a-numpy-7", 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "ej = np.array([[1, 2], [3, 4]])\n", 124 | "print(ej)\n", 125 | "np.flipud(ej)" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "id": "02a-numpy-8", 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "upside_down = np.flipud(eagle) # eagle[::-1,...].copy()\n", 136 | "plt.imshow(upside_down)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "id": "02a-numpy-9", 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "left_to_right = np.fliplr(eagle) # eagle[:, ::-1].copy()\n", 147 | "plt.imshow(left_to_right)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "id": "02a-numpy-10", 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "rotated = np.rot90(eagle)\n", 158 | "plt.imshow(rotated)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "id": "02a-numpy-11", 164 | "metadata": {}, 165 | "source": [ 166 | "## Uniendo arreglos \n", 167 | "\n", 168 | "\n", 169 | "" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "id": "02a-numpy-12", 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "v1 = np.full((3,), 1)\n", 180 | "v2 = np.full((3,), 2)\n", 181 | "v3 = np.full((3,), 3)\n", 182 | "print(v1, v2, v3)" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "id": "02a-numpy-13", 189 | "metadata": {}, 190 | "outputs": [], 191 | "source": [ 192 | "m1 = np.full((3, 3), 1)\n", 193 | "m2 = np.full((3, 3), 2)\n", 194 | "m3 = np.full((3, 3), 3)\n", 195 | "print(m1)\n", 196 | "print(m2)\n", 197 | "print(m3)" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "id": "02a-numpy-14", 203 | "metadata": {}, 204 | "source": [ 205 | "### Concatenación " 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": null, 211 | "id": "02a-numpy-15", 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "v = np.concatenate((v1, v2))\n", 216 | "print(v.shape, v)" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "id": "02a-numpy-16", 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "v = np.concatenate((v1, v2), axis=0)\n", 227 | "print(v.shape, v)" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": null, 233 | "id": "02a-numpy-17", 234 | "metadata": {}, 235 | "outputs": [], 236 | "source": [ 237 | "v = np.concatenate((v1, v2, v3), axis=0)\n", 238 | "print(v.shape, v)" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "id": "02a-numpy-18", 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "v = np.concatenate((m1, m2), axis=1)\n", 249 | "print(v.shape)\n", 250 | "print(v)" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": null, 256 | "id": "02a-numpy-19", 257 | "metadata": {}, 258 | "outputs": [], 259 | "source": [ 260 | "v = np.concatenate((m1, m2), axis=0)\n", 261 | "print(v.shape)\n", 262 | "print(v)" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "id": "02a-numpy-20", 268 | "metadata": {}, 269 | "source": [ 270 | "### *Stacking*" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": null, 276 | "id": "02a-numpy-21", 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "# np.stack\n", 281 | "v = np.stack((v1, v2, v3))\n", 282 | "print(v.shape)\n", 283 | "print(v)" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "id": "02a-numpy-22", 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "v = np.stack((v1, v2, v3), axis=1)\n", 294 | "print(v.shape)\n", 295 | "print(v)" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": null, 301 | "id": "02a-numpy-23", 302 | "metadata": {}, 303 | "outputs": [], 304 | "source": [ 305 | "v = np.hstack((v1, v2, v3))\n", 306 | "print(v.shape)\n", 307 | "print(v)" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": null, 313 | "id": "02a-numpy-24", 314 | "metadata": {}, 315 | "outputs": [], 316 | "source": [ 317 | "v = np.vstack((v1, v2, v3))\n", 318 | "print(v.shape)\n", 319 | "print(v)" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": null, 325 | "id": "02a-numpy-25", 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [ 329 | "v = np.dstack((v1, v2, v3))\n", 330 | "print(v.shape)\n", 331 | "print(v)" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "id": "02a-numpy-26", 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [ 341 | "v = np.dstack((v1, v2, v3))\n", 342 | "print(v.shape)\n", 343 | "print(v)" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": null, 349 | "id": "02a-numpy-27", 350 | "metadata": {}, 351 | "outputs": [], 352 | "source": [ 353 | "v = np.stack((m1, m2, m3))\n", 354 | "print(v.shape)\n", 355 | "print(v)" 356 | ] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "execution_count": null, 361 | "id": "02a-numpy-28", 362 | "metadata": {}, 363 | "outputs": [], 364 | "source": [ 365 | "v = np.stack((m1, m2, m3), axis=1)\n", 366 | "print(v.shape)\n", 367 | "print(v)" 368 | ] 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "id": "02a-numpy-29", 373 | "metadata": {}, 374 | "source": [ 375 | "### Observaciones sobre unir arreglos \n", 376 | "\n", 377 | " - Concatenamos sobre dimensiones existentes\n", 378 | " - Apilamos sobre nuevas dimensiones\n" 379 | ] 380 | }, 381 | { 382 | "cell_type": "markdown", 383 | "id": "02a-numpy-30", 384 | "metadata": {}, 385 | "source": [ 386 | "## Combinación de transformaciones" 387 | ] 388 | }, 389 | { 390 | "cell_type": "code", 391 | "execution_count": null, 392 | "id": "02a-numpy-31", 393 | "metadata": {}, 394 | "outputs": [], 395 | "source": [ 396 | "primer_canal = eagle[:, :, 0]\n", 397 | "plt.imshow(primer_canal, cmap=\"gray\")" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": null, 403 | "id": "02a-numpy-32", 404 | "metadata": {}, 405 | "outputs": [], 406 | "source": [ 407 | "half_image = primer_canal[: len(primer_canal) // 4 * 3]\n", 408 | "plt.imshow(half_image, cmap=\"gray\")\n", 409 | "print(half_image.shape)" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": null, 415 | "id": "02a-numpy-33", 416 | "metadata": {}, 417 | "outputs": [], 418 | "source": [ 419 | "new_image = np.dstack(\n", 420 | " [half_image, np.zeros_like(half_image), half_image] # Rojo # Verde # Azul\n", 421 | ")\n", 422 | "plt.imshow(new_image)" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": null, 428 | "id": "02a-numpy-34", 429 | "metadata": {}, 430 | "outputs": [], 431 | "source": [ 432 | "upside_down = np.flipud(new_image)\n", 433 | "left_to_right = np.fliplr(upside_down)\n", 434 | "two_eagle = np.concatenate((new_image, left_to_right))\n", 435 | "plt.figure(figsize=(15, 15))\n", 436 | "plt.imshow(two_eagle)" 437 | ] 438 | }, 439 | { 440 | "cell_type": "code", 441 | "execution_count": null, 442 | "id": "02a-numpy-35", 443 | "metadata": {}, 444 | "outputs": [], 445 | "source": [ 446 | "ejemplo = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])\n", 447 | "print(ejemplo)\n", 448 | "np.roll(ejemplo, (1, -2), axis=(0, 1))" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": null, 454 | "id": "02a-numpy-36", 455 | "metadata": {}, 456 | "outputs": [], 457 | "source": [ 458 | "plt.figure(figsize=(10, 10))\n", 459 | "shift = 100\n", 460 | "shifted_left = np.roll(new_image, shift, axis=(1))\n", 461 | "shifted_right = np.roll(left_to_right, -shift, axis=(1))\n", 462 | "two_eagle = np.concatenate((shifted_left, shifted_right))\n", 463 | "plt.imshow(two_eagle)" 464 | ] 465 | }, 466 | { 467 | "cell_type": "code", 468 | "execution_count": null, 469 | "id": "02a-numpy-37", 470 | "metadata": {}, 471 | "outputs": [], 472 | "source": [ 473 | "color_change_1 = np.roll(shifted_left, 2, axis=(2))\n", 474 | "color_change_2 = np.roll(shifted_right, 1, axis=(2))\n", 475 | "color_change_2 = np.where(color_change_2 < 30, 0, color_change_2)\n", 476 | "two_eagle = np.concatenate((color_change_1, color_change_2))\n", 477 | "plt.figure(figsize=(10, 10))\n", 478 | "plt.imshow(two_eagle)" 479 | ] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "id": "02a-numpy-38", 484 | "metadata": {}, 485 | "source": [ 486 | "## `np.where`" 487 | ] 488 | }, 489 | { 490 | "cell_type": "code", 491 | "execution_count": null, 492 | "id": "02a-numpy-39", 493 | "metadata": {}, 494 | "outputs": [], 495 | "source": [ 496 | "array = np.ones((5, 4)) * np.array([1, 2, 3, 4])\n", 497 | "array_neg = np.ones((5, 4)) * np.array([1, 2, 3, 4]) * -9\n", 498 | "print(array)\n", 499 | "print(array_neg)" 500 | ] 501 | }, 502 | { 503 | "cell_type": "code", 504 | "execution_count": null, 505 | "id": "02a-numpy-40", 506 | "metadata": {}, 507 | "outputs": [], 508 | "source": [ 509 | "np.where(array % 2 == 0, 100, -800)" 510 | ] 511 | }, 512 | { 513 | "cell_type": "markdown", 514 | "id": "02a-numpy-41", 515 | "metadata": {}, 516 | "source": [ 517 | "## `np.nonzero` \n" 518 | ] 519 | }, 520 | { 521 | "cell_type": "code", 522 | "execution_count": null, 523 | "id": "02a-numpy-42", 524 | "metadata": {}, 525 | "outputs": [], 526 | "source": [ 527 | "array = np.array([[1, 0, 0], [0, 2, 0], [3, 4, 0]])\n", 528 | "array" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": null, 534 | "id": "02a-numpy-43", 535 | "metadata": {}, 536 | "outputs": [], 537 | "source": [ 538 | "columnas, filas = np.nonzero(array)\n", 539 | "\n", 540 | "for x, y in zip(columnas, filas):\n", 541 | " print(f\"({x}, {y})\", array[x, y])" 542 | ] 543 | }, 544 | { 545 | "cell_type": "markdown", 546 | "id": "02a-numpy-44", 547 | "metadata": {}, 548 | "source": [ 549 | "## Ordenación" 550 | ] 551 | }, 552 | { 553 | "cell_type": "markdown", 554 | "id": "02a-numpy-45", 555 | "metadata": {}, 556 | "source": [ 557 | "### *Sort* \n", 558 | "\n", 559 | "Crea una **copia ordenada** del arreglo que recibe como parámetro. Quicksort es el algoritmo usado por default." 560 | ] 561 | }, 562 | { 563 | "cell_type": "code", 564 | "execution_count": null, 565 | "id": "02a-numpy-46", 566 | "metadata": {}, 567 | "outputs": [], 568 | "source": [ 569 | "arreglo = np.random.randint(0, 30, size=(6, 6))\n", 570 | "print(arreglo)" 571 | ] 572 | }, 573 | { 574 | "cell_type": "code", 575 | "execution_count": null, 576 | "id": "02a-numpy-47", 577 | "metadata": {}, 578 | "outputs": [], 579 | "source": [ 580 | "np.sort(arreglo[0])" 581 | ] 582 | }, 583 | { 584 | "cell_type": "code", 585 | "execution_count": null, 586 | "id": "02a-numpy-48", 587 | "metadata": {}, 588 | "outputs": [], 589 | "source": [ 590 | "copia = np.array(arreglo[0])\n", 591 | "print(copia)\n", 592 | "copia.sort() # In Place\n", 593 | "print(copia)" 594 | ] 595 | }, 596 | { 597 | "cell_type": "code", 598 | "execution_count": null, 599 | "id": "02a-numpy-49", 600 | "metadata": {}, 601 | "outputs": [], 602 | "source": [ 603 | "np.sort(arreglo, axis=1)" 604 | ] 605 | }, 606 | { 607 | "cell_type": "markdown", 608 | "id": "02a-numpy-50", 609 | "metadata": {}, 610 | "source": [ 611 | "### `np.argsort`" 612 | ] 613 | }, 614 | { 615 | "cell_type": "code", 616 | "execution_count": null, 617 | "id": "02a-numpy-51", 618 | "metadata": {}, 619 | "outputs": [], 620 | "source": [ 621 | "arreglo[0]" 622 | ] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": null, 627 | "id": "02a-numpy-52", 628 | "metadata": {}, 629 | "outputs": [], 630 | "source": [ 631 | "indices = np.argsort(arreglo[0])\n", 632 | "print(indices)" 633 | ] 634 | }, 635 | { 636 | "cell_type": "code", 637 | "execution_count": null, 638 | "id": "02a-numpy-53", 639 | "metadata": {}, 640 | "outputs": [], 641 | "source": [ 642 | "[4, 4, 5, 23, 26, 26]" 643 | ] 644 | }, 645 | { 646 | "cell_type": "code", 647 | "execution_count": null, 648 | "id": "02a-numpy-54", 649 | "metadata": {}, 650 | "outputs": [], 651 | "source": [ 652 | "print(arreglo[0][indices])" 653 | ] 654 | }, 655 | { 656 | "cell_type": "markdown", 657 | "id": "02a-numpy-55", 658 | "metadata": {}, 659 | "source": [ 660 | "## `np.argmax` y `np.argmin` " 661 | ] 662 | }, 663 | { 664 | "cell_type": "code", 665 | "execution_count": null, 666 | "id": "02a-numpy-56", 667 | "metadata": {}, 668 | "outputs": [], 669 | "source": [ 670 | "arreglo[0]" 671 | ] 672 | }, 673 | { 674 | "cell_type": "code", 675 | "execution_count": null, 676 | "id": "02a-numpy-57", 677 | "metadata": {}, 678 | "outputs": [], 679 | "source": [ 680 | "maximo = np.argmax(arreglo[0])\n", 681 | "minimo = np.argmin(arreglo[0])\n", 682 | "print(maximo, minimo)\n", 683 | "print(arreglo[0][maximo], arreglo[0][minimo])" 684 | ] 685 | }, 686 | { 687 | "cell_type": "code", 688 | "execution_count": null, 689 | "id": "02a-numpy-58", 690 | "metadata": {}, 691 | "outputs": [], 692 | "source": [ 693 | "arreglo" 694 | ] 695 | }, 696 | { 697 | "cell_type": "code", 698 | "execution_count": null, 699 | "id": "02a-numpy-59", 700 | "metadata": {}, 701 | "outputs": [], 702 | "source": [ 703 | "np.argmin(arreglo, axis=1)" 704 | ] 705 | }, 706 | { 707 | "cell_type": "markdown", 708 | "id": "02a-numpy-60", 709 | "metadata": {}, 710 | "source": [ 711 | "## Vectorización \n", 712 | "Ya hablamos de la vectorización... pero, ¿cómo es que podemos aplicar esto en *\"la realidad*\"?\n", 713 | "\n", 714 | "### \"Regla de oro\" \n", 715 | "\n", 716 | "#### 🚫 `for` 🚫 \n", 717 | "\n", 718 | "El objetivo de la vectorización es, entre otras cosas, evitar el programar ciclos explícitamente.\n", 719 | "\n", 720 | " - Analiza las variables de entrada\n", 721 | " - Genera arreglos auxiliares\n", 722 | " - Aplica las operaciones\n", 723 | " - Recolecta los resultados\n", 724 | "\n", 725 | "### Alternativas al `if` " 726 | ] 727 | }, 728 | { 729 | "cell_type": "code", 730 | "execution_count": null, 731 | "id": "02a-numpy-61", 732 | "metadata": {}, 733 | "outputs": [], 734 | "source": [ 735 | "sample = np.random.uniform(-20, 20, (15,))\n", 736 | "print(sample)" 737 | ] 738 | }, 739 | { 740 | "cell_type": "markdown", 741 | "id": "02a-numpy-62", 742 | "metadata": {}, 743 | "source": [ 744 | "#### `np.where` " 745 | ] 746 | }, 747 | { 748 | "cell_type": "code", 749 | "execution_count": null, 750 | "id": "02a-numpy-63", 751 | "metadata": {}, 752 | "outputs": [], 753 | "source": [ 754 | "copy = sample.copy()\n", 755 | "\n", 756 | "threshold = 2\n", 757 | "\n", 758 | "for i in range(len(copy)):\n", 759 | " if copy[i] < threshold:\n", 760 | " copy[i] *= 10\n", 761 | "print(copy)" 762 | ] 763 | }, 764 | { 765 | "cell_type": "code", 766 | "execution_count": null, 767 | "id": "02a-numpy-64", 768 | "metadata": {}, 769 | "outputs": [], 770 | "source": [ 771 | "copy = sample.copy()\n", 772 | "print(np.where(copy < threshold, copy * 10, copy))" 773 | ] 774 | }, 775 | { 776 | "cell_type": "markdown", 777 | "id": "02a-numpy-65", 778 | "metadata": {}, 779 | "source": [ 780 | "#### `np.maximum`" 781 | ] 782 | }, 783 | { 784 | "cell_type": "code", 785 | "execution_count": null, 786 | "id": "02a-numpy-66", 787 | "metadata": {}, 788 | "outputs": [], 789 | "source": [ 790 | "copy = sample.copy()" 791 | ] 792 | }, 793 | { 794 | "cell_type": "code", 795 | "execution_count": null, 796 | "id": "02a-numpy-67", 797 | "metadata": {}, 798 | "outputs": [], 799 | "source": [ 800 | "print(np.maximum(copy, 0))" 801 | ] 802 | }, 803 | { 804 | "cell_type": "markdown", 805 | "id": "02a-numpy-68", 806 | "metadata": {}, 807 | "source": [ 808 | "#### `np.clip`" 809 | ] 810 | }, 811 | { 812 | "cell_type": "code", 813 | "execution_count": null, 814 | "id": "02a-numpy-69", 815 | "metadata": {}, 816 | "outputs": [], 817 | "source": [ 818 | "print(np.clip(copy, -6, 6))" 819 | ] 820 | }, 821 | { 822 | "cell_type": "markdown", 823 | "id": "02a-numpy-70", 824 | "metadata": {}, 825 | "source": [ 826 | "#### `np.isnan`" 827 | ] 828 | }, 829 | { 830 | "cell_type": "code", 831 | "execution_count": null, 832 | "id": "02a-numpy-71", 833 | "metadata": {}, 834 | "outputs": [], 835 | "source": [ 836 | "aa = np.array([np.nan, 10, 15, np.nan, 16])\n", 837 | "promedio = aa[~np.isnan(aa)].mean()\n", 838 | "np.where(np.isnan(aa), promedio, aa)" 839 | ] 840 | }, 841 | { 842 | "cell_type": "code", 843 | "execution_count": null, 844 | "id": "02a-numpy-72", 845 | "metadata": {}, 846 | "outputs": [], 847 | "source": [ 848 | "print(~np.isnan(aa))\n", 849 | "print(np.isnan(aa))" 850 | ] 851 | }, 852 | { 853 | "cell_type": "markdown", 854 | "id": "02a-numpy-73", 855 | "metadata": {}, 856 | "source": [ 857 | "## Ecuaciones\n", 858 | "\n", 859 | "### Sumatorias (y acumulacionse en general) \n", 860 | "\n", 861 | "Imagina una ecuación así\n", 862 | "\n", 863 | "$$\\sum_{i=0}^{} \\cos(x_i^{2})$$\n" 864 | ] 865 | }, 866 | { 867 | "cell_type": "code", 868 | "execution_count": null, 869 | "id": "02a-numpy-74", 870 | "metadata": {}, 871 | "outputs": [], 872 | "source": [ 873 | "x = np.random.uniform(0, 1, 10)\n", 874 | "\n", 875 | "np.sum(np.cos(x ** 2))" 876 | ] 877 | }, 878 | { 879 | "cell_type": "markdown", 880 | "id": "02a-numpy-75", 881 | "metadata": {}, 882 | "source": [ 883 | "Supongamos que el índice del elemento $i$ aparece dentro del cuerpo de la sumatoria:\n", 884 | "\n", 885 | "\n", 886 | "$$\\sum_{i=0}^{} \\cos(x_i) * i$$\n", 887 | "\n", 888 | "\n", 889 | "La solución es generar un arreglo con el índice $i$ de antemano:" 890 | ] 891 | }, 892 | { 893 | "cell_type": "code", 894 | "execution_count": null, 895 | "id": "02a-numpy-76", 896 | "metadata": {}, 897 | "outputs": [], 898 | "source": [ 899 | "i = np.arange(len(x))\n", 900 | "print(i)\n", 901 | "np.sum(np.cos(x) * i)" 902 | ] 903 | }, 904 | { 905 | "cell_type": "markdown", 906 | "id": "02a-numpy-77", 907 | "metadata": {}, 908 | "source": [ 909 | "----------\n", 910 | "\n", 911 | "## Referencias\n", 912 | "\n", 913 | "\n", 914 | " \n", 915 | " \n", 916 | " \n", 921 | " \n", 926 | " \n", 931 | " \n", 936 | " \n", 941 | " \n", 946 | " \n", 951 | " \n", 952 | " \n", 953 | "
\n", 917 | " \n", 918 | " \n", 919 | " \n", 920 | " \n", 922 | " \n", 923 | " \n", 924 | " \n", 925 | " \n", 927 | " \n", 928 | " \n", 929 | " \n", 930 | " \n", 932 | " \n", 933 | " \n", 934 | " \n", 935 | " \n", 937 | " \n", 938 | " \n", 939 | " \n", 940 | " \n", 942 | " \n", 943 | " \n", 944 | " \n", 945 | " \n", 947 | " \n", 948 | " \n", 949 | " \n", 950 | "
\n", 954 | "\n", 955 | "\n", 956 | "### Libros \n", 957 | "\n", 958 | " - **Elegant SciPy: The Art of Scientific Python**: [México](https://amzn.to/3c0ZNZM) · [España](https://amzn.to/30WWmge) · [US](https://amzn.to/2PbkbhC) \n", 959 | " - **High Performance Python: Practical Performant Programming for Humans**: [México](https://amzn.to/310hEtt) · [España](https://amzn.to/3cVZsXD) · [US](https://amzn.to/3s9eUFW) \n", 960 | "\n", 961 | "\n", 962 | "### Sitios web \n", 963 | "\n", 964 | " - **From Python to NumPy** - [https://www.labri.fr/perso/nrougier/from-python-to-numpy/](https://www.labri.fr/perso/nrougier/from-python-to-numpy/) \n", 965 | " - **100 NumPy excercises** - [https://github.com/rougier/numpy-100](https://github.com/rougier/numpy-100)\n", 966 | " - **NumPy Cheat Sheet — Python for Data Science** - [https://www.dataquest.io/blog/numpy-cheat-sheet/](https://www.dataquest.io/blog/numpy-cheat-sheet/)\n", 967 | " - **Chapter 3 Numerical calculations with NumPy** - [http://kestrel.nmt.edu/~raymond/software/python_notes/paper003.html](http://kestrel.nmt.edu/~raymond/software/python_notes/paper003.html)" 968 | ] 969 | } 970 | ], 971 | "metadata": { 972 | "kernelspec": { 973 | "display_name": "Python 3", 974 | "language": "python", 975 | "name": "python3" 976 | }, 977 | "language_info": { 978 | "codemirror_mode": { 979 | "name": "ipython", 980 | "version": 3 981 | }, 982 | "file_extension": ".py", 983 | "mimetype": "text/x-python", 984 | "name": "python", 985 | "nbconvert_exporter": "python", 986 | "pygments_lexer": "ipython3", 987 | "version": "3.8.6" 988 | } 989 | }, 990 | "nbformat": 4, 991 | "nbformat_minor": 5 992 | } 993 | -------------------------------------------------------------------------------- /03-numpy-tecnicalidades.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "03-numpy-tecnicalidades-0", 6 | "metadata": {}, 7 | "source": [ 8 | "# 03: Tecnicalidades de *NumPy*\n", 9 | "\n", 10 | "\n", 11 | " \n", 12 | " \n", 13 | " \n", 18 | " \n", 23 | " \n", 28 | " \n", 33 | " \n", 38 | " \n", 43 | " \n", 48 | " \n", 49 | " \n", 50 | "
\n", 14 | " \n", 15 | " \n", 16 | " \n", 17 | " \n", 19 | " \n", 20 | " \n", 21 | " \n", 22 | " \n", 24 | " \n", 25 | " \n", 26 | " \n", 27 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | "
" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "03-numpy-tecnicalidades-1", 56 | "metadata": {}, 57 | "source": [ 58 | "## Paquetes \n", 59 | "\n", 60 | " - `numpy`\n" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "id": "03-numpy-tecnicalidades-2", 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "import numpy as np\n", 71 | "\n", 72 | "from df.number_internals import show_float\n", 73 | "from df.array_internals import show_array_details" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "id": "03-numpy-tecnicalidades-3", 79 | "metadata": {}, 80 | "source": [ 81 | "Recordando la semana pasada, los arreglos:\n", 82 | "\n", 83 | " - Tienen un tamaño pre-determinado\n", 84 | " - Tienen que ser *\"rectangulares\"*\n", 85 | " - Todos los elementos tienen el mismo tipo\n", 86 | " - Los elementos son números\n", 87 | "\n", 88 | "A partir de este punto, vamos a conocer sobre:\n", 89 | "\n", 90 | " - **Representación de arreglos en memoria**\n", 91 | " - **Representación de números en memoria** " 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "id": "03-numpy-tecnicalidades-4", 97 | "metadata": {}, 98 | "source": [ 99 | "## Arreglos en memoria \n", 100 | "\n", 101 | "Los arreglos en *NumPy* implementados de forma compacta en memoria; la información numérica está almacenada de forma contigua (un número después del otro) con un pequeño encabezado que describe cómo es que el arreglo debe ser manipulado.\n", 102 | "\n", 103 | "No importa si estamos hablando de un arreglo unidimensional o de un tensor de 6 dimensiones, los arreglos en *NumPy* son planos, un número después del otro con un poco de información extra al inicio:\n", 104 | "\n", 105 | "\n", 106 | "\n", 107 | "### Zancadas o *strides* \n", 108 | "\n", 109 | "El *truco* detrás de la implementación de los arreglos es el uso de algo llamado *zancada (!???!)* o stride en inglés. Estas *strides* especifican cómo es que se debe indexar el arreglo. Hay un *stride* por cada dimensión.\n", 110 | "\n", 111 | "Se llama zancada porque se usa para especificar qué tan grande es el *\"paso\" en bytes* que se debe dar para acceder a cada uno de los elementos; y este es el truco que le permite a *NumPy* mantener el arreglo como una sola secuencia plana de números:\n", 112 | "\n", 113 | "\n", 114 | "\n", 115 | "Es más claro cuando vemos este concepto aplicado a un arreglo de dos dimensiones, para calcular la ubicación en memoria de el elemento en la posición [$i$, $j$] se puede usar la fórmula: \n", 116 | "\n", 117 | "\n", 118 | "\n", 119 | "$$location = i * stride_0 + j * stride_1$$ \n", 120 | "\n", 121 | "Podemos generalizar esta idea para arreglos de 3, 4 y demás dimensiones." 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "id": "03-numpy-tecnicalidades-5", 127 | "metadata": {}, 128 | "source": [ 129 | "### Transformaciones rígidas" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "id": "03-numpy-tecnicalidades-6", 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "base = np.zeros((3, 4), dtype=np.float32)\n", 140 | "show_array_details(base)" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": null, 146 | "id": "03-numpy-tecnicalidades-7", 147 | "metadata": {}, 148 | "outputs": [], 149 | "source": [ 150 | "print(\"Original\")\n", 151 | "show_array_details(base)" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "id": "03-numpy-tecnicalidades-8", 158 | "metadata": { 159 | "tags": [] 160 | }, 161 | "outputs": [], 162 | "source": [ 163 | "print(\"Transpuesta\")\n", 164 | "base_t = base.T\n", 165 | "show_array_details(base_t)" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "id": "03-numpy-tecnicalidades-9", 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "flipud = np.flipud(base)\n", 176 | "print(\"Invertida\")\n", 177 | "show_array_details(flipud)" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": null, 183 | "id": "03-numpy-tecnicalidades-10", 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "print(\"Reflejada\")\n", 188 | "fliplr = np.fliplr(base)\n", 189 | "show_array_details(fliplr)" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "id": "03-numpy-tecnicalidades-11", 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [ 199 | "rotated = np.rot90(base)\n", 200 | "print(\"Girada 90 grados\")\n", 201 | "show_array_details(rotated)" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "id": "03-numpy-tecnicalidades-12", 207 | "metadata": {}, 208 | "source": [ 209 | "## Tipos de datos en *NumPy* \n", 210 | "\n", 211 | "Por default habrás visto más arriba, los tipos de dato de *NumPy* son `float64` si creamos arreglos con números flotantes e `int32` si estamos creando arreglos con números enteros, pero no son los únicos\n", 212 | "\n", 213 | "### Tipos numéricos \n", 214 | "\n", 215 | "#### Enteros \n", 216 | "\n", 217 | "Los enteros representan números sin partes fraccionales; hay de dos tipos: con signo y si signo. La representación de ellos es en forma de una cadena binaria. \n", 218 | "\n", 219 | "Aquí una tabla con los detalles sobre algunos de estos números:\n", 220 | "\n", 221 | "| Nombre | bytes | Mínimo | Máximo |\n", 222 | "|--------|-------|----------------------------|----------------------------|\n", 223 | "| int8 | 1 | -128 | 127 |\n", 224 | "| uint8 | 1 | 0 | 255 |\n", 225 | "| int16 | 2 | -32,768 | 32,767 |\n", 226 | "| uint16 | 2 | 0 | 65,535 |\n", 227 | "| int32 | 4 | -2,147,483,648 | 2,147,483,647 |\n", 228 | "| uint32 | 4 | 0 | 4,294,967,295 |\n", 229 | "| int64 | 8 | -9,223,372,036,854,775,808 | +9,223,372,036,854,775,807 |\n", 230 | "| uint64 | 8 | 0 | 18,446,744,073,709,551,615 |\n", 231 | "\n", 232 | "##### Desbordamiento \n", 233 | "\n", 234 | "Si una operación se sale de los límites del número entero del tipo de dato ocurre un desbordamiento o *overflow*. En muchos sistemas el número simplemente va a reiniciar desde el valor más pequeño: " 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "id": "03-numpy-tecnicalidades-13", 241 | "metadata": {}, 242 | "outputs": [], 243 | "source": [ 244 | "int_array = np.array([100, 110, 120], dtype=np.int8)\n", 245 | "print(int_array)\n", 246 | "print(int_array + 20)" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": null, 252 | "id": "03-numpy-tecnicalidades-14", 253 | "metadata": {}, 254 | "outputs": [], 255 | "source": [ 256 | "uint_array = np.array([100, 110, 120], dtype=np.uint8)\n", 257 | "print(uint_array)\n", 258 | "\n", 259 | "print(uint_array + 20)\n", 260 | "print(uint_array + 150)" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "id": "03-numpy-tecnicalidades-15", 266 | "metadata": {}, 267 | "source": [ 268 | "#### *Floats* " 269 | ] 270 | }, 271 | { 272 | "cell_type": "markdown", 273 | "id": "03-numpy-tecnicalidades-16", 274 | "metadata": {}, 275 | "source": [ 276 | "### IEEE 754 \n", 277 | "\n", 278 | "La forma más común de representar números de punto flotante es el estándar *IEEE 754*, que especifica una forma de representar números y representaciones específicas para representar números \"especiales\".\n", 279 | "\n", 280 | "Este es un resumen de los tipos definidos:\n", 281 | "\n", 282 | "| Name | Nombre común | Base | Mantissa | Bits del exponente | Sesgo exponente | $E_{min}$ | $E_{max}$ |\n", 283 | "|--------------|---------------------|------|----------|--------------------|-----------------|-----------|-----------|\n", 284 | "| binary16 | Precisión media | 2 | 11 | 5 | 2^4−1 = 15 | −14 | +15 |\n", 285 | "| **binary32** | Precisión simple | 2 | 24 | 8 | 2^7−1 = 127 | −126 | +127 |\n", 286 | "| **binary64** | Precisión doble | 2 | 53 | 11 | 2^10−1 = 1023 | −1022 | +1023 |\n", 287 | "| binary128 | Precisión cuádruple | 2 | 113 | 15 | 2^14−1 = 16383 | −16382 | +16383 |\n", 288 | "| binary256 | Precision óctuple | 2 | 237 | 19 | 2^18−1 = 262143 | −262142 | +262143 |\n", 289 | "\n", 290 | "\n", 291 | "\n", 292 | "#### Simples y dobles \n", 293 | "\n", 294 | "Remarcadas en la tabla de arriba están las precisiones simples y dobles, puesto que estas son las más comunes en la actualidad. Como te podrás imaginar, **float32** ocupa 32 bits (4 bytes) por número y **float64** usa 64 bits (8 bytes) por número.\n", 295 | "\n", 296 | "Inclusive las GPUs de la actualidad siguen usando **float32** por default, algunas llegan a **float64**, pero la verdadera rapidez proviene de usar el número de 32 bits.\n", 297 | "\n", 298 | "Sistemas de precisiones fuera de 32 y 64 bits son raras y muy limitadas; en particular, 128 y 256 son usadas cuando se habla de aplicaciones de escala astronómica: \n", 299 | "\n", 300 | " > In addition, the range from the Sun to the pulsar source may exceed ten thousand light years away, so it needs at least quadruple precision (128 bits of floating point representation). Otherwise, the numerical error of computing will result in poor navigation accuracy.In addition, the range from the Sun to the pulsar source may exceed ten thousand light years away, so it needs at least quadruple precision (128 bits of floating point representation). Otherwise, the numerical error of computing will result in poor navigation accuracy. ~ [Autonomous Navigation of Mars Probes by Single X-ray Pulsar Measurement and Optical Data of Viewing Martian Moons](https://www.cambridge.org/core/journals/journal-of-navigation/article/autonomous-navigation-of-mars-probes-by-single-x-ray-pulsar-measurement-and-optical-data-of-viewing-martian-moons/E0C7BEB033776F4211289E7916D7EA52)\n", 301 | "\n", 302 | " > 🚨 *NumPy* no soporta números de 128 a 256" 303 | ] 304 | }, 305 | { 306 | "cell_type": "markdown", 307 | "id": "03-numpy-tecnicalidades-17", 308 | "metadata": {}, 309 | "source": [ 310 | "## Representación binaria \n", 311 | "\n", 312 | "Un *float* se representa binariamente por tres partes, cada una de estas partes es... un entero 🤪. Cada una de estas partes tiene un nombre específico: \n", 313 | "\n", 314 | " - Signo\n", 315 | " - Mantissa\n", 316 | " - Exponente \n", 317 | "\n", 318 | "Para reconstruir un número flotante a partir de estos tres números debemos seguir la siguiente fórmula: \n", 319 | "\n", 320 | "$$signo \\times (1 + mantissa) \\times 2^{exponente}$$\n" 321 | ] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "id": "03-numpy-tecnicalidades-18", 326 | "metadata": {}, 327 | "source": [ 328 | "### Flotantes" 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": null, 334 | "id": "03-numpy-tecnicalidades-19", 335 | "metadata": {}, 336 | "outputs": [], 337 | "source": [ 338 | "show_float(1.0)" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "id": "03-numpy-tecnicalidades-20", 345 | "metadata": {}, 346 | "outputs": [], 347 | "source": [ 348 | "show_float(-1.0)" 349 | ] 350 | }, 351 | { 352 | "cell_type": "code", 353 | "execution_count": null, 354 | "id": "03-numpy-tecnicalidades-21", 355 | "metadata": {}, 356 | "outputs": [], 357 | "source": [ 358 | "show_float(4.0)" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": null, 364 | "id": "03-numpy-tecnicalidades-22", 365 | "metadata": {}, 366 | "outputs": [], 367 | "source": [ 368 | "show_float(1e-8)" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": null, 374 | "id": "03-numpy-tecnicalidades-23", 375 | "metadata": {}, 376 | "outputs": [], 377 | "source": [ 378 | "show_float(1.5e200, type_=np.float32)" 379 | ] 380 | }, 381 | { 382 | "cell_type": "markdown", 383 | "id": "03-numpy-tecnicalidades-24", 384 | "metadata": {}, 385 | "source": [ 386 | "### Enteros \n", 387 | "\n", 388 | "Recordemos que para **float64** el tamaño de la mantissa es de 53 bits, lo cual significa que cualquier entero en los rangos $-2^{53}$ to $2^{53}$ es representable. Números fuera de este rango no son representados exactamente." 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": null, 394 | "id": "03-numpy-tecnicalidades-25", 395 | "metadata": {}, 396 | "outputs": [], 397 | "source": [ 398 | "entero = 2 ** 53 - 1\n", 399 | "show_float(1.0 * entero)\n", 400 | "print(entero)" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": null, 406 | "id": "03-numpy-tecnicalidades-26", 407 | "metadata": {}, 408 | "outputs": [], 409 | "source": [ 410 | "entero = 2 ** 53 + 1\n", 411 | "show_float(1.0 * entero)\n", 412 | "print(entero)" 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": null, 418 | "id": "03-numpy-tecnicalidades-27", 419 | "metadata": {}, 420 | "outputs": [], 421 | "source": [ 422 | "entero = 3 ** 55\n", 423 | "show_float(1.0 * entero)\n", 424 | "print(entero)" 425 | ] 426 | }, 427 | { 428 | "cell_type": "markdown", 429 | "id": "03-numpy-tecnicalidades-28", 430 | "metadata": {}, 431 | "source": [ 432 | "### Errores \n", 433 | "\n", 434 | "Cuando operamos con números flotantes podemos provocar errores **a nivel de hardware**: \n", 435 | "\n", 436 | " - **Operación inválida**: cuando provocamos una operación sin un valor definido: $\\frac{0}{0}$, $\\sqrt{-1}$\n", 437 | " - **División entre cero**: ...\n", 438 | " - **Underflow**: cuando el resultado de una operación es más pequeño que el número más pequeño que se puede representar (usualmente el resultado se redondea a cero)\n", 439 | " - **Overflow**: cuando el resultado de una operación es más grande que el número más grande que se puede representar.\n", 440 | " - **Resultado inexacto**: cuando una operación va a generar un resultado inexacto por redondeo \n", 441 | " \n", 442 | "Las excepciones son generadas en el hardware y son escaladas por el sistema operativo y pueden o no ser interceptadas por alguna capa antes de llegar al usuario. Cada sistema (o framework) puede decidir qué hacer con ellas: informar al usuario, detener la ejecución del programa...\n", 443 | "\n", 444 | "*NumPy* por default intercepta los errores y le informa al usuario a través de una *warning*:" 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": null, 450 | "id": "03-numpy-tecnicalidades-29", 451 | "metadata": {}, 452 | "outputs": [], 453 | "source": [ 454 | "np.array(0.0) / np.array(0.0)" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": null, 460 | "id": "03-numpy-tecnicalidades-30", 461 | "metadata": {}, 462 | "outputs": [], 463 | "source": [ 464 | "np.array(1.0) / np.array(0.0)" 465 | ] 466 | }, 467 | { 468 | "cell_type": "code", 469 | "execution_count": null, 470 | "id": "03-numpy-tecnicalidades-31", 471 | "metadata": {}, 472 | "outputs": [], 473 | "source": [ 474 | "np.array(0.1) * np.array(1e-307)" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": null, 480 | "id": "03-numpy-tecnicalidades-32", 481 | "metadata": {}, 482 | "outputs": [], 483 | "source": [ 484 | "np.array(2.0) / np.array(3.0)" 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "id": "03-numpy-tecnicalidades-33", 490 | "metadata": {}, 491 | "source": [ 492 | "## Números especiales " 493 | ] 494 | }, 495 | { 496 | "cell_type": "markdown", 497 | "id": "03-numpy-tecnicalidades-34", 498 | "metadata": {}, 499 | "source": [ 500 | "### ¿Cero negativo? \n", 501 | "\n", 502 | "Gracias a la forma en que se pueden representar los números bajo el estándar IEEE 754, podemos tener dos ceros." 503 | ] 504 | }, 505 | { 506 | "cell_type": "code", 507 | "execution_count": null, 508 | "id": "03-numpy-tecnicalidades-35", 509 | "metadata": {}, 510 | "outputs": [], 511 | "source": [ 512 | "show_float(-0.0)" 513 | ] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "execution_count": null, 518 | "id": "03-numpy-tecnicalidades-36", 519 | "metadata": {}, 520 | "outputs": [], 521 | "source": [ 522 | "show_float(0.0)" 523 | ] 524 | }, 525 | { 526 | "cell_type": "markdown", 527 | "id": "03-numpy-tecnicalidades-37", 528 | "metadata": {}, 529 | "source": [ 530 | "### Dos infinitos \n", 531 | "\n", 532 | "También existe una definición explícita para infinito" 533 | ] 534 | }, 535 | { 536 | "cell_type": "code", 537 | "execution_count": null, 538 | "id": "03-numpy-tecnicalidades-38", 539 | "metadata": {}, 540 | "outputs": [], 541 | "source": [ 542 | "show_float(np.inf)" 543 | ] 544 | }, 545 | { 546 | "cell_type": "code", 547 | "execution_count": null, 548 | "id": "03-numpy-tecnicalidades-39", 549 | "metadata": {}, 550 | "outputs": [], 551 | "source": [ 552 | "show_float(-np.inf)" 553 | ] 554 | }, 555 | { 556 | "cell_type": "code", 557 | "execution_count": null, 558 | "id": "03-numpy-tecnicalidades-40", 559 | "metadata": {}, 560 | "outputs": [], 561 | "source": [ 562 | "0 * np.inf" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "id": "03-numpy-tecnicalidades-41", 568 | "metadata": {}, 569 | "source": [ 570 | "### No es un número - NaN (*Not A Number*) \n", 571 | "\n", 572 | "Otro número muy especial, usado para representar valores inválidos: \n", 573 | "\n", 574 | " - $\\frac{0}{0}$\n", 575 | " - $\\frac{\\infty}{\\infty}$\n", 576 | " - $\\infty - \\infty$ o $-\\infty + \\infty$\n", 577 | " - $0 \\times \\infty$\n", 578 | " - $\\sqrt(x)$ para toda $x < 0$" 579 | ] 580 | }, 581 | { 582 | "cell_type": "code", 583 | "execution_count": null, 584 | "id": "03-numpy-tecnicalidades-42", 585 | "metadata": {}, 586 | "outputs": [], 587 | "source": [ 588 | "show_float(-np.nan)" 589 | ] 590 | }, 591 | { 592 | "cell_type": "markdown", 593 | "id": "03-numpy-tecnicalidades-43", 594 | "metadata": {}, 595 | "source": [ 596 | " > 🚨 NaN se propaga, una vez que en un cálculo se genera un NaN, cualquier operación en la que este se vea involucrado resultará en NaN" 597 | ] 598 | }, 599 | { 600 | "cell_type": "code", 601 | "execution_count": null, 602 | "id": "03-numpy-tecnicalidades-44", 603 | "metadata": {}, 604 | "outputs": [], 605 | "source": [ 606 | "np.nan * np.inf" 607 | ] 608 | }, 609 | { 610 | "cell_type": "markdown", 611 | "id": "03-numpy-tecnicalidades-45", 612 | "metadata": {}, 613 | "source": [ 614 | "## Redondeo y precisión \n", 615 | "\n", 616 | "Debido a las limitaciones en tamaño de bits para representar ciertos números los flotantes son propensos a presentar errores de redondeo:" 617 | ] 618 | }, 619 | { 620 | "cell_type": "code", 621 | "execution_count": null, 622 | "id": "03-numpy-tecnicalidades-46", 623 | "metadata": {}, 624 | "outputs": [], 625 | "source": [ 626 | "show_float(1.0 + 1e-2)" 627 | ] 628 | }, 629 | { 630 | "cell_type": "code", 631 | "execution_count": null, 632 | "id": "03-numpy-tecnicalidades-47", 633 | "metadata": {}, 634 | "outputs": [], 635 | "source": [ 636 | "show_float(1.0 + 1e-4)" 637 | ] 638 | }, 639 | { 640 | "cell_type": "code", 641 | "execution_count": null, 642 | "id": "03-numpy-tecnicalidades-48", 643 | "metadata": {}, 644 | "outputs": [], 645 | "source": [ 646 | "show_float(1.0 + 1e-8)" 647 | ] 648 | }, 649 | { 650 | "cell_type": "code", 651 | "execution_count": null, 652 | "id": "03-numpy-tecnicalidades-49", 653 | "metadata": {}, 654 | "outputs": [], 655 | "source": [ 656 | "show_float(1.0 + 1e-16, type_=np.float128)" 657 | ] 658 | }, 659 | { 660 | "cell_type": "markdown", 661 | "id": "03-numpy-tecnicalidades-50", 662 | "metadata": {}, 663 | "source": [ 664 | " > 🚨 **NUNCA** uses números flotantes para cálculos financieros; un error gracias al redondeo (por más pequeño que sea) repetido muchas veces puede resultar en un error gigantesco. Python tiene un módulo llamado `decimal` que es lento, pero seguro." 665 | ] 666 | }, 667 | { 668 | "cell_type": "markdown", 669 | "id": "03-numpy-tecnicalidades-51", 670 | "metadata": {}, 671 | "source": [ 672 | "### Algunas reglas...\n", 673 | "\n", 674 | "El redondeo en números flotantes es un problema que para la mayoría de aplicaciones no es tan grande; aún así existen algunas cosas que podemos hacer para mitigarlo a la hora de escribir código: \n", 675 | " - **Error de magnitud**: evita sumas $x + y$ donde $x$ e $y$ sean de magnitudes diferentes.\n", 676 | " - **Error de magnificación**: evita divisiones $\\frac{x}{y}$ en donde $y$ sea muy pequeña.\n", 677 | " - **Error de cancelación**: evita restas $x - y$ en donde $x \\simeq y$" 678 | ] 679 | }, 680 | { 681 | "cell_type": "code", 682 | "execution_count": null, 683 | "id": "03-numpy-tecnicalidades-52", 684 | "metadata": {}, 685 | "outputs": [], 686 | "source": [ 687 | "(1e30 + 1.0) - 1e30" 688 | ] 689 | }, 690 | { 691 | "cell_type": "code", 692 | "execution_count": null, 693 | "id": "03-numpy-tecnicalidades-53", 694 | "metadata": {}, 695 | "outputs": [], 696 | "source": [ 697 | "(1e30 - 1e30) + 1.0" 698 | ] 699 | }, 700 | { 701 | "cell_type": "markdown", 702 | "id": "03-numpy-tecnicalidades-54", 703 | "metadata": {}, 704 | "source": [ 705 | "## Comparaciones numéricas \n", 706 | "\n", 707 | "Gracias a estos errores de redondeo, comparaciones directas entre números flotantes pueden resultar *\"incorrectas\"*, y por tanto no es apropiado usarlas:" 708 | ] 709 | }, 710 | { 711 | "cell_type": "code", 712 | "execution_count": null, 713 | "id": "03-numpy-tecnicalidades-55", 714 | "metadata": {}, 715 | "outputs": [], 716 | "source": [ 717 | "x = 0.1\n", 718 | "y = 50000.0\n", 719 | "z = 0.1\n", 720 | "x = (x + y) - y\n", 721 | "\n", 722 | "print(x == z)\n", 723 | "print(x - z)" 724 | ] 725 | }, 726 | { 727 | "cell_type": "markdown", 728 | "id": "03-numpy-tecnicalidades-56", 729 | "metadata": {}, 730 | "source": [ 731 | "La solución es usar la diferencia absoluta comparada contra un determinado valor muy pequeño:\n", 732 | "\n", 733 | "$$|x-z|<\\epsilon$$" 734 | ] 735 | }, 736 | { 737 | "cell_type": "code", 738 | "execution_count": null, 739 | "id": "03-numpy-tecnicalidades-57", 740 | "metadata": {}, 741 | "outputs": [], 742 | "source": [ 743 | "x = 0.1\n", 744 | "y = 50000.0\n", 745 | "z = 0.1\n", 746 | "\n", 747 | "epsilon = 1e-8\n", 748 | "\n", 749 | "x = (x + y) - y\n", 750 | "\n", 751 | "print(abs(x - z) < epsilon)" 752 | ] 753 | }, 754 | { 755 | "cell_type": "markdown", 756 | "id": "03-numpy-tecnicalidades-58", 757 | "metadata": {}, 758 | "source": [ 759 | "### All close \n", 760 | "\n", 761 | "*NumPy* al rescate con la función: `np.allclose`:" 762 | ] 763 | }, 764 | { 765 | "cell_type": "code", 766 | "execution_count": null, 767 | "id": "03-numpy-tecnicalidades-59", 768 | "metadata": {}, 769 | "outputs": [], 770 | "source": [ 771 | "x = 0.1\n", 772 | "y = 50000.0\n", 773 | "z = 0.1\n", 774 | "\n", 775 | "x = (x + y) - y\n", 776 | "\n", 777 | "print(x, z)\n", 778 | "np.allclose(x, z)" 779 | ] 780 | }, 781 | { 782 | "cell_type": "code", 783 | "execution_count": null, 784 | "id": "03-numpy-tecnicalidades-60", 785 | "metadata": {}, 786 | "outputs": [], 787 | "source": [ 788 | "x = np.full((3, 3), 0.1)\n", 789 | "y = np.full((3, 3), 500000000.0)\n", 790 | "z = np.full((3, 3), 0.1)\n", 791 | "\n", 792 | "x = (x + y) - y\n", 793 | "\n", 794 | "\n", 795 | "print(x)\n", 796 | "print(z)\n", 797 | "np.allclose(x, z)" 798 | ] 799 | }, 800 | { 801 | "cell_type": "markdown", 802 | "id": "03-numpy-tecnicalidades-61", 803 | "metadata": {}, 804 | "source": [ 805 | "----------\n", 806 | "\n", 807 | "## Referencias\n", 808 | "\n", 809 | "\n", 810 | " \n", 811 | " \n", 812 | " \n", 817 | " \n", 822 | " \n", 827 | " \n", 832 | " \n", 837 | " \n", 842 | " \n", 847 | " \n", 848 | " \n", 849 | "
\n", 813 | " \n", 814 | " \n", 815 | " \n", 816 | " \n", 818 | " \n", 819 | " \n", 820 | " \n", 821 | " \n", 823 | " \n", 824 | " \n", 825 | " \n", 826 | " \n", 828 | " \n", 829 | " \n", 830 | " \n", 831 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 843 | " \n", 844 | " \n", 845 | " \n", 846 | "
\n", 850 | "\n", 851 | "\n", 852 | "### Libros \n", 853 | "\n", 854 | " - **Elegant SciPy: The Art of Scientific Python**: [México](https://amzn.to/3c0ZNZM) · [España](https://amzn.to/30WWmge) · [US](https://amzn.to/2PbkbhC) \n", 855 | " - **High Performance Python: Practical Performant Programming for Humans**: [México](https://amzn.to/310hEtt) · [España](https://amzn.to/3cVZsXD) · [US](https://amzn.to/3s9eUFW) \n", 856 | "\n", 857 | "\n", 858 | "### Sitios web \n", 859 | "\n", 860 | " - **What makes Numpy Arrays Fast: Memory and Strides** - [https://www.jessicayung.com/numpy-arrays-memory-and-strides](https://www.jessicayung.com/numpy-arrays-memory-and-strides) \n", 861 | " - **What Every Computer Scientist Should Know About Floating-Point Arithmetic** - [https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)\n", 862 | " - **IEEE 754 en Wikipedia** - [https://es.wikipedia.org/wiki/IEEE_754](https://es.wikipedia.org/wiki/IEEE_754)\n", 863 | " - **PEP 754** - [https://www.python.org/dev/peps/pep-0754/](https://www.python.org/dev/peps/pep-0754/)\n", 864 | " - **Machine epsilon en Wikipedia** - [https://en.wikipedia.org/wiki/Machine_epsilon](https://en.wikipedia.org/wiki/Machine_epsilon)" 865 | ] 866 | } 867 | ], 868 | "metadata": { 869 | "kernelspec": { 870 | "display_name": "Python 3", 871 | "language": "python", 872 | "name": "python3" 873 | }, 874 | "language_info": { 875 | "codemirror_mode": { 876 | "name": "ipython", 877 | "version": 3 878 | }, 879 | "file_extension": ".py", 880 | "mimetype": "text/x-python", 881 | "name": "python", 882 | "nbconvert_exporter": "python", 883 | "pygments_lexer": "ipython3", 884 | "version": "3.8.6" 885 | } 886 | }, 887 | "nbformat": 4, 888 | "nbformat_minor": 5 889 | } 890 | -------------------------------------------------------------------------------- /05-algebra-lineal.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "05-algebra-lineal-0", 6 | "metadata": {}, 7 | "source": [ 8 | "# 05: Álgebra Lineal Computacional (Vectores)\n", 9 | "\n", 10 | "\n", 11 | " \n", 12 | " \n", 13 | " \n", 18 | " \n", 23 | " \n", 28 | " \n", 33 | " \n", 38 | " \n", 43 | " \n", 48 | " \n", 49 | " \n", 50 | "
\n", 14 | " \n", 15 | " \n", 16 | " \n", 17 | " \n", 19 | " \n", 20 | " \n", 21 | " \n", 22 | " \n", 24 | " \n", 25 | " \n", 26 | " \n", 27 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | "
" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "05-algebra-lineal-1", 56 | "metadata": {}, 57 | "source": [ 58 | "## Paquetes \n", 59 | "\n", 60 | " - `numpy`\n", 61 | " - `matplotlib`" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "id": "05-algebra-lineal-2", 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "import numpy as np\n", 72 | "import matplotlib.pyplot as plt\n", 73 | "from df.display_algebra import show_vector, show_normed_vects, draw_covariance_ellipse" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "id": "05-algebra-lineal-3", 79 | "metadata": {}, 80 | "source": [ 81 | "## Espacios vectoriales \n", 82 | "\n", 83 | "Supongamos que tenemos un conjunto de vectores $[x_1, x_2, \\dots x_d], x_i \\in \\mathbb{R}$ (vamos a hablar sobre números reales, pero podemos generalizar a cualquier otro conjunto de números) de una dimensión $d$, nosotros podríamos decir que este conjunto de vectores existe dentro de un espacio vectorial si se cumplen ciertas condiciones... \n", 84 | "\n", 85 | "Digamos que tenemos los vectores: \n", 86 | "\n", 87 | "$$\\vec{x} = [x_1, x_2, \\dots, x_d]$$ \n", 88 | "$$\\vec{y} = [y_1, y_2, \\dots, y_d]$$\n", 89 | "\n", 90 | "### Operaciones en espacios vectoriales \n", 91 | "\n", 92 | "Entre las condiciones que deben cumplirse están que debemos tener bien definidas dos operaciones: \n", 93 | "\n", 94 | " - **multiplicación por un escalar**: de tal modo que $\\alpha \\vec{x}$ está definido para cualquier número escalar $\\alpha$. En nuestro caso de los reales, $\\alpha \\vec{x} = [\\alpha x_1, \\alpha x_2, \\dots, \\alpha x_d]$ \n", 95 | " \n", 96 | " - **suma de vectores**: de tal modo que $\\vec{x} + \\vec{y}$, En nuestro caso de los reales, $\\vec{x} + \\vec{y} = [x_1 + y_1, x_2 + y_2, \\dots x_d + y_d]$\n", 97 | " \n", 98 | "Existen 10 axiomas basadas en estas dos operaciones que deben cumplirse.\n", 99 | "\n", 100 | "\n", 101 | "Existe un subconjunto de espacios vectoriales, llamado **espacio vectorial topológico** bajo los cuales debemos definir otro par de operaciones: \n", 102 | "\n", 103 | " - **norma**: $||\\vec{x}||$ que define la longitud de un vector, útil para medir y comparar vectores \n", 104 | "\n", 105 | " - **producto interno**: $\\langle \\vec{x} | \\vec{y} \\rangle$ or $\\vec{x} \\bullet \\vec{y}$ permite comparar los ángulos de dos vectores. El producto interno de dos vectores ortogonales es 0. Para vectores de números reales $\\vec{x} \\bullet \\vec{y} = x_1 y_1 + x_2 y_2 + x_3 y_3 \\dots x_d y_d$.\n", 106 | " \n", 107 | "Las operaciones que definimos son importantes para la ciencia de datos porque nos permite hablar de que un par de vectores están cerca el uno del otro, nos permite sumarlos, compararlos y escalarlos.\n", 108 | "\n", 109 | "\n", 110 | "\n", 111 | "### Recordando *NumPy*..." 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "id": "05-algebra-lineal-4", 118 | "metadata": {}, 119 | "outputs": [], 120 | "source": [ 121 | "x = np.array([0, 1, 1])\n", 122 | "y = np.array([4, 5, 6])\n", 123 | "a = 2\n", 124 | "\n", 125 | "\n", 126 | "def pp(text, value):\n", 127 | " print(text.rjust(10), \"=\", str(value))\n", 128 | "\n", 129 | "\n", 130 | "pp(\"a\", a)\n", 131 | "pp(\"x\", x)\n", 132 | "pp(\"y\", y)\n", 133 | "pp(\"a * x\", a * x)\n", 134 | "pp(\"x + y\", x + y)\n", 135 | "pp(\"||x||\", np.linalg.norm(x))\n", 136 | "pp(\"x · y\", np.dot(x, y))" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "id": "05-algebra-lineal-5", 142 | "metadata": {}, 143 | "source": [ 144 | "- **`np.linalg.norm`** \n", 145 | "- **`np.dot`**" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "id": "05-algebra-lineal-6", 151 | "metadata": {}, 152 | "source": [ 153 | "### Información como vectores \n", 154 | "\n", 155 | "Las representaciones vectoriales son de vital importancia en el aprendizaje automático, puesto que la información es más fácilmente procesada por un algoritmo cuando es representada en forma de vectores.\n", 156 | "\n", 157 | "Pero antes de hablar de vectores, es importante entender que muchas veces la información no está en forma vectorial por naturaleza, en muchos casos el proceso es el siguiente: \n", 158 | "\n", 159 | "" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "id": "05-algebra-lineal-7", 165 | "metadata": {}, 166 | "source": [ 167 | "### Un clásico de machine learning... \n", 168 | "\n", 169 | "\n", 170 | "\n", 171 | "Imagen original en el curso de Data Fundamentals https://www.gla.ac.uk/undergraduate/degrees/computingscience/?card=course&code=COMPSCI4073" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "id": "05-algebra-lineal-8", 177 | "metadata": {}, 178 | "source": [ 179 | "## Operaciones vectoriales" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "id": "05-algebra-lineal-9", 185 | "metadata": {}, 186 | "source": [ 187 | "### Sumas y multiplicaciones \n", 188 | "\n", 189 | "Ya hablamos de las sumas y multiplicaciones *elemento-elemento*; una consecuencia de esto es que podemos introducir el concepto de sumas ponderadas: \n", 190 | "\n", 191 | "$$\\lambda_1 x_1 + \\lambda_2 x_2 + \\dots + \\lambda_n x_n$$" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": null, 197 | "id": "05-algebra-lineal-10", 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [ 201 | "a = np.array([1.0, 1.0])\n", 202 | "b = np.array([0.0, 0.5])\n", 203 | "c = np.array([0.5, 0.0])\n", 204 | "\n", 205 | "origin = np.array([0, 0])\n", 206 | "\n", 207 | "\n", 208 | "fig = plt.figure(figsize=(15, 5), dpi=200)\n", 209 | "ax = fig.gca()\n", 210 | "\n", 211 | "\n", 212 | "show_vector(ax, origin, a, \"$\\\\vec{a}$\")\n", 213 | "show_vector(ax, origin, b, \"$\\\\vec{b}$\")\n", 214 | "show_vector(ax, origin, c, \"$\\\\vec{c}$\")\n", 215 | "\n", 216 | "show_vector(ax, a, a + b, \"$\\\\vec{a} + \\\\vec{b}$\")\n", 217 | "show_vector(ax, a + b, a + b + c, \"$\\\\vec{a} + \\\\vec{b} + \\\\vec{c}$\")\n", 218 | "show_vector(ax, a + b + c, a + b + c - b, \"$\\\\vec{a} + \\\\vec{b} + \\\\vec{c} - \\\\vec{b}$\")\n", 219 | "\n", 220 | "\n", 221 | "ax.set_xlim(-1.5, 2.0)\n", 222 | "ax.set_ylim(-0.5, 2.0)\n", 223 | "ax.set_aspect(1.0)\n", 224 | "ax.legend(loc=\"center left\")" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "id": "05-algebra-lineal-11", 230 | "metadata": {}, 231 | "source": [ 232 | "### Tamaño de un vector\n", 233 | "\n", 234 | "Una de las formas para calcular la \"magnitud\" o la \"longitud\" de un vector es la siguiente fórmula: \n", 235 | "\n", 236 | "$$ \\|\\vec{x}\\|_2 = \\sqrt{x_0^2 + x_1^2 + x_2^2 + \\dots + x_n^2 } $$\n", 237 | "\n", 238 | "Esta fórmula es conocida como la *norma* de un vector; *NumPy* nos la ofrece a través de la función `np.linalg.norm`:" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "id": "05-algebra-lineal-12", 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "x = np.array([1.0, 10.0, -5.0])\n", 249 | "y = np.array([1.0, -4.0, 8.0])\n", 250 | "print(np.linalg.norm(x))\n", 251 | "print(np.linalg.norm(y))" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "id": "05-algebra-lineal-13", 257 | "metadata": {}, 258 | "source": [ 259 | "Esta norma es conocida como la **norma euclidiana**, y que también nos ayuda a calcular la distancia entre dos vectores $\\vec{a}$ y $\\vec{b}$: \n", 260 | "\n", 261 | "$$||\\vec{x}-\\vec{y}||_2$$" 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "id": "05-algebra-lineal-14", 267 | "metadata": {}, 268 | "source": [ 269 | "#### Diferentes tamaños \n", 270 | "\n", 271 | "Como ya lo mencioné, existen diversas maneras de determinar la magnitud de un vector:\n", 272 | "\n", 273 | "| Notación | Nombre | Efecto |\n", 274 | "|----------------------|------------------|-------------------------------|\n", 275 | "| $\\|x\\|$ or $\\|x\\|_2$ | Norma Euclidiana | Distancia \"ordinaria\" |\n", 276 | "| $\\|x\\|_1$ | Norma Manhattan | Suma de los valores absolutos |\n", 277 | "| $\\|x\\|_0$ | Pseudo-Norma 0 | Número de valores $\\neq 0$ |\n", 278 | "| $\\|x\\|_\\inf$ | Norma máxima | Elemento mayor |\n", 279 | "| $\\|x\\|_{-\\inf}$ | Norma mínima | Elemento menor |\n" 280 | ] 281 | }, 282 | { 283 | "cell_type": "code", 284 | "execution_count": null, 285 | "id": "05-algebra-lineal-15", 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [ 289 | "test_vector = [2, 0, 5, -5, -1, 0]" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": null, 295 | "id": "05-algebra-lineal-16", 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "np.linalg.norm(test_vector, 0) # Pseudo-norma cero" 300 | ] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": null, 305 | "id": "05-algebra-lineal-17", 306 | "metadata": {}, 307 | "outputs": [], 308 | "source": [ 309 | "np.linalg.norm(test_vector, 1) # Manhattan" 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": null, 315 | "id": "05-algebra-lineal-18", 316 | "metadata": {}, 317 | "outputs": [], 318 | "source": [ 319 | "np.linalg.norm(test_vector, np.inf) # Máximo absoluto" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": null, 325 | "id": "05-algebra-lineal-19", 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [ 329 | "np.linalg.norm(test_vector, -np.inf) # Mínimo absoluto" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": null, 335 | "id": "05-algebra-lineal-20", 336 | "metadata": {}, 337 | "outputs": [], 338 | "source": [ 339 | "np.linalg.norm(test_vector, 2)" 340 | ] 341 | }, 342 | { 343 | "cell_type": "code", 344 | "execution_count": null, 345 | "id": "05-algebra-lineal-21", 346 | "metadata": {}, 347 | "outputs": [], 348 | "source": [ 349 | "np.linalg.norm(test_vector)" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "id": "05-algebra-lineal-22", 355 | "metadata": {}, 356 | "source": [ 357 | "#### Vectores unitarios \n", 358 | "\n", 359 | "Un vector cuya norma sea $1$ es conocido como vector unitario (esto implica que diferente normas nos llevan a diferentes vectores unitarios). \n", 360 | "\n", 361 | "Cuando nosotros tomamos un vector y lo dividimos entre su norma podemos decir que el vector ha sido normalizado, por ejemplo con la norma euclidiana: \n", 362 | "\n", 363 | "$$\\vec{x}_n = \\vec{x} \\frac{1}{\\|\\vec{x}\\|_2}$$" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": null, 369 | "id": "05-algebra-lineal-23", 370 | "metadata": {}, 371 | "outputs": [], 372 | "source": [ 373 | "def print_v(x):\n", 374 | " return print(np.around(x, 2))" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": null, 380 | "id": "05-algebra-lineal-24", 381 | "metadata": {}, 382 | "outputs": [], 383 | "source": [ 384 | "x = np.random.uniform(1, 5, 4)\n", 385 | "norm = np.linalg.norm(x)\n", 386 | "x_norm = x / norm\n", 387 | "\n", 388 | "print_v(x)\n", 389 | "print(norm)\n", 390 | "print_v(x_norm)" 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "id": "05-algebra-lineal-25", 396 | "metadata": {}, 397 | "source": [ 398 | "#### Representación gráfica" 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": null, 404 | "id": "05-algebra-lineal-26", 405 | "metadata": {}, 406 | "outputs": [], 407 | "source": [ 408 | "x = np.random.normal(0, 1, (50, 2))" 409 | ] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": null, 414 | "id": "05-algebra-lineal-27", 415 | "metadata": {}, 416 | "outputs": [], 417 | "source": [ 418 | "fig = plt.figure(figsize=(15, 5), dpi=200)\n", 419 | "ax = fig.gca()\n", 420 | "\n", 421 | "show_normed_vects(ax, x, 2)" 422 | ] 423 | }, 424 | { 425 | "cell_type": "markdown", 426 | "id": "05-algebra-lineal-28", 427 | "metadata": {}, 428 | "source": [ 429 | "### Producto interno \n", 430 | "\n", 431 | "Existe otra operación que nos da la noción del ángulo que existe entre dos vectores, esta operación se conoce como **producto interno**:\n", 432 | "\n", 433 | "$$\\cos \\theta = \\frac{\\vec{x} \\bullet \\vec{y}} {||\\vec{x}|| \\ ||\\vec{y}||}$$\n", 434 | "\n", 435 | "La operación en el numerador es la suma de la multiplicación *elemento-a-elemento*: \n", 436 | "\n", 437 | "$$\\vec{x} \\bullet \\vec{y} = \\sum_i{x_i y_i}$$ \n", 438 | "\n", 439 | "Si estamos hablando de vectores unitarios podemos ignorar el denominador (la norma de ambos vectores es $1$).\n", 440 | "\n", 441 | "Evidentemente, el producto interno está solamente definido para vectores con dimensiones iguales." 442 | ] 443 | }, 444 | { 445 | "cell_type": "code", 446 | "execution_count": null, 447 | "id": "05-algebra-lineal-29", 448 | "metadata": {}, 449 | "outputs": [], 450 | "source": [ 451 | "x = [1, 2, 3, 4]\n", 452 | "y = [4, 3, 2, 1]\n", 453 | "\n", 454 | "print(np.inner(x, y))" 455 | ] 456 | }, 457 | { 458 | "cell_type": "markdown", 459 | "id": "05-algebra-lineal-30", 460 | "metadata": {}, 461 | "source": [ 462 | "El producto interno es una generalización del producto punto.\n", 463 | "\n", 464 | "Este producto nos ayuda cuando queremos comparar un par de vectores, para revisar si es que ambos apuntan a la misma dirección." 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "id": "05-algebra-lineal-31", 470 | "metadata": {}, 471 | "source": [ 472 | "### Producto externo\n", 473 | "\n", 474 | "Existe otra clase de producto que dados dos vectores $\\vec{x}$ y $\\vec{y}$ está definido de la siguiente manera: \n", 475 | "\n", 476 | "$$\\vec{x} \\otimes \\vec{y} = \\begin{bmatrix}\n", 477 | "x_1 y_1 & x_1 y_2 & \\dots & x_1 y_m \\\\\n", 478 | "x_2 y_1 & x_2 y_2 & \\dots & x_2 y_m \\\\\n", 479 | "\\dots\\\\\n", 480 | "x_n y_1 & x_n y_2 & \\dots & x_n y_m \\\\\n", 481 | "\\end{bmatrix}$$\n", 482 | "\n", 483 | "Sí, es una matriz cuyas entradas están definidas por $A=\\vec{x}\\otimes\\vec{y}$ are: $A_{ij} = x_i y_j$ ." 484 | ] 485 | }, 486 | { 487 | "cell_type": "markdown", 488 | "id": "05-algebra-lineal-32", 489 | "metadata": {}, 490 | "source": [ 491 | "### Producto cruz \n", 492 | "\n", 493 | "En tres dimensiones existe un producto llamado **producto cruz**, dados dos vectores $\\vec{a}$ e $\\vec{b}$, el resultado es un tercero que está separado exactamente 90 grados de los dos vectores que se usaron para generarlo. \n", 494 | "\n", 495 | "\n", 496 | "$${\\displaystyle {\\begin{alignedat}{3}\\mathbf {\\vec{a}} &=a_{1}\\mathbf {\\color {blue}{i}} &&+a_{2}\\mathbf {\\color {red}{j}} &&+a_{3}\\mathbf {\\color {lime}{k}}\\end{alignedat}}}$$ \n", 497 | "\n", 498 | "$${\\displaystyle {\\begin{alignedat}{3}\\mathbf {\\vec{b}} &=b_{1}\\mathbf {\\color {blue}{i}} &&+b_{2}\\mathbf {\\color {red}{j}} &&+b_{3}\\mathbf {\\color {lime}{k}} \\end{alignedat}}}$$\n", 499 | "\n", 500 | "$${\\displaystyle {\\begin{aligned}\\mathbf {\\vec{a} \\times \\vec{b}} &=(a_{2}b_{3}\\mathbf {i} +a_{3}b_{1}\\mathbf {j} +a_{1}b_{2}\\mathbf {k} )-(a_{3}b_{2}\\mathbf {i} +a_{1}b_{3}\\mathbf {j} +a_{2}b_{1}\\mathbf {k} )\\\\&=(a_{2}b_{3}-a_{3}b_{2})\\mathbf {i} +(a_{3}b_{1}-a_{1}b_{3})\\mathbf {j} +(a_{1}b_{2}-a_{2}b_{1})\\mathbf {k}\\end{aligned}}}$$" 501 | ] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "id": "05-algebra-lineal-33", 506 | "metadata": {}, 507 | "source": [ 508 | "## Estadísticas sobre vectores \n", 509 | "\n", 510 | "Ya hablamos sobre algunas operaciones en vectores; podemos utilizarlas para definir operaciones estadísticas (que existen en el mundo de los números reales).\n", 511 | "\n", 512 | "### Centroide geométrico \n", 513 | "\n", 514 | "Supongamos que tenemos una colección de $N$ vectores, podemos definir el promedio de esta colección como la suma de todos los vectores dividida entre $N$: \n", 515 | "\n", 516 | "$$mean(\\vec{x_1}, \\vec{x_2}, \\dots, \\vec{x_n}) = \\frac{1}{N} \\sum_i {\\vec{x_i}}$$ \n", 517 | "En el mundo de los vectores este vector promedio se conoce como el **centroide** del conjunto de $N$ vectores definidos anteriormente.\n", 518 | "\n", 519 | "*NumPy* nos hace muy sencillo calcular el vector promedio usando `np.mean(vectors, axis=0)`:" 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "execution_count": null, 525 | "id": "05-algebra-lineal-34", 526 | "metadata": {}, 527 | "outputs": [], 528 | "source": [ 529 | "centro = 25\n", 530 | "spread = 5\n", 531 | "vectors = np.random.normal(centro, spread, (50, 2))\n", 532 | "\n", 533 | "\n", 534 | "fig = plt.figure(figsize=(10, 10), dpi=200)\n", 535 | "ax = fig.gca()\n", 536 | "\n", 537 | "centroid = np.mean(vectors, axis=0)\n", 538 | "\n", 539 | "ax.scatter(vectors[:, 0], vectors[:, 1], label=\"Vectores\")\n", 540 | "ax.scatter([centroid[0]], [centroid[1]], s=50, label=\"Centroid\")\n", 541 | "ax.legend()\n", 542 | "# ax.set_xlim((centro-spread, centro+spread))\n", 543 | "# ax.set_ylim((centro-spread, centro+spread))" 544 | ] 545 | }, 546 | { 547 | "cell_type": "markdown", 548 | "id": "05-algebra-lineal-35", 549 | "metadata": {}, 550 | "source": [ 551 | "### Centrando un dataset \n", 552 | "\n", 553 | "Tomando el centroide, podemos realizar una operación muy importante conocida como **centrar un dataset** si tomamos todos los vectores de nuestro conjunto y le restamos este centroide. Esto nos va a llevar a que nuestro nuevo conjunto de vectores (llamémosle \"centrado\") tiene un **promedio cero**:" 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "execution_count": null, 559 | "id": "05-algebra-lineal-36", 560 | "metadata": {}, 561 | "outputs": [], 562 | "source": [ 563 | "fig = plt.figure(figsize=(10, 10), dpi=200)\n", 564 | "ax = fig.gca()\n", 565 | "ax.axhline(0, c=\"k\", ls=\":\")\n", 566 | "ax.axvline(0, c=\"k\", ls=\":\")\n", 567 | "\n", 568 | "centroid = np.mean(vectors, axis=0)\n", 569 | "centrado = vectors - centroid\n", 570 | "new_centroid = np.mean(centrado, axis=0)\n", 571 | "\n", 572 | "\n", 573 | "ax.scatter(vectors[:, 0], vectors[:, 1], label=\"Vectores originales\")\n", 574 | "ax.scatter([centroid[0]], [centroid[1]], s=50, label=\"Centroide original\")\n", 575 | "\n", 576 | "ax.arrow(\n", 577 | " centroid[0],\n", 578 | " centroid[1],\n", 579 | " new_centroid[0] - centroid[0],\n", 580 | " new_centroid[1] - centroid[1],\n", 581 | " head_width=1,\n", 582 | " linestyle=\"-\",\n", 583 | " width=0.2,\n", 584 | " overhang=1,\n", 585 | " length_includes_head=True,\n", 586 | " color=\"black\",\n", 587 | ")\n", 588 | "\n", 589 | "ax.scatter(centrado[:, 0], centrado[:, 1], label=\"Vectores centrados\")\n", 590 | "ax.scatter([new_centroid[0]], [new_centroid[1]], s=50, label=\"Nuevo centroide\")\n", 591 | "ax.grid()\n", 592 | "ax.legend()" 593 | ] 594 | }, 595 | { 596 | "cell_type": "markdown", 597 | "id": "05-algebra-lineal-37", 598 | "metadata": {}, 599 | "source": [ 600 | "### Midiendo la extensión (*Measuring the spread*) \n", 601 | "\n", 602 | "Generalizando el concepto de varianza (que nos ayuda a medir la extensión de un conjunto de datos) podemos encontrar la extensión de nuestro conjunto de datos en el caso multidimensional. Recordando la forma unidimensional: \n", 603 | "\n", 604 | "$$\\sigma^2 = \\frac{1}{N-1} \\sum_{i=0}^{N-1}{(x_i - \\mu)^2}$$\n", 605 | "\n", 606 | "(La varianza es el promedio de las sumas del cuadrado de las diferencias de cada elemento con el promedio...)\n", 607 | "\n", 608 | "De la varianza podemos obtener la desviación estándar si calculamos la raíz cuadrada de $\\sigma$.\n", 609 | "\n", 610 | "En el caso multidimensional, tenemos que calcular algo conocido como **covarianza**, esta covarianza nos dice qué tan ... \n", 611 | "\n", 612 | "Este es el promedio de las diferencias de cada elemento de una columna con el promedio del resto de las columnas: \n", 613 | "\n", 614 | "$$\\Sigma_{ij} = \\frac{1}{N-1} \\sum_{k=1}^{N} (X_{ki}-\\mu_i)(X_{kj}-\\mu_j)$$\n", 615 | "\n", 616 | "En *NumPy* podemos calcular la covarianza con `np.cov`:" 617 | ] 618 | }, 619 | { 620 | "cell_type": "code", 621 | "execution_count": null, 622 | "id": "05-algebra-lineal-38", 623 | "metadata": {}, 624 | "outputs": [], 625 | "source": [ 626 | "x = np.random.normal(2, 1, (100, 2)) @ np.array(\n", 627 | " [[0.5, 0.1], [-0.9, -3.0]]\n", 628 | ") # 30 filas y 4 columnas\n", 629 | "\n", 630 | "sigma_np = np.cov(x, rowvar=False)\n", 631 | "sigma_np" 632 | ] 633 | }, 634 | { 635 | "cell_type": "markdown", 636 | "id": "05-algebra-lineal-39", 637 | "metadata": {}, 638 | "source": [ 639 | "#### Elipses de covarianza \n", 640 | "\n", 641 | "Podemos ver gráficamente la covarianza a través de elipses; este elipse parece \"capturar\" todo nuestro dataset. El centroide del dataset es el centro del elipse y la matriz de covarianza representa la forma del mismo: " 642 | ] 643 | }, 644 | { 645 | "cell_type": "code", 646 | "execution_count": null, 647 | "id": "05-algebra-lineal-40", 648 | "metadata": {}, 649 | "outputs": [], 650 | "source": [ 651 | "fig = plt.figure(figsize=(5, 5), dpi=200)\n", 652 | "ax = fig.gca()\n", 653 | "ax.scatter(x[:, 0], x[:, 1], s=5)\n", 654 | "draw_covariance_ellipse(ax, x, 0.5)\n", 655 | "draw_covariance_ellipse(ax, x, 1.0)\n", 656 | "draw_covariance_ellipse(ax, x, 2.0)" 657 | ] 658 | }, 659 | { 660 | "cell_type": "markdown", 661 | "id": "05-algebra-lineal-41", 662 | "metadata": {}, 663 | "source": [ 664 | "## Espacios vectoriales de grandes dimensiones\n", 665 | "\n", 666 | "Trabajar con información representada en forma de arreglos de una, dos y hasta tres dimensiones es sencillo: estamos familiarizados con ella, podemos graficarla, es fácil visualizarla mentalmente... sin embargo, muchas veces, la ciencia de datos involucra espacios vectoriales de grandes dimensiones. \n", 667 | "\n", 668 | "Piensa en una imagen pequeña, de unos 500 pixeles de ancho por 500 de alto, esta información es representada en un vector de $500 \\times 500 \\times 3 = 750,000$ entradas." 669 | ] 670 | }, 671 | { 672 | "cell_type": "markdown", 673 | "id": "05-algebra-lineal-42", 674 | "metadata": {}, 675 | "source": [ 676 | "### La maldición de la dimensionalidad 😱 \n", 677 | "\n", 678 | "¿Cómo se escucha trabajar con vectores de 1000, 5000, 10000 elementos? \n", 679 | "\n", 680 | "\n", 681 | "Hay algoritmos que se desempeñan perfectamente cuando operamos con vectores en espacios de pocas dimensiones pero cuando aumentamos la cantidad de elementos en los vectores comienzan a fallar.\n", 682 | "\n", 683 | "La **maldición de la dimensionalidad** hace que buscar en un espacio sea complejo afectando a los algoritmos que funcionan particionando el dataset: Árboles de decisión, Redes Neuronales, Máquinas de Vectores, Nearest Neighbours, K-means...\n", 684 | "\n", 685 | "Entre más dimensiones, es más difícil generalizar." 686 | ] 687 | }, 688 | { 689 | "cell_type": "markdown", 690 | "id": "05-algebra-lineal-43", 691 | "metadata": {}, 692 | "source": [ 693 | "## Matrices \n", 694 | "\n", 695 | "Ya habíamos hablado sobre matrices, ahora vamos a ver algunas de las operaciones que podemos hacer con algo conocido como álgebra de matrices, también conocida como álgebra lineal: \n", 696 | "\n", 697 | "### Suma de matrices\n", 698 | "\n", 699 | "$$\\begin{equation} A + B = \\begin{bmatrix}\n", 700 | "a_{1,1} + b_{1,1} & a_{1,2} + b_{1,2} & \\dots & a_{1,m} + b_{1,m} \\\\\n", 701 | "a_{2,1} + b_{2,1} & a_{2,2} + b_{2,2} & \\dots & a_{2,m} + b_{2,m} \\\\\n", 702 | "\\dots & \\dots & \\dots & \\dots \\\\\n", 703 | "a_{n,1} + b_{n,1} & a_{n,2} + b_{n,2} & \\dots & a_{n,m} + b_{n,m} \\\\\n", 704 | "\\end{bmatrix}\n", 705 | "\\end{equation}$$ \n", 706 | "\n", 707 | "### Multiplicación por escalar \n", 708 | "\n", 709 | "$$\\begin{equation} cA = \\begin{bmatrix}\n", 710 | "ca_{1,1} & ca_{1,2} & \\dots & ca_{1,m} \\\\\n", 711 | "ca_{2,1} & ca_{2,2} & \\dots & ca_{2,m} \\\\\n", 712 | "\\dots & \\dots & \\dots & \\dots \\\\\n", 713 | "ca_{n,1} & ca_{n,2} & \\dots & ca_{n,m} \\\\\n", 714 | "\\end{bmatrix}\n", 715 | "\\end{equation}$$\n", 716 | "\n", 717 | "### Multiplicación entre matrices \n", 718 | "\n", 719 | "El resultado de una multiplicación de matrices $A$ y $B$ es otra matriz: \n", 720 | "\n", 721 | "$$AB = C$$ \n", 722 | "\n", 723 | "La multiplicación de matrices solamente está definida para matrices $A$ y $B$ si: \n", 724 | "\n", 725 | " - $A$ tiene dimensiones $p \\times q$\n", 726 | " - $B$ tiene dimensiones $q \\times r$ \n", 727 | "\n", 728 | "Puedes ver la multiplicación de matrices definida con la siguiente función: \n", 729 | "\n", 730 | "$$\\begin{equation}C_{ij}=\\sum_k a_{ik} b_{kj} \\end{equation}$$ \n", 731 | "\n", 732 | "La multiplicación de matrices viene integrada en *NumPy*: \n", 733 | "\n", 734 | " - `np.dot(A, B)` \n", 735 | " - `A @ B`" 736 | ] 737 | }, 738 | { 739 | "cell_type": "code", 740 | "execution_count": null, 741 | "id": "05-algebra-lineal-44", 742 | "metadata": {}, 743 | "outputs": [], 744 | "source": [ 745 | "A = np.random.randint(0, 10, (3, 5))\n", 746 | "B = np.random.randint(0, 10, (5, 2))\n", 747 | "\n", 748 | "dot_result = np.dot(A, B)\n", 749 | "at_result = A @ B\n", 750 | "\n", 751 | "print(A.shape, B.shape)\n", 752 | "print()\n", 753 | "print(dot_result)\n", 754 | "print()\n", 755 | "print(at_result)\n", 756 | "print()\n", 757 | "print(np.allclose(dot_result, at_result))" 758 | ] 759 | }, 760 | { 761 | "cell_type": "markdown", 762 | "id": "05-algebra-lineal-45", 763 | "metadata": {}, 764 | "source": [ 765 | "### Matrices y vectores \n", 766 | "\n", 767 | "También podemos multiplicar matrices por un vector... de nuevo, las reglas anteriormente definidas aplican: \n", 768 | "\n", 769 | "$$\\vec{x} = \n", 770 | "\\begin{bmatrix}\n", 771 | "x_1\\\\\n", 772 | "x_2\\\\\n", 773 | "\\dots\\\\\n", 774 | "x_n\\\\\n", 775 | "\\end{bmatrix}\n", 776 | "$$\n", 777 | "\n", 778 | "El vector de acá arriba se conoce como **vector columna** de dimensión $D \\times 1$, mientras que el de abajo se conoce como **vector fila** de dimensión $1 \\times D$: \n", 779 | "\n", 780 | "$$\\vec{y} =\n", 781 | "\\begin{bmatrix}\n", 782 | "x_1 &\n", 783 | "x_2 & \n", 784 | "\\dots & \n", 785 | "x_n\n", 786 | "\\end{bmatrix}\n", 787 | "$$\n", 788 | "\n", 789 | "Dada nuestra definición de multiplicación, multiplicar $\\vec{x}\\vec{y}$ resultará en una matriz de $D \\times D$ (el producto externo 😉), mientras que multiplicar $\\vec{y}\\vec{x}$ resultará en un escalar (el producto interno 😉):" 790 | ] 791 | }, 792 | { 793 | "cell_type": "code", 794 | "execution_count": null, 795 | "id": "05-algebra-lineal-46", 796 | "metadata": {}, 797 | "outputs": [], 798 | "source": [ 799 | "x = np.array([[1, 2, 3]])\n", 800 | "y = np.array([[4, 5, 6]])\n", 801 | "\n", 802 | "print(\"x\", x.shape)\n", 803 | "print(x, \"\\n\")\n", 804 | "print(\"y\", y.shape)\n", 805 | "print(y, \"\\n\")" 806 | ] 807 | }, 808 | { 809 | "cell_type": "code", 810 | "execution_count": null, 811 | "id": "05-algebra-lineal-47", 812 | "metadata": {}, 813 | "outputs": [], 814 | "source": [ 815 | "print(np.outer(x, y))\n", 816 | "print(x.T @ y)" 817 | ] 818 | }, 819 | { 820 | "cell_type": "code", 821 | "execution_count": null, 822 | "id": "05-algebra-lineal-48", 823 | "metadata": {}, 824 | "outputs": [], 825 | "source": [ 826 | "print(np.inner(y, x))\n", 827 | "print(y @ x.T)" 828 | ] 829 | }, 830 | { 831 | "cell_type": "code", 832 | "execution_count": null, 833 | "id": "05-algebra-lineal-49", 834 | "metadata": {}, 835 | "outputs": [], 836 | "source": [ 837 | "# Multiplicación matricial\n", 838 | "\n", 839 | "x = np.array([1, 2, 3])\n", 840 | "A = np.array([[1, 0, 1], [1, 0, 0], [0, 1, 1]]) # 3x3\n", 841 | "print(x.shape)\n", 842 | "print(x)\n", 843 | "print(A.shape)\n", 844 | "print(A)\n", 845 | "print()\n", 846 | "print(A @ x)" 847 | ] 848 | }, 849 | { 850 | "cell_type": "code", 851 | "execution_count": null, 852 | "id": "05-algebra-lineal-50", 853 | "metadata": {}, 854 | "outputs": [], 855 | "source": [ 856 | "x = np.array([1, 2, 3])\n", 857 | "\n", 858 | "\n", 859 | "print(np.dot(x, x))\n", 860 | "print(x @ x.T)" 861 | ] 862 | }, 863 | { 864 | "cell_type": "markdown", 865 | "id": "05-algebra-lineal-51", 866 | "metadata": {}, 867 | "source": [ 868 | " > ⚠️ A diferencia de con los números reales, la multiplicación de matrices no es conmutativa:\n", 869 | " \n", 870 | "$$AB \\neq BA$$\n", 871 | "\n", 872 | " > ⚠️ Aún que las dimensiones sean compatibles, en general al multiplicación de matrices no es conmutativa." 873 | ] 874 | }, 875 | { 876 | "cell_type": "markdown", 877 | "id": "05-algebra-lineal-52", 878 | "metadata": {}, 879 | "source": [ 880 | "### Exponenciación de matrices \n", 881 | "\n", 882 | "Una vez que tenemos a nuestra disposición la multiplicación, podemos definir el hecho de multiplicar una matriz **cuadrada** por si misma como la exponenciación matricial: \n", 883 | "\n", 884 | "$$A^4=AAAA$$" 885 | ] 886 | }, 887 | { 888 | "cell_type": "markdown", 889 | "id": "05-algebra-lineal-53", 890 | "metadata": {}, 891 | "source": [ 892 | "## Matrices especiales \n", 893 | "\n", 894 | "### Matriz diagonal \n", 895 | "\n", 896 | "Matrices para las que los elementos $A_{ij} == 0$ en donde $i \\neq j$ se llaman matrices diagonales, tienen significado especial en el álgebra lineal. \n", 897 | "\n", 898 | "Podemos convertir un vector de longitud $D$ en una matriz de $D \\times D$ con los elementos del vector en la diagonal usando `np.diag`:" 899 | ] 900 | }, 901 | { 902 | "cell_type": "code", 903 | "execution_count": null, 904 | "id": "05-algebra-lineal-54", 905 | "metadata": {}, 906 | "outputs": [], 907 | "source": [ 908 | "x = np.random.randint(0, 10, (4,))\n", 909 | "diag_x = np.diag(x)\n", 910 | "\n", 911 | "print(x)\n", 912 | "print()\n", 913 | "print(diag_x)" 914 | ] 915 | }, 916 | { 917 | "cell_type": "markdown", 918 | "id": "05-algebra-lineal-55", 919 | "metadata": {}, 920 | "source": [ 921 | "Es posible extraer el vector de la diagonal de una matriz usando... `np.diag`:" 922 | ] 923 | }, 924 | { 925 | "cell_type": "code", 926 | "execution_count": null, 927 | "id": "05-algebra-lineal-56", 928 | "metadata": {}, 929 | "outputs": [], 930 | "source": [ 931 | "x = np.random.randint(0, 10, (4, 4))\n", 932 | "diag_x = np.diag(x)\n", 933 | "\n", 934 | "print(x)\n", 935 | "print()\n", 936 | "print(diag_x)" 937 | ] 938 | }, 939 | { 940 | "cell_type": "markdown", 941 | "id": "05-algebra-lineal-57", 942 | "metadata": {}, 943 | "source": [ 944 | "### Matriz identidad \n", 945 | "\n", 946 | "Una matriz cuadrada cuyos elementos son $0$ en todas partes excepto en la diagonal en donde los valores son $1$ es conocida como una matriz identidad. \n", 947 | "\n", 948 | "La matriz identidad no provoca ningún cambio cuando la usamos en una multiplicación. En *NumPy* podemos usar `np.eye(n)` para generar una matriz identidad de tamaño `n`:" 949 | ] 950 | }, 951 | { 952 | "cell_type": "code", 953 | "execution_count": null, 954 | "id": "05-algebra-lineal-58", 955 | "metadata": {}, 956 | "outputs": [], 957 | "source": [ 958 | "x = np.random.randint(0, 10, (3, 3))\n", 959 | "I = np.eye(3, dtype=int)\n", 960 | "\n", 961 | "print(x)\n", 962 | "print(x @ I)\n", 963 | "print(I @ x)\n", 964 | "# print(I * x) # Not the same!" 965 | ] 966 | }, 967 | { 968 | "cell_type": "markdown", 969 | "id": "05-algebra-lineal-59", 970 | "metadata": {}, 971 | "source": [ 972 | "### Matriz cero \n", 973 | "\n", 974 | "Ah... lo que dice el título" 975 | ] 976 | }, 977 | { 978 | "cell_type": "markdown", 979 | "id": "05-algebra-lineal-60", 980 | "metadata": {}, 981 | "source": [ 982 | "### Matriz cuadrada \n", 983 | "\n", 984 | "🔲🔳◾️▫️" 985 | ] 986 | }, 987 | { 988 | "cell_type": "markdown", 989 | "id": "05-algebra-lineal-61", 990 | "metadata": {}, 991 | "source": [ 992 | "### Matriz triangular \n", 993 | "\n", 994 | "Una matriz es considerada triangular si los elementos debajo o arriba de la diagonal son cero. \n", 995 | "\n", 996 | "Existen dos subtipos: \n", 997 | "\n", 998 | " - **Triangular superior**\n", 999 | " \n", 1000 | "$$\\begin{bmatrix}\n", 1001 | "1 & 2 & 3 & 4 \\\\\n", 1002 | "0 & 5 & 6 & 7 \\\\\n", 1003 | "0 & 0 & 8 & 9 \\\\\n", 1004 | "0 & 0 & 0 & 10 \\\\\n", 1005 | "\\end{bmatrix}\n", 1006 | "$$\n", 1007 | " \n", 1008 | " - **Triangular inferior** \n", 1009 | " \n", 1010 | "$$\\begin{bmatrix}\n", 1011 | "1 & 0 & 0 & 0 \\\\\n", 1012 | "2 & 3 & 0 & 0 \\\\\n", 1013 | "4 & 5 & 6 & 0 \\\\\n", 1014 | "7 & 8 & 9 & 10 \\\\\n", 1015 | "\\end{bmatrix}\n", 1016 | "$$\n", 1017 | "\n", 1018 | "Podemos usar `np.tri(n)` para generar una matriz triangular inferior de $1$ usando *NumPy*:" 1019 | ] 1020 | }, 1021 | { 1022 | "cell_type": "code", 1023 | "execution_count": null, 1024 | "id": "05-algebra-lineal-62", 1025 | "metadata": {}, 1026 | "outputs": [], 1027 | "source": [ 1028 | "triangular = np.tri(4)\n", 1029 | "\n", 1030 | "print(triangular)\n", 1031 | "print()\n", 1032 | "print(triangular.T)" 1033 | ] 1034 | }, 1035 | { 1036 | "cell_type": "markdown", 1037 | "id": "05-algebra-lineal-63", 1038 | "metadata": {}, 1039 | "source": [ 1040 | "### Matrices dispersas \n", 1041 | "\n", 1042 | "A pesar de que no hemos hablado de estas matrices (ni hablaremos en el futuro en este curso) sobre matrices dispersas, son una parte central en la ciencia de datos. \n", 1043 | "\n", 1044 | "Imagina que en Spotify tienen una matriz en donde las filas son las todas las canciones y las columnas los usuarios, y los valores dentro de la matriz representan si al usuario le gusta o no dicha canción, ¿cómo crees que se vería esa matriz?\n", 1045 | "\n", 1046 | "| | Feregrino | Alma | Benito | ... | Terry | Destiny |\n", 1047 | "|--------------------|-----------|-----------|-----------|-----|-----------|-----------|\n", 1048 | "| La Puerta Negra | ${\\bf 1}$ | $0$ | $0$ | | $0$ | $0$ |\n", 1049 | "| Flux | $0$ | ${\\bf 1}$ | $0$ | | ${\\bf 1}$ | $0$ |\n", 1050 | "| La mesa del rincón | ${\\bf 1}$ | $0$ | $0$ | | $0$ | $0$ |\n", 1051 | "| Andar conmigo | ${\\bf 1}$ | ${\\bf 1}$ | $0$ | | ${\\bf 1}$ | $0$ |\n", 1052 | "| ... | | | | ... | | |\n", 1053 | "| Dark Horse | $0$ | ${\\bf 1}$ | ${\\bf 1}$ | | $0$ | ${\\bf 1}$ |\n", 1054 | "\n", 1055 | "Lo más seguro es que la gran mayoría de las personas no disfruten de la gran mayoría de las canciones, entonces la matriz de acá arriba muy probablemente estará dominada por $0$, con muy pocos $1$ esparcidos aquí y allá.\n", 1056 | "\n", 1057 | "¿Cómo lidiarías con ella?" 1058 | ] 1059 | }, 1060 | { 1061 | "cell_type": "markdown", 1062 | "id": "05-algebra-lineal-64", 1063 | "metadata": {}, 1064 | "source": [ 1065 | "---------\n", 1066 | "## Resources\n", 1067 | "\n", 1068 | "\n", 1069 | " \n", 1070 | " \n", 1071 | " \n", 1076 | " \n", 1081 | " \n", 1086 | " \n", 1091 | " \n", 1096 | " \n", 1101 | " \n", 1106 | " \n", 1107 | " \n", 1108 | "
\n", 1072 | " \n", 1073 | " \n", 1074 | " \n", 1075 | " \n", 1077 | " \n", 1078 | " \n", 1079 | " \n", 1080 | " \n", 1082 | " \n", 1083 | " \n", 1084 | " \n", 1085 | " \n", 1087 | " \n", 1088 | " \n", 1089 | " \n", 1090 | " \n", 1092 | " \n", 1093 | " \n", 1094 | " \n", 1095 | " \n", 1097 | " \n", 1098 | " \n", 1099 | " \n", 1100 | " \n", 1102 | " \n", 1103 | " \n", 1104 | " \n", 1105 | "
\n", 1109 | "\n", 1110 | "### Sitios web\n", 1111 | "\n", 1112 | " - **Espacios y subespacios vectoriales** - [https://aga.frba.utn.edu.ar/espacios-y-subespacios-vectoriales/](https://aga.frba.utn.edu.ar/espacios-y-subespacios-vectoriales/) \n", 1113 | " \n", 1114 | " - **High-Dimensional Space** - [https://www.cs.cmu.edu/~venkatg/teaching/CStheory-infoage/chap1-high-dim-space.pdf](https://www.cs.cmu.edu/~venkatg/teaching/CStheory-infoage/chap1-high-dim-space.pdf)\n", 1115 | " \n", 1116 | " - **Maldición de la dimensión** - [https://es.wikipedia.org/wiki/Maldici%C3%B3n_de_la_dimensi%C3%B3n](https://es.wikipedia.org/wiki/Maldici%C3%B3n_de_la_dimensi%C3%B3n) \n", 1117 | " \n", 1118 | " - **List of named matrices** - [https://en.wikipedia.org/wiki/List_of_named_matrices](https://en.wikipedia.org/wiki/List_of_named_matrices)\n", 1119 | "\n", 1120 | "### Videos\n", 1121 | "\n", 1122 | " - **3blue1brown Linear Algebra series** -[https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab](https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab) \n", 1123 | "\n", 1124 | "### Libros\n", 1125 | "\n", 1126 | " - **Introduction to applied linear algebra** - [http://stanford.edu/~boyd/vmls/vmls.pdf](http://stanford.edu/~boyd/vmls/vmls.pdf)\n", 1127 | "\n", 1128 | " - **Coding the matrix** - [https://codingthematrix.com/](https://codingthematrix.com/)\n", 1129 | " " 1130 | ] 1131 | } 1132 | ], 1133 | "metadata": { 1134 | "kernelspec": { 1135 | "display_name": "Python 3", 1136 | "language": "python", 1137 | "name": "python3" 1138 | }, 1139 | "language_info": { 1140 | "codemirror_mode": { 1141 | "name": "ipython", 1142 | "version": 3 1143 | }, 1144 | "file_extension": ".py", 1145 | "mimetype": "text/x-python", 1146 | "name": "python", 1147 | "nbconvert_exporter": "python", 1148 | "pygments_lexer": "ipython3", 1149 | "version": "3.8.6" 1150 | } 1151 | }, 1152 | "nbformat": 4, 1153 | "nbformat_minor": 5 1154 | } 1155 | -------------------------------------------------------------------------------- /06-algebra-lineal-matrices.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "06-algebra-lineal-matrices-0", 6 | "metadata": {}, 7 | "source": [ 8 | "# 06: Álgebra Lineal Computacional (Matrices)\n", 9 | "\n", 10 | "\n", 11 | " \n", 12 | " \n", 13 | " \n", 18 | " \n", 23 | " \n", 28 | " \n", 33 | " \n", 38 | " \n", 43 | " \n", 48 | " \n", 49 | " \n", 50 | "
\n", 14 | " \n", 15 | " \n", 16 | " \n", 17 | " \n", 19 | " \n", 20 | " \n", 21 | " \n", 22 | " \n", 24 | " \n", 25 | " \n", 26 | " \n", 27 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | "
" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "06-algebra-lineal-matrices-1", 56 | "metadata": {}, 57 | "source": [ 58 | "## Paquetes \n", 59 | "\n", 60 | " - `numpy`\n", 61 | " - `pandas`\n", 62 | " - `matplotlib`\n", 63 | " - `scikit-learn`" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "id": "06-algebra-lineal-matrices-2", 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "import numpy as np\n", 74 | "import pandas as pd\n", 75 | "import random\n", 76 | "import matplotlib.pyplot as plt\n", 77 | "from df.display_algebra import draw_covariance_ellipse" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "id": "06-algebra-lineal-matrices-3", 83 | "metadata": {}, 84 | "source": [ 85 | "## Descomposición matricial \n", 86 | "\n", 87 | "¿Recuerdas los factores primos? recuerdas que un número puede ser obtenido a partir de sus factores primos, por ejemplo: \n", 88 | "\n", 89 | "$$42 = 2 \\times 3 \\times 7$$ \n", 90 | "\n", 91 | "Este proceso se conoce como descomposición matemática. Como el título de esta sección lo indica, las matrices también se pueden descomponer de esta manera en objetos relativamente más simples. \n", 92 | "\n", 93 | "Estas descomposiciones pueden ser usadas para diversos fines: \n", 94 | "\n", 95 | " - Análisis de datos representados como matrices\n", 96 | " - Ejecución eficiente de operaciones matriciales\n", 97 | "\n", 98 | "En ejemplos más tangibles: podemos usar descomposiciones para [sistemas de recomendación](https://datajobs.com/data-science-repo/Recommender-Systems-[Netflix].pdf), [motores de búsqueda](https://www.maa.org/press/periodicals/loci/joma/the-linear-algebra-behind-search-engines-an-advanced-vector-space-model-lsi), [compresión de imágenes](http://timbaumann.info/svd-image-compression-demo/), [reconocimiento facial](http://openimaj.org/tutorial/eigenfaces.html), y [otras aplicaciones](https://heartbeat.fritz.ai/applications-of-matrix-decompositions-for-machine-learning-f1986d03571a)." 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "id": "06-algebra-lineal-matrices-4", 104 | "metadata": {}, 105 | "source": [ 106 | "### Puntos fijos\n", 107 | "\n", 108 | "Supongamos que tenemos un punto $x$, tal que si le aplicamos una función $f$, el resultado es $x$. Es decir si: \n", 109 | "\n", 110 | "$$f(x) = x$$ \n", 111 | "\n", 112 | "Podemos decir que $x$ es un punto fijo de $f$. Por ejemplo, supongamos que $f(x) = x^2$: \n", 113 | " - Cuando $x = 0$, $f(x) = 0$\n", 114 | " - Cuando $x = 1$, $f(1) = 1$\n", 115 | " \n", 116 | "Tanto $x = 0$ como $x = 1$ son considerados puntos fijos de $x^2$.\n", 117 | "\n", 118 | " > 🤔 existen dos tipos de puntos fijos: estables y no estables. Se dice que un punto fijo es estable si cualquier valor en su vecindad hace que la función se acerque al punto fijo, mientras que se dice que uno no es estable si cualquier valor en su vecindad hace que la función se aleje de este. " 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "id": "06-algebra-lineal-matrices-5", 124 | "metadata": {}, 125 | "source": [ 126 | "Podemos obtener los puntos fijos de una función de manera *ingenua* eligiendo un valor aleatorio $x$ y aplicando sucesivamente la regla $x_t=f(x_{t-1})$ hasta llegar a un punto estable, ese es tu punto fijo:" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "id": "06-algebra-lineal-matrices-6", 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "def find_fixed_point(f, r, eps=1e-5):\n", 137 | "\n", 138 | " fr = f(r)\n", 139 | " history = [(r, fr)]\n", 140 | " # ¿Ya dejamos de movernos?\n", 141 | " while np.abs(fr - r) > eps and np.abs(fr) < np.inf:\n", 142 | " r = fr\n", 143 | " fr = f(r)\n", 144 | " history.append((r, fr))\n", 145 | "\n", 146 | " return fr, np.array(history)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": null, 152 | "id": "06-algebra-lineal-matrices-7", 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "def function(x):\n", 157 | " return np.cos(x)\n", 158 | "\n", 159 | "\n", 160 | "r = np.random.uniform(-5, 5) # random starting point\n", 161 | "fixed, history = find_fixed_point(function, r)\n", 162 | "\n", 163 | "print(f\"Punto fijo {fixed:0.5}\")\n", 164 | "print(f\"f({fixed:0.5}) = {function(fixed):0.5}\")" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "id": "06-algebra-lineal-matrices-8", 170 | "metadata": {}, 171 | "source": [ 172 | "## Eigen.. ¿qué?\n", 173 | "\n", 174 | "De cierto modo, cada matriz representan una función conocida como [aplicación lineal](https://es.wikipedia.org/wiki/Aplicaci%C3%B3n_lineal), dentro de las aplicaciones lineales existen las [transformaciones lineales] que transforman elementos entre espacios vectoriales.\n", 175 | "\n", 176 | "### Eigenvalores y eigenvectores \n", 177 | "\n", 178 | "Si bien las transformaciones lineales tienen puntos fijos, existen valores análogos, y aún más interesantes para analizar de una matriz: \n", 179 | "\n", 180 | " - **Eigenvectores** \n", 181 | " - **Eigenvalores** \n", 182 | "\n", 183 | " > **eigen** = propio (o característico).\n", 184 | "\n", 185 | "#### Método de las potencias \n", 186 | "\n", 187 | "Vamos a tomar una matriz cuadrada $A$ y aplicarla a un vector aleatorio $\\vec{x}$, al resultado volverle a aplicar $A$ y luego volverle a aplicar $A$... es decir, calculamos:\n", 188 | "\n", 189 | "$$AAA \\dots AA\\vec{x}$$\n", 190 | "$$A^n\\vec{x}$$\n", 191 | "\n", 192 | "En la práctica esto causaría que el resultado crezca hasta infinito o colapse a cero. Podemos normalizar el resultado hacer algo para contrarrestar este efecto:\n", 193 | "\n", 194 | "\n", 195 | "$$\\begin{equation}x_n = \\frac{Ax_{n-1}}{\\|Ax_{n-1}\\|_\\infty}\\end{equation}$$\n", 196 | "\n", 197 | "El resultado a cada paso será forzado a ser un vector unitario usando al norma mínima $L_\\infty$. Finalmente, a este método lo vamos a conocer como el **método de las potencias** o *power iteration method*." 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "id": "06-algebra-lineal-matrices-9", 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "def power_iterate(A, x, n):\n", 208 | " for i in range(n):\n", 209 | " x = A @ x # multiply\n", 210 | " x = x / np.linalg.norm(x, np.inf) # normalize\n", 211 | "\n", 212 | " return x / np.linalg.norm(x, 2)" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "id": "06-algebra-lineal-matrices-10", 218 | "metadata": {}, 219 | "source": [ 220 | "Comencemos con nuestra matriz $A$:" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": null, 226 | "id": "06-algebra-lineal-matrices-11", 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "A = np.random.normal(0, 1, (2, 2))\n", 231 | "print(A)" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "id": "06-algebra-lineal-matrices-12", 237 | "metadata": {}, 238 | "source": [ 239 | "Seguimos con un vector cualquiera:" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": null, 245 | "id": "06-algebra-lineal-matrices-13", 246 | "metadata": {}, 247 | "outputs": [], 248 | "source": [ 249 | "cualquiera = np.random.normal(0, 3, (2,))\n", 250 | "print(cualquiera)" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": null, 256 | "id": "06-algebra-lineal-matrices-14", 257 | "metadata": {}, 258 | "outputs": [], 259 | "source": [ 260 | "# El vector resultante -casi siempre- es el mismo\n", 261 | "eigenvector = power_iterate(A, cualquiera, n=500)\n", 262 | "print(eigenvector)" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "id": "06-algebra-lineal-matrices-15", 268 | "metadata": {}, 269 | "source": [ 270 | "Este vector es conocido como el **eigenvector principal**, y es una característica de la matriz. \n", 271 | "\n", 272 | "La matriz $A$ toma nuestro vector y la única transformación que le hace es escalarlo (hacerlo más grande) sin rotaciones ni sesgos.\n", 273 | "\n", 274 | "Para calcular el factor de escalamiento podemos..." 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": null, 280 | "id": "06-algebra-lineal-matrices-16", 281 | "metadata": {}, 282 | "outputs": [], 283 | "source": [ 284 | "ratio = (A @ eigenvector) / eigenvector\n", 285 | "eigenvalue = ratio[0] # Todos los elementos tienen el mismo valor.\n", 286 | "print(eigenvalue)" 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "id": "06-algebra-lineal-matrices-17", 292 | "metadata": {}, 293 | "source": [ 294 | "El escalar que ves más arriba es conocido como el **eigenvalor principal**, llamémoslo $\\lambda$ por el momento, y satisface esta ecuación:\n", 295 | "\n", 296 | "$$A\\vec{x}_i = \\lambda_i\\vec{x}_i$$" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": null, 302 | "id": "06-algebra-lineal-matrices-18", 303 | "metadata": {}, 304 | "outputs": [], 305 | "source": [ 306 | "uno = A @ eigenvector\n", 307 | "dos = eigenvector * eigenvalue\n", 308 | "\n", 309 | "np.allclose(uno, dos)" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "id": "06-algebra-lineal-matrices-19", 315 | "metadata": {}, 316 | "source": [ 317 | "Cada vector $\\vec{x}_i$ que satisfaga la ecuación es un **eigenvector** y cada $\\lambda_i$ que cumpla esta ecuación es un **eigenvalue**. Estos elementos vienen en pares." 318 | ] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "id": "06-algebra-lineal-matrices-20", 323 | "metadata": {}, 324 | "source": [ 325 | "### Otros eigenvalores \n", 326 | "\n", 327 | "El método de las potencias nos ayuda a encontrar **un solo** vector, sin embargo pueden existir múltiples pares de **eigen cosas**, para encontrar los otros, podemos seguir este algoritmo: \n", 328 | "\n", 329 | "#### Encontrar más valores..." 330 | ] 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "id": "06-algebra-lineal-matrices-21", 335 | "metadata": {}, 336 | "source": [ 337 | "Mientras tanto en NumPy..." 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": null, 343 | "id": "06-algebra-lineal-matrices-22", 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [ 347 | "evals, evecs = np.linalg.eig(A)\n", 348 | "print(evals[0])\n", 349 | "print(evecs[:, 0])" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "id": "06-algebra-lineal-matrices-23", 355 | "metadata": {}, 356 | "source": [ 357 | "## El espectro... *eigenspectro*. \n", 358 | "\n", 359 | "A la secuencia eigenvalores ordenados por su valor absoluto se le conoce como el eigenespectro de una matriz; esto nos puede ayudar a encontrar versiones simplificadas de nuestras matrices.\n", 360 | "\n", 361 | "Supongamos que tenemos un dataset de 18K jugadores de fútbol:" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": null, 367 | "id": "06-algebra-lineal-matrices-24", 368 | "metadata": {}, 369 | "outputs": [], 370 | "source": [ 371 | "fifa19 = pd.read_csv(\"assets/06/fifa19.csv\")\n", 372 | "# https://fifauteam.com/fifa-19-attributes-guide/\n", 373 | "\n", 374 | "attributes = [\n", 375 | " \"Acceleration\",\n", 376 | " \"SprintSpeed\", # Pace\n", 377 | " \"Finishing\",\n", 378 | " \"LongShots\",\n", 379 | " \"Penalties\",\n", 380 | " \"Positioning\",\n", 381 | " \"ShotPower\",\n", 382 | " \"Volleys\", # Shooting\n", 383 | " \"Crossing\",\n", 384 | " \"Curve\",\n", 385 | " \"FKAccuracy\",\n", 386 | " \"LongPassing\",\n", 387 | " \"ShortPassing\",\n", 388 | " \"Vision\", # Passing\n", 389 | " \"Agility\",\n", 390 | " \"Balance\",\n", 391 | " \"BallControl\",\n", 392 | " \"Composure\",\n", 393 | " \"Dribbling\",\n", 394 | " \"Reactions\", # Dribbling\n", 395 | " \"HeadingAccuracy\",\n", 396 | " \"Interceptions\",\n", 397 | " \"Marking\",\n", 398 | " \"SlidingTackle\",\n", 399 | " \"StandingTackle\", # Defending\n", 400 | " \"Aggression\",\n", 401 | " \"Jumping\",\n", 402 | " \"Stamina\",\n", 403 | " \"Strength\", # Physical\n", 404 | " # 'GKDiving', 'GKHandling', 'GKKicking', 'GKPositioning', 'GKReflexes', # Goalkeeping\n", 405 | "]\n", 406 | "\n", 407 | "player_stats = fifa19[attributes].copy().fillna(axis=\"index\", method=\"backfill\")\n", 408 | "player_stats.head()" 409 | ] 410 | }, 411 | { 412 | "cell_type": "markdown", 413 | "id": "06-algebra-lineal-matrices-25", 414 | "metadata": {}, 415 | "source": [ 416 | "Calculando la matriz de covarianza y vemos los eigenvectores y los valores correspondientes a cada uno de ellos:" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": null, 422 | "id": "06-algebra-lineal-matrices-26", 423 | "metadata": {}, 424 | "outputs": [], 425 | "source": [ 426 | "players_cov = np.cov(player_stats.values, rowvar=False)\n", 427 | "eigs, eigv = np.linalg.eig(players_cov)\n", 428 | "\n", 429 | "order = np.argsort(eigs)\n", 430 | "eigs, eigv = eigs[order], eigv[order]\n", 431 | "\n", 432 | "fig = plt.figure()\n", 433 | "ax = fig.gca()\n", 434 | "ax.bar(np.arange(len(eigs)), list(reversed(eigs)))\n", 435 | "ax.set_xlabel(\"Eigenvalor\")\n", 436 | "ax.set_ylabel(\"Amplitud\")\n", 437 | "ax.set_title(\"Eigenspectrum of the FIFA 19 dataset\")" 438 | ] 439 | }, 440 | { 441 | "cell_type": "markdown", 442 | "id": "06-algebra-lineal-matrices-27", 443 | "metadata": {}, 444 | "source": [ 445 | "Los eigenvectores nos ayudan a identificar los ejes a lo largo de los que nuestro dataset varía:" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": null, 451 | "id": "06-algebra-lineal-matrices-28", 452 | "metadata": {}, 453 | "outputs": [], 454 | "source": [ 455 | "def print_vector(vector):\n", 456 | " properties = []\n", 457 | " for attribute, value in zip(attributes, vector):\n", 458 | " properties.append(f\"{attribute:<20s} {value:+.03}\")\n", 459 | " results = \"\\n\".join(properties)\n", 460 | " print(results)\n", 461 | "\n", 462 | "\n", 463 | "print(\"Eigenvector 1\")\n", 464 | "print_vector(eigv[0])\n", 465 | "print()\n", 466 | "print(\"Eigenvector 2\")\n", 467 | "print_vector(eigv[0])" 468 | ] 469 | }, 470 | { 471 | "cell_type": "markdown", 472 | "id": "06-algebra-lineal-matrices-29", 473 | "metadata": {}, 474 | "source": [ 475 | "Estos dos vectores son las direcciones con mayor varianza en nuestro dataset... difícil de ver e imaginar porque estamos hablando de un espacio de 29 dimensiones." 476 | ] 477 | }, 478 | { 479 | "cell_type": "markdown", 480 | "id": "06-algebra-lineal-matrices-30", 481 | "metadata": {}, 482 | "source": [ 483 | "## PCA \n", 484 | "\n", 485 | "Lo que acabamos de hacer acá arriba se conoce como **análisis de componentes principales** o *principal component análisis* (*PCA*). \n", 486 | "\n", 487 | "Los componentes principales de un dataset son los eigenvectores de su matriz de covarianza. Usando este análisis de componentes principales podemos encontrar una proyección de bajas dimensiones para nuestros datos si proyectamos nuestro dataset en los vectores principales:" 488 | ] 489 | }, 490 | { 491 | "cell_type": "code", 492 | "execution_count": null, 493 | "id": "06-algebra-lineal-matrices-31", 494 | "metadata": {}, 495 | "outputs": [], 496 | "source": [ 497 | "def pca(data, n_components):\n", 498 | " cov = np.cov(data, rowvar=False)\n", 499 | " eigs, eigv = np.linalg.eig(cov)\n", 500 | "\n", 501 | " order = np.argsort(eigs)\n", 502 | " eigs, eigv = eigs[order], eigv[order]\n", 503 | " eig_order = np.argsort(np.abs(eigs))\n", 504 | " components = []\n", 505 | " for i in range(n_components):\n", 506 | " components.append(data @ eigv[eig_order[-i - 1]])\n", 507 | " return np.stack(components).T" 508 | ] 509 | }, 510 | { 511 | "cell_type": "code", 512 | "execution_count": null, 513 | "id": "06-algebra-lineal-matrices-32", 514 | "metadata": {}, 515 | "outputs": [], 516 | "source": [ 517 | "components = pca(player_stats.values, 2)" 518 | ] 519 | }, 520 | { 521 | "cell_type": "code", 522 | "execution_count": null, 523 | "id": "06-algebra-lineal-matrices-33", 524 | "metadata": {}, 525 | "outputs": [], 526 | "source": [ 527 | "player_ids = [158023, 20801, 190871, 238789, 231747, 194765, 208722, 209331] + [\n", 528 | " 156519,\n", 529 | " 186302,\n", 530 | " 192041,\n", 531 | " 187478,\n", 532 | " 167524,\n", 533 | " 213445,\n", 534 | " 217167,\n", 535 | " 224103,\n", 536 | " 173434,\n", 537 | " 186551,\n", 538 | " 187208,\n", 539 | " 199569,\n", 540 | " 190485,\n", 541 | " 233260,\n", 542 | " 187705,\n", 543 | " 186979,\n", 544 | " 167532,\n", 545 | " 212377,\n", 546 | " 183743,\n", 547 | " 139229,\n", 548 | " 203283,\n", 549 | " 171377,\n", 550 | " 222645,\n", 551 | " 192015,\n", 552 | " # 186513, 232316, 239545, 187722, 237498, 169415, 202786, 192046,\n", 553 | " # 235368, 235123, 236434, 237978, 246188, 243157, 236842, 240177,\n", 554 | " # 237657, 244349, 244611, 193164, 242823, 210426, 244743, 233535,\n", 555 | " 244615,\n", 556 | " 212475,\n", 557 | " 246294,\n", 558 | " 244831,\n", 559 | " 240107,\n", 560 | " 239548,\n", 561 | " 244637,\n", 562 | "]\n", 563 | "\n", 564 | "\n", 565 | "index = fifa19[fifa19[\"ID\"].isin(set(player_ids))].index\n", 566 | "comp = components[index]" 567 | ] 568 | }, 569 | { 570 | "cell_type": "code", 571 | "execution_count": null, 572 | "id": "06-algebra-lineal-matrices-34", 573 | "metadata": {}, 574 | "outputs": [], 575 | "source": [ 576 | "len(comp), len(index), len(set(player_ids)), len(player_ids)" 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": null, 582 | "id": "06-algebra-lineal-matrices-35", 583 | "metadata": {}, 584 | "outputs": [], 585 | "source": [ 586 | "fifa19[fifa19[\"Name\"].str.contains(\"Mané\")]" 587 | ] 588 | }, 589 | { 590 | "cell_type": "code", 591 | "execution_count": null, 592 | "id": "06-algebra-lineal-matrices-36", 593 | "metadata": {}, 594 | "outputs": [], 595 | "source": [ 596 | "fig = plt.figure(figsize=(10, 5), dpi=100)\n", 597 | "ax = fig.gca()\n", 598 | "\n", 599 | "ax.scatter(comp[:, 0], comp[:, 1])\n", 600 | "for i, player_id in enumerate(player_ids):\n", 601 | " ax.text(\n", 602 | " comp[i, 0],\n", 603 | " comp[i, 1],\n", 604 | " fifa19[fifa19[\"ID\"] == player_id][\"Name\"].values[0],\n", 605 | " fontsize=6,\n", 606 | " )\n", 607 | "\n", 608 | "ax.set_xlabel(\"Componente 1\")\n", 609 | "ax.set_ylabel(\"Componente 2\")\n", 610 | "ax.set_title(\"Componentes principales FIFA19\")" 611 | ] 612 | }, 613 | { 614 | "cell_type": "markdown", 615 | "id": "06-algebra-lineal-matrices-37", 616 | "metadata": {}, 617 | "source": [ 618 | "### Proyecciones de pocas dimensiones \n", 619 | "\n", 620 | "Las proyecciones de pocas dimensiones son herramientas de vital importancia en la ciencia de datos exploratoria. PCA es una forma de lograr esta tarea, sin embargo, no es la única. Existe otro algoritmo llamado *t-SNE* que, al igual que PCA nos permite descomponer nuestra información para proyectar nuestro dataset:" 621 | ] 622 | }, 623 | { 624 | "cell_type": "code", 625 | "execution_count": null, 626 | "id": "06-algebra-lineal-matrices-38", 627 | "metadata": {}, 628 | "outputs": [], 629 | "source": [ 630 | "from sklearn.manifold import TSNE\n", 631 | "\n", 632 | "t_reduce = TSNE()\n", 633 | "xy_2d = t_reduce.fit_transform(player_stats.values)[index]" 634 | ] 635 | }, 636 | { 637 | "cell_type": "code", 638 | "execution_count": null, 639 | "id": "06-algebra-lineal-matrices-39", 640 | "metadata": {}, 641 | "outputs": [], 642 | "source": [ 643 | "fig = plt.figure(figsize=(10, 5), dpi=100)\n", 644 | "ax = fig.add_subplot(1, 1, 1)\n", 645 | "\n", 646 | "ax.scatter(xy_2d[:, 0], xy_2d[:, 1])\n", 647 | "for i, player_id in enumerate(player_ids):\n", 648 | " ax.text(\n", 649 | " xy_2d[i, 0],\n", 650 | " xy_2d[i, 1],\n", 651 | " fifa19[fifa19[\"ID\"] == player_id][\"Name\"].values[0],\n", 652 | " fontsize=6,\n", 653 | " )\n", 654 | "\n", 655 | "\n", 656 | "ax.set_xlabel(\"t-SNE componente 1\")\n", 657 | "ax.set_ylabel(\"t-SNE componente 2\")\n", 658 | "ax.set_title(\"t-SNE transformation of the whisky dataset\")" 659 | ] 660 | }, 661 | { 662 | "cell_type": "markdown", 663 | "id": "06-algebra-lineal-matrices-40", 664 | "metadata": {}, 665 | "source": [ 666 | "## Otras propiedades de las matrices \n", 667 | "\n", 668 | "### Traza \n", 669 | "\n", 670 | "La traza $\\text{tr}$ de una matriz cuadrada es la suma de los elementos en la diagonal:\n", 671 | "\n", 672 | "$$\\text{tr}(A) = a_{1,1} + a_{2,2} + \\dots + a_{n,n}$$ \n", 673 | "\n", 674 | "Esto es igual que la suma de los eigenvalores: \n", 675 | "\n", 676 | "$$\\text{tr}(A) = \\sum_{i=1}^n \\lambda_i$$\n", 677 | "\n", 678 | "### Determinante \n", 679 | "\n", 680 | "El determinante $\\text{det}$ de una matriz es igual a la multiplicación de los eigenvalores: \n", 681 | "\n", 682 | "$$\\text{det}(A) = \\prod_{i=1}^n \\lambda_i$$ \n", 683 | "\n", 684 | "Cuando alguno de los eigenvalores de la matriz es $0$ el determinante es $0$, lo cual tiene implicaciones para la matriz.\n", 685 | "\n", 686 | " > ⚠️ Hay matrices con eigenvalores complejos, sin embargo... no las veremos aquí.\n", 687 | "\n", 688 | "\n", 689 | "## Inversion de matrices \n", 690 | "\n", 691 | "Recordarás las operaciones básicas sobre las matrices: \n", 692 | " \n", 693 | " - Multiplicación por un escalar: $cA$\n", 694 | " - Suma matricial: $A + B$\n", 695 | " - Multiplicación matricial: $AB$\n", 696 | " - Transposición: $A^T$ \n", 697 | " \n", 698 | "Ves la suma y la multiplicación y tal vez te preguntes... ¿hay división? \n", 699 | "\n", 700 | "La operación más parecida es la inversa de una matriz, que nos permite: \n", 701 | "\n", 702 | " - $A^{-1}(A\\vec{x}) = \\vec{x}$ \n", 703 | " - $A^{-1}A = I$ \n", 704 | " - $(A^{-1})^{-1} = A$ \n", 705 | " \n", 706 | "Sin embargo esta operación, (la inversa) no está siempre definida para todas las matrices. En particular, la inversa no está definida para matrices **no cuadradas** y con **determinante = $0$**. \n", 707 | "\n", 708 | "Una matriz es llamada **singular** si su determinante es 0 (y por tanto no invertible) y es llamada **no singular** si es posible invertirla.\n", 709 | "\n", 710 | "#### Algoritmo \n", 711 | "\n", 712 | "Hay diversas maneras de calcular la inversa de una matriz, incluyendo un algoritmo recursivo; este algoritmo recursivo funciona, pero solo para matrices pequeñas. Más adelante hablaremos de una forma efectiva para invertir matrices.\n", 713 | " \n", 714 | "### ¿Qué problemas resuelve la inversión de matrices? \n", 715 | "\n", 716 | "#### Sistemas lineales \n", 717 | "\n", 718 | "Imagina que tenemos la siguiente matriz: \n", 719 | "\n", 720 | "$$A = \\begin{bmatrix}\n", 721 | "0.5 & 1.0 & 2.0\\\\\n", 722 | "1.0 & 0.5 & 0.0\\\\\n", 723 | "0.6 & 1.1 & -0.3\\\\\n", 724 | "\\end{bmatrix}$$ \n", 725 | "\n", 726 | "Esta matriz representa una aplicación lineal que opera sobre vectores tridimensionales $\\vec{x}$, y que produce vectores 3D $\\vec{y}$. Cada uno de las entradas de este vector es una suma ponderada de las entradas del vector $\\vec{x}$:\n", 727 | "\n", 728 | "$$y_1 = 0.5x_1 + 1.0x_2 +2.0x_3\\\\\n", 729 | "y_2 = 1.0x_1 +0.5x_2 +0.0x_3\\\\\n", 730 | "y_3 = 0.6x_1 + 1.1x_2 - 0.3x_3$$" 731 | ] 732 | }, 733 | { 734 | "cell_type": "code", 735 | "execution_count": null, 736 | "id": "06-algebra-lineal-matrices-41", 737 | "metadata": {}, 738 | "outputs": [], 739 | "source": [ 740 | "A = np.array([[0.5, 1.0, 2.0], [1.0, 0.5, 0.0], [0.6, 1.1, -0.3]])\n", 741 | "x = np.array([1, -1, 1])\n", 742 | "\n", 743 | "print(A @ x)" 744 | ] 745 | }, 746 | { 747 | "cell_type": "markdown", 748 | "id": "06-algebra-lineal-matrices-42", 749 | "metadata": {}, 750 | "source": [ 751 | "Viéndolo desde otra perspectiva, esta es la forma en la que sistemas de ecuaciones simultaneas pueden ser representadas de la siguiente forma:\n", 752 | "\n", 753 | "$$A\\vec{x} = \\vec{y}$$\n", 754 | "\n", 755 | "Esto es conocido como un sistema de ecuaciones lineares. \n", 756 | "\n", 757 | "Partiendo de esto, y de que ahora sabemos que la inversa de una matriz existe, podríamos pensar que la solución es bastante trivial:\n", 758 | "\n", 759 | "$$\n", 760 | "A^{-1}A\\vec{x}=A^{-1}\\vec{y} \\\\\n", 761 | "I \\vec{x} = A^{-1} \\vec{y} \\\\\n", 762 | "\\vec{x} = A^{-1}\\vec{y}\n", 763 | "$$\n", 764 | "\n", 765 | " > Recuerda que la inversa de una matriz solamente está definida si la matriz es cuadrada \n", 766 | " \n", 767 | "En la práctica, los sistemas lineales casi nunca son resueltos invirtiendo directamente la matriz; hay problemas de estabilidad numérica así como de complejidad algorítmica que lo hacen un procedimiento inviable.\n", 768 | "\n", 769 | "### Soluciones aproximadas \n", 770 | "\n", 771 | "En lugar de tratar de invertir de una vez la matriz, resuelven el problema de forma iterativa, una de las formas es utilizando optimización, algo que veremos en el futuro. " 772 | ] 773 | }, 774 | { 775 | "cell_type": "markdown", 776 | "id": "06-algebra-lineal-matrices-43", 777 | "metadata": {}, 778 | "source": [ 779 | "## Descomposición en valores singulares \n", 780 | "\n", 781 | "La eigendescomposición que vimos hace poco solamente funciona para matrices cuadradas, sin embargo no siempre nuestros problemas van a venir en esta forma; es aquí en donde entra otra forma de descomponer matrices:\n", 782 | "\n", 783 | "La *singular value decomposition* (SVD) es una forma general de descomponer cualquier matriz $A$. Es una de las grandes herramientas del álgebra lineal. \n", 784 | "\n", 785 | "*SVD* descompone una matriz en tres partes: \n", 786 | "\n", 787 | "$$A = U \\Sigma V$$ \n", 788 | "\n", 789 | "En donde: \n", 790 | "\n", 791 | " - $A$ es la matriz inicial de $m \\times n$, \n", 792 | " - $U$ es una matriz **ortonormal** de $m \\times m$, conocida como **vectores singulares izquierdos**,\n", 793 | " - $V$ es una matriz **ortonormal** de $n \\times n$, conocida como **vectores singulares derechos**,\n", 794 | " - $\\Sigma$ es una matriz diagonal de $m \\times n$, la diagonal son los **valores singulares**\n", 795 | " \n", 796 | " > Una matriz ortonormal $U$ es una matriz que cumple que $U^{-1} =U^T$ y que las columnas y filas tienen norma unitaria. \n", 797 | " \n", 798 | "La diagonal de $\\Sigma$ son los **valores singulares** que son parecidos a los *eigenvectores* sin embargo no son lo mismo. Por ejemplo, los valores singulares siempre son números positivos. \n", 799 | "\n", 800 | "En *NumPy*..." 801 | ] 802 | }, 803 | { 804 | "cell_type": "code", 805 | "execution_count": null, 806 | "id": "06-algebra-lineal-matrices-44", 807 | "metadata": {}, 808 | "outputs": [], 809 | "source": [ 810 | "A = np.array([[3, 2, 3, 2], [2, 9, 4, 2], [4, 1, 9, 8]])\n", 811 | "\n", 812 | "\n", 813 | "u, sigma, v = np.linalg.svd(A)\n", 814 | "true_sigma = np.zeros_like(A, dtype=np.float64)\n", 815 | "np.fill_diagonal(true_sigma, sigma)\n", 816 | "A_reconstructed = u @ true_sigma @ v\n", 817 | "\n", 818 | "\n", 819 | "print(u)\n", 820 | "print()\n", 821 | "# print(sigma)\n", 822 | "print(true_sigma)\n", 823 | "print()\n", 824 | "print(v)\n", 825 | "\n", 826 | "print()\n", 827 | "print(A_reconstructed)" 828 | ] 829 | }, 830 | { 831 | "cell_type": "markdown", 832 | "id": "06-algebra-lineal-matrices-45", 833 | "metadata": {}, 834 | "source": [ 835 | "### SVD 🤝 eigendescomposición \n", 836 | "\n", 837 | "La *SVD* es: \n", 838 | "\n", 839 | " - Obtener los eigenvectores de $A^T A$ para obtener $U$\n", 840 | " - Obtener los eigenvectores de $A A^T$ para obtener $V$ \n", 841 | " - Calcular la raíz cuadrada de los eigenvalores de $A^T A$" 842 | ] 843 | }, 844 | { 845 | "cell_type": "markdown", 846 | "id": "06-algebra-lineal-matrices-46", 847 | "metadata": {}, 848 | "source": [ 849 | "### Aplicaciones de las descomposiciones \n", 850 | "\n", 851 | "Una vez que tenemos la descomposición hay operaciones que se vuelven triviales: \n", 852 | "\n", 853 | "$$A = U\\Sigma V$$\n", 854 | "$$A^{-1} = V^T \\Sigma^\\dagger U^T$$ \n", 855 | "\n", 856 | " - $\\Sigma^\\dagger$ es $\\text{diag}(1.0/\\Sigma_{ii})^T$ \n", 857 | " \n", 858 | "### Pseudo-inversa...\n", 859 | "\n", 860 | "$\\dots$" 861 | ] 862 | }, 863 | { 864 | "cell_type": "markdown", 865 | "id": "06-algebra-lineal-matrices-47", 866 | "metadata": {}, 867 | "source": [ 868 | "## Los valores singulares \n", 869 | "\n", 870 | "Como te imaginarás, los valores singulares capturan algunos aspectos esenciales de una matriz. \n", 871 | "\n", 872 | " - Se dice que una matriz es de **rango completo** si su número de valores singulares distintos de cero es igual al número de elementos en la diagonal de la matriz; de otro modo se trata de una matriz de **rango deficiente**. \n", 873 | " \n", 874 | " - El **número condicional** de una matriz es la relación entre su valor singular más grande y el más pequeño; este número nos dice qué tan sensible es la aplicación lineal a cambios, es decir en la operación $A\\vec{x} = \\vec{y}$ nos dice si $\\vec{y}$ cambia mucho o poco cuando realizamos pequeños cambios a $\\vec{x}$. Se relaciona con los conceptos de matrices **bien condicionadas** y **mal condicionadas**. \n", 875 | " \n", 876 | "### Los valores singulares y la singularidad \n", 877 | "\n", 878 | "Habíamos dicho que una matriz es singular si es *no invertible* y tiene $\\text{det}(A) = 0$, esta definición es binaria, los conceptos de rango y número condicional nos ayudan a convertir esta distinción en un gradiente, usando estos dos números podemos responder la pregunta \"¿Qué tan singular es la matriz?\"" 879 | ] 880 | }, 881 | { 882 | "cell_type": "markdown", 883 | "id": "06-algebra-lineal-matrices-48", 884 | "metadata": {}, 885 | "source": [ 886 | "## Aplicaciones de las descomposiciones \n", 887 | "\n", 888 | "### Operaciones *avanzadas*. \n", 889 | "\n", 890 | "Ahora que podemos descomponer una matriz, podemos plantearnos el realizar operaciones como: \n", 891 | "\n", 892 | " - Elevar una matriz a una potencia fraccionaria $A^{\\frac{1}{3}}$ \n", 893 | " - Invertir una matriz ${A^{-1}}$\n", 894 | " - Calcular el logaritmo de una matriz $\\ln{A}$ \n", 895 | " \n", 896 | "La forma de hacerlo es \"sencilla\": ignoramos $U$ y $V$ y se le aplica la operación en cuestión a todos los elementos de $\\Sigma$.\n", 897 | "\n", 898 | "### Esferización (*sphering*)\n", 899 | "\n", 900 | "El **esferización** (mas comúnmente conocido como blanqueamiento) consiste en remover todas las correlaciones lineales dentro de un dataset, es una forma de normalizar un conjunto de datos que se realiza antes de realizar un análisis. \n", 901 | "\n", 902 | "Dado una matriz $X$: \n", 903 | "\n", 904 | "$$X^* =(X-\\mu)\\Sigma^{-\\frac{1}{2}}$$\n", 905 | "\n", 906 | "en donde $\\vec{\\mu}$ es el **vector promedio** y $\\Sigma$ es la **matriz de covarianza**. \n", 907 | "\n", 908 | "El resultado de aplicar esta operación **centra el dataset** y modifica el dataset para que la matriz de covarianza sea **unitaria**. " 909 | ] 910 | }, 911 | { 912 | "cell_type": "code", 913 | "execution_count": null, 914 | "id": "06-algebra-lineal-matrices-49", 915 | "metadata": {}, 916 | "outputs": [], 917 | "source": [ 918 | "def apply_sphering(x):\n", 919 | " center_x = x - np.mean(x, axis=0)\n", 920 | " u, sigma, v = np.linalg.svd(np.cov(center_x, rowvar=False))\n", 921 | "\n", 922 | " # La magia\n", 923 | " sigma_inv_sqr = v.T @ np.diag(1.0 / np.sqrt(sigma)) @ u.T\n", 924 | " shphere_x = center_x @ sigma_inv_sqr\n", 925 | "\n", 926 | " return shphere_x" 927 | ] 928 | }, 929 | { 930 | "cell_type": "code", 931 | "execution_count": null, 932 | "id": "06-algebra-lineal-matrices-50", 933 | "metadata": {}, 934 | "outputs": [], 935 | "source": [ 936 | "X = np.random.normal(0, 1, (200, 2)) @ np.array([[0.1, 0.5], [-0.9, 1.0]]) + np.array(\n", 937 | " [2, 3]\n", 938 | ")\n", 939 | "X_sphered = apply_sphering(X)\n", 940 | "\n", 941 | "fig = plt.figure(figsize=(10, 10))\n", 942 | "ax = fig.gca()\n", 943 | "\n", 944 | "ax.scatter(X[:, 0], X[:, 1], c=\"#99d8c9\", label=\"Original\")\n", 945 | "ax.scatter(X_sphered[:, 0], X_sphered[:, 1], c=\"#feb24c\", label=\"Esferada\")\n", 946 | "\n", 947 | "for dataset in [X, X_sphered]:\n", 948 | " for std in [0.5, 1, 2]:\n", 949 | " draw_covariance_ellipse(ax, dataset, std)\n", 950 | "\n", 951 | "for one, two in zip(X, X_sphered):\n", 952 | " ax.plot([one[0], two[0]], [one[1], two[1]], alpha=0.2)\n", 953 | "\n", 954 | "# ax.set_xlim(-6, 6)\n", 955 | "# ax.set_ylim(-6, 6)\n", 956 | "\n", 957 | "ax.axhline(0)\n", 958 | "ax.axvline(0)\n", 959 | "ax.set_aspect(1.0)\n", 960 | "ax.legend()\n", 961 | "ax.set_title(\"Esferización de un dataset\")" 962 | ] 963 | }, 964 | { 965 | "cell_type": "markdown", 966 | "id": "06-algebra-lineal-matrices-51", 967 | "metadata": {}, 968 | "source": [ 969 | "### Aproximaciones de bajo rango\n", 970 | "\n", 971 | "¿Recuerdan las matrices dispersas de la sesión pasada?\n", 972 | "\n", 973 | "| | Feregrino | Alma | Benito | ... | Terry | Destiny |\n", 974 | "|--------------------|-----------|-----------|-----------|-----|-----------|-----------|\n", 975 | "| La Puerta Negra | ${\\bf 1}$ | $0$ | $0$ | | $0$ | $0$ |\n", 976 | "| Flux | $0$ | ${\\bf 1}$ | $0$ | | ${\\bf 1}$ | $0$ |\n", 977 | "| La mesa del rincón | ${\\bf 1}$ | $0$ | $0$ | | $0$ | $0$ |\n", 978 | "| Andar conmigo | ${\\bf 1}$ | ${\\bf 1}$ | $0$ | | ${\\bf 1}$ | $0$ |\n", 979 | "| ... | | | | ... | | |\n", 980 | "| Dark Horse | $0$ | ${\\bf 1}$ | ${\\bf 1}$ | | $0$ | ${\\bf 1}$ |\n", 981 | "\n", 982 | "Datasets como este tipo son el pan de cada día de compañías como Spotify, Netflix y Amazon. Lo que siempre están tratando de hacer es encontrar nuevos objetos para recomendarles a sus usuarios. \n", 983 | "\n", 984 | "Como vimos anteriormente, la gran mayoría de los usuarios han escuchado/visto/comprado una cantidad mínima de canciones/películas/productos. \n", 985 | "\n", 986 | "Centrándonos en el ejemplo de las canciones, una forma simplista de verlos podríamos encapsular a los usuarios dentro de grupos como \"el fan de los tigres del norte\", \"el que solo escucha canciones de my chemical romance\", \"el fan de música de tienda de ropa\"... sin embargo la realidad no funciona así, un modelo más realista es uno que representa a los usuarios como una suma ponderada: \n", 987 | "\n", 988 | "$$\\text{usuario} = 0.2 \\times \\text{tigres del norte} + 0.7 \\times \\text{mcr} + 0.1 \\times \\text{música de zara}$$ \n", 989 | "\n", 990 | "Esto nos permite usar solamente una parcialidad de la información de los usuarios para realizar recomendaciones; usamos la *SVD* para realizar esto. \n", 991 | "\n", 992 | "Podemos encontrar una aproximación de bajo rango truncando la *SVD* y manteniendo solamente una fracción, digamos $K$, de los valores singulares, y las primeras $K$ columnas y filas de $U$ y $V$ respectivamente.\n", 993 | "\n", 994 | "Esta es una forma de reducción dimensional." 995 | ] 996 | }, 997 | { 998 | "cell_type": "markdown", 999 | "id": "06-algebra-lineal-matrices-52", 1000 | "metadata": {}, 1001 | "source": [ 1002 | "[Ver recursos al final]" 1003 | ] 1004 | }, 1005 | { 1006 | "cell_type": "markdown", 1007 | "id": "06-algebra-lineal-matrices-53", 1008 | "metadata": {}, 1009 | "source": [ 1010 | "---------\n", 1011 | "## Recursos\n", 1012 | "\n", 1013 | "\n", 1014 | " \n", 1015 | " \n", 1016 | " \n", 1021 | " \n", 1026 | " \n", 1031 | " \n", 1036 | " \n", 1041 | " \n", 1046 | " \n", 1051 | " \n", 1052 | " \n", 1053 | "
\n", 1017 | " \n", 1018 | " \n", 1019 | " \n", 1020 | " \n", 1022 | " \n", 1023 | " \n", 1024 | " \n", 1025 | " \n", 1027 | " \n", 1028 | " \n", 1029 | " \n", 1030 | " \n", 1032 | " \n", 1033 | " \n", 1034 | " \n", 1035 | " \n", 1037 | " \n", 1038 | " \n", 1039 | " \n", 1040 | " \n", 1042 | " \n", 1043 | " \n", 1044 | " \n", 1045 | " \n", 1047 | " \n", 1048 | " \n", 1049 | " \n", 1050 | "
\n", 1054 | "\n", 1055 | "### Sitios web\n", 1056 | "\n", 1057 | " - **The Linear Algebra Behind Search Engines** - [https://www.maa.org/press/periodicals/loci/joma/the-linear-algebra-behind-search-engines-introduction](https://www.maa.org/press/periodicals/loci/joma/the-linear-algebra-behind-search-engines-introduction)\n", 1058 | " \n", 1059 | " - **Eigenfaces: Recovering Humans from Ghosts** - [https://towardsdatascience.com/eigenfaces-recovering-humans-from-ghosts-17606c328184](https://towardsdatascience.com/eigenfaces-recovering-humans-from-ghosts-17606c328184) \n", 1060 | " \n", 1061 | " - **Eigenfaces for recognition** - [https://www.face-rec.org/algorithms/pca/jcn.pdf](https://www.face-rec.org/algorithms/pca/jcn.pdf) \n", 1062 | " \n", 1063 | " - **Image Compression with Singular Value Decomposition** - [http://timbaumann.info/svd-image-compression-demo/](http://timbaumann.info/svd-image-compression-demo/)\n", 1064 | " \n", 1065 | " - **Eigenvectors and eigenvalues** - [http://setosa.io/ev/eigenvectors-and-eigenvalues/](http://setosa.io/ev/eigenvectors-and-eigenvalues/)\n", 1066 | " \n", 1067 | " - **Power Iteration | ML Wiki** - [http://mlwiki.org/index.php/Power_Iteration](http://mlwiki.org/index.php/Power_Iteration)\n", 1068 | " \n", 1069 | " - **PCA Whitening** - [http://ufldl.stanford.edu/tutorial/unsupervised/PCAWhitening](http://ufldl.stanford.edu/tutorial/unsupervised/PCAWhitening)\n", 1070 | " \n", 1071 | " - **Matrix Factorization for Movie Recommendations in Python** - [https://beckernick.github.io/_posts/2016-11-10-matrix-factorization-recommender/](https://beckernick.github.io/_posts/2016-11-10-matrix-factorization-recommender/)" 1072 | ] 1073 | } 1074 | ], 1075 | "metadata": { 1076 | "kernelspec": { 1077 | "display_name": "Python 3", 1078 | "language": "python", 1079 | "name": "python3" 1080 | }, 1081 | "language_info": { 1082 | "codemirror_mode": { 1083 | "name": "ipython", 1084 | "version": 3 1085 | }, 1086 | "file_extension": ".py", 1087 | "mimetype": "text/x-python", 1088 | "name": "python", 1089 | "nbconvert_exporter": "python", 1090 | "pygments_lexer": "ipython3", 1091 | "version": "3.8.6" 1092 | } 1093 | }, 1094 | "nbformat": 4, 1095 | "nbformat_minor": 5 1096 | } 1097 | --------------------------------------------------------------------------------