<jupyter_start><jupyter_text>**Numpy***** **Editado por: <NAME>** Contacto: <EMAIL> | [Linkedin](https://www.linkedin.com/in/kevin-alexander-g%C3%B3mez-2b0263111/) | [Github](https://github.com/kevinalexandr19)***  **Descripción**Usando este manual, aprenderás a usar Numpy, la librería de álgebra lineal y matemática en Python.Este Notebook es parte del proyecto [**Python para Geólogos**](https://github.com/kevinalexandr19/manual-python-geologia), y ha sido creado con la finalidad de facilitar el aprendizaje en Python para estudiantes y profesionales en el campo de la Geología.***  **1. ¿Qué es Numpy?**Numpy (Numeric Python) es un paquete fundamental para computación científica en Python a través del uso de objetos en arreglos multidimensionales.\Contiene herramientas de álgebra lineal, matemática, simulación aleatoria y estadística básica. **1.1. ¿Cómo usar Numpy?**La base de Numpy es el `ndarray`, en este manual nos referiremos a este objeto como un **arreglo**.\Los arreglos agrupan datos homogéneos en una o más dimensiones, tienen un tamaño fijo, y son más rápidos y eficientes en comparación con las listas o tuplas.Una de las ventajas más importantes de Numpy es el `broadcast`, que consiste en la habilidad de procesar arreglos de diferente tamaño como si todos tuvieran el mismo tamaño.Podemos considerar que un **vector** es un arreglo de 1 dimensión, mientras que una **matriz** es un arreglo de 2 dimensiones. Para usar Numpy dentro de Python, debemos importar la librería con el mismo nombre: > Usaremos `np` como una referencia abreviada de la librería.\> Para usar una función de Numpy, debemos anteponer su referencia (ejemplo: la función `np.log` calcula el logaritmo de un número).<jupyter_code>import numpy as np<jupyter_output><empty_output><jupyter_text>***  **2. Vectores**Usaremos la función `array` para crear un vector:<jupyter_code>vector = np.array([0, 1, 2, 3, 4, 5])
print(vector)<jupyter_output><empty_output><jupyter_text>Este **vector** contiene el mismo tipo de información en cada posición. Lo verificamos usando el atributo `dtype`:<jupyter_code>vector.dtype<jupyter_output><empty_output><jupyter_text>Para transformar los valores del vector de **integer** a **float**, usaremos la función `astype`:<jupyter_code>vector.astype(float)<jupyter_output><empty_output><jupyter_text>De manera similar a una lista o tupla, podemos seleccionar partes de un arreglo haciendo **slicing**:<jupyter_code># Primer elemento
vector[0]
# Del tercer al quinto elemento
vector[2:5]<jupyter_output><empty_output><jupyter_text>También podemos reemplazar valores dentro del vector:<jupyter_code>vector[-1] = 10
vector<jupyter_output><empty_output><jupyter_text>Podemos crear una copia del arreglo usando el método `copy`:<jupyter_code>copia = vector.copy()<jupyter_output><empty_output><jupyter_text>De esta forma, si modificamos un valor de la copia, el original permanecerá igual:<jupyter_code>copia[0] = -1
print(vector)
print(copia)<jupyter_output><empty_output><jupyter_text>Podemos ordenar los elementos en un arreglo con `sort`:<jupyter_code>x = np.array([3, 1, 4, 6, 2, 5, 0])
np.sort(x)<jupyter_output><empty_output><jupyter_text>***  **3. Matrices**Usaremos la función `array` y una **lista de listas** para crear una matriz:<jupyter_code>matriz = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(matriz)<jupyter_output><empty_output><jupyter_text>Para obtener el número de dimensiones, usaremos el atributo `ndim`:<jupyter_code>matriz.ndim<jupyter_output><empty_output><jupyter_text>Para obtener el número de elementos en un arreglo, usaremos el atributo `size`:<jupyter_code>matriz.size<jupyter_output><empty_output><jupyter_text>Para obtener la forma de un arreglo, usaremos el atributo `shape`:<jupyter_code>matriz.shape<jupyter_output><empty_output><jupyter_text>Para hacer **slicing** a una matriz, debemos señalar la posición de la fila y columna en el siguiente orden: `arreglo[fila, columna]`<jupyter_code># Primera fila, primera columna
matriz[0, 0]
# Primera fila, todas las columnas
matriz[0, :]
# A partir de la segunda fila, escoge todas las columnas
matriz[1:, :]<jupyter_output><empty_output><jupyter_text>Podemos usar el método `reshape` para cambiar la forma del arreglo:<jupyter_code>matriz.reshape((1, 9))<jupyter_output><empty_output><jupyter_text>La transpuesta de una matriz se calcula usando el atributo `T`:<jupyter_code>print(matriz.T)<jupyter_output><empty_output><jupyter_text>Para reordenar los elementos de una matriz en un solo vector, podemos utilizar la función `flatten`:<jupyter_code>matriz.flatten()<jupyter_output><empty_output><jupyter_text>Para el siguiente ejemplo, tenemos dos matrices de 2 x 2, llamadas A y B:<jupyter_code>A = np.array([[1, 1], [1, 1]])
B = np.array([[-1, 0], [0, -1]])
print(A)
print(B)<jupyter_output><empty_output><jupyter_text>Podemos agrupar las matrices horizontalmente usando la función `hstack`:<jupyter_code>np.hstack([A, B])<jupyter_output><empty_output><jupyter_text>O verticalmente usando `vstack`:<jupyter_code>np.vstack([A, B])<jupyter_output><empty_output><jupyter_text>También podemos usar la función `concatenate` para agruparlos de acuerdo a un eje (0 para vertical y 1 para horizontal).<jupyter_code>np.concatenate([A, B], axis=0)
np.concatenate([A, B], axis=1)<jupyter_output><empty_output><jupyter_text>Podemos crear matrices de valores 0 o 1 con las funciones `zeros` y `ones` respectivamente:<jupyter_code>np.zeros(shape=(3, 3))
np.ones(shape=(3, 3))<jupyter_output><empty_output><jupyter_text>***  **4. Álgebra lineal en Numpy**Tenemos dos vectores $v_{1}$ y $v_{2}$:<jupyter_code>v1 = np.array([1, 2, 3, 4])
v2 = np.array([5, 6, 7, 8])<jupyter_output><empty_output><jupyter_text>Podemos sumar y restar los vectores:<jupyter_code>print(v1 + v2)
print(v1 - v2)<jupyter_output><empty_output><jupyter_text>Multiplicar y dividirlos:<jupyter_code>print(v1 * v2)
print(v1 / v2)<jupyter_output><empty_output><jupyter_text>Calcular el producto escalar:<jupyter_code>np.dot(v1, v2)<jupyter_output><empty_output><jupyter_text>**Arreglo de valores booleanos**Si establecemos una condición sobre un arreglo, obtenemos un arreglo de valores booleanos. A este tipo de arreglos se les conoce como **máscaras** o **mask** en inglés:<jupyter_code>x = np.array([1, 2, 3, 4, 5])
print(x)
mask = (x > 2)
print(mask)<jupyter_output><empty_output><jupyter_text>También podemos establecer condiciones y devolver resultados en base a estos usando la función `where`.\Por ejemplo, reemplazaremos los valores de `x` mayores a 2 por el valor de 0 y los menores por el valor de 1:<jupyter_code>np.where(x > 2, 0, 1)<jupyter_output><empty_output><jupyter_text>**Funciones básicas para un arreglo** También podemos calcular la suma de componentes, máximo, mínimo, media, varianza y desviación estándar de cada vector:<jupyter_code>print(v1)
v1.sum()
v1.max()
v1.min()
v1.mean()
v1.var()
v1.std()<jupyter_output><empty_output><jupyter_text>**Constantes**<jupyter_code># Infinito
np.inf
# Valor nulo o vacío
np.nan
# Número de Euler
np.e
# Pi
np.pi<jupyter_output><empty_output><jupyter_text>**Espacios lineales**Podemos crear espacios lineales usando la función `arange`:<jupyter_code>np.arange(1, 10, 1)<jupyter_output><empty_output><jupyter_text>Y también usando `linspace`:<jupyter_code>np.linspace(1, 10, 10)<jupyter_output><empty_output><jupyter_text>**Valores aleatorios**Podemos obtener valores aleatorios usando el módulo `random`. El resultado es un arreglo cuyo tamaño se indica como parámetro de la función.\Para crear un **RNG (Random Number Generator)** que genera valores aleatorios, usaremos la función `default_rng`:<jupyter_code>rng = np.random.default_rng()<jupyter_output><empty_output><jupyter_text>Por ejemplo, eligiremos un número aleatorio entre 0 y 1:<jupyter_code>rng.random()<jupyter_output><empty_output><jupyter_text>Un número aleatorio de una distribución uniforme entre 0 y 5:<jupyter_code>rng.uniform(0, 5)<jupyter_output><empty_output><jupyter_text>Un número entero aleatorio entre 0 y 5:<jupyter_code>rng.integers(0, 5)<jupyter_output><empty_output><jupyter_text>Una matriz de tamaño 2 $\times$ 2 compuesta por valores aleatorios de una distribución normal de media 0 y varianza 1<jupyter_code>rng.normal(size=(2, 2))<jupyter_output><empty_output><jupyter_text>**Simulación de valores aleatorios** Podemos crear conjuntos de datos aleatorios usando distribuciones aleatorias.\Por ejemplo, crearemos 100 números aleatorios que se aproximen a la recta `y = 3x + 1`<jupyter_code>x = rng.normal(size=(10, 10))
y = 3*x + rng.normal(size=(10, 10)) + 1<jupyter_output><empty_output><jupyter_text>Usaremos `matplotlib` para graficar los datos:<jupyter_code>import matplotlib.pyplot as plt
plt.scatter(x, y);<jupyter_output><empty_output><jupyter_text>**Reproduciendo la aleatoriedad en Numpy**Podemos establecer un estado de aleatoriedad fijo a través del parámetro `seed`:<jupyter_code>rng = np.random.default_rng(seed=42)<jupyter_output><empty_output><jupyter_text>El primer resultado obtenido con la función `random` luego de establecer el estado aleatorio debe ser 0.7739560485559633:<jupyter_code>rng.random()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11331877729257642, 'fraction_numerical': 0.01943231441048035, 'mean_word_length': 7.774904214559387, 'pattern_counts': {'":': 0, '<': 167, '<?xml version=': 0, '>': 171, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '41', 'dir': 'jupyter-structured-clean-dedup', 'id': '347106', 'license': 'MIT', 'n_tokens_mistral': 3615, 'n_tokens_neox': 3488, 'n_words': 893, 'path': 'notebooks/1b_numpy.ipynb', 'repo_name': 'kevinalexandr19/manual-python-geologia', 'size': '9193'}