05 – Matrices en Python con Numpy

NumPy (Numerical Python) es un paquete de código abierto ampliamente utilizado en ciencia e ingeniería.

Realiza una amplia variedad de operaciones matemáticas avanzadas con alta eficiencia. En este artículo se verán varias funciones clave de NumPy como la creación de arrays, segmentación (slicing), indexación, cambio de forma (reshape) y apilamiento.

Arrays con Numpy

Los arrays son una de las estructuras de datos fundamentales de la librería NumPy, esenciales para organizar tus datos. Puedes pensar en ellos como una cuadrícula de valores, todos del mismo tipo.

Si has utilizado listas de Python anteriormente, recordarás que son convenientes, ya que puedes almacenar diferentes tipos de datos. Sin embargo, las listas de Python tienen funciones limitadas y ocupan más espacio y tiempo de procesamiento que los arreglos de NumPy.

NumPy proporciona un objeto array que es mucho más rápido y compacto que las listas de Python. A través de su amplia integración de API, la librería ofrece muchas funciones incorporadas que facilitan mucho el cómputo con solo unas pocas líneas de código. Esto puede ser una ventaja enorme al realizar operaciones matemáticas en grandes conjuntos de datos.

El objeto de array en NumPy se llama ndarray, que significa “array n-dimensional”. Un arreglo 1-D representa una lista estándar de valores en una dimensión. Recuerda que en NumPy, todos los elementos dentro de un array son del mismo tipo.

Crear arrays

Puedes crear un array de una dimensión utilizando simplemente la función array() la cual recibe una lista de valores como argumento.

a = np.array([1, 2, 3])
print(a)
[1 2 3]

Otra forma de implementar un array es utilizando np.arange(). Esta función devolverá un arreglo de valores espaciados uniformemente dentro de un intervalo dado.

b = np.arange(3)
print(b)
[0 1 2]

Otro ejemplo usando las características de np.arange():

# Crear un array que comienza por 1, termina en 20, incrementando valores por 3.
c = np.arange(1, 20, 3)
print(c)
[ 1  4  7 10 13 16 19]

La función np.linspace() puede generar valores espaciados uniformemente en un intervalo. En este ejemplo el intervalo es entre 0 y 100 y el número de elementos en el array, 5.

lin_spaced_arr = np.linspace(0, 100, 5)
print(lin_spaced_arr)
[  0.  25.  50.  75. 100.]

Por defecto los valores devueltos por np.linspace son de punto flotante (np.float64). Puedes especificar fácilmente tu tipo de datos usando dtype.

c_int = np.arange(1, 20, 3, dtype=int)
print(c_int)
[ 1  4  7 10 13 16 19]

Más arrays de NumPy

Puedes crear arreglos fácilmente con funciones integradas tales como: 

  • np.ones(shape) – Devuelve un nuevo arreglo estableciendo los valores en uno.
  • np.zeros(shape) – Devuelve un nuevo arreglo estableciendo los valores en cero.
  • np.empty(shape) – Devuelve un nuevo arreglo sin inicializar.
  • np.random.rand(shape) – Devuelve un nuevo arreglo con valores randoms. 

Arrays multidimensionales

Con NumPy también puedes crear arreglos con más de una dimensión. En los ejemplos anteriores, trabajaste con arreglos 1-D, donde puedes acceder a sus elementos utilizando un único índice. Un arreglo multidimensional tiene más de una columna. Piensa en un arreglo multidimensional como una hoja de Excel donde cada fila/columna representa una dimensión.

# Crear array de dos dimensiones (2-D)
two_dim_arr = np.array([[1,2,3], [4,5,6]])
print(two_dim_arr)
[[1 2 3]
 [4 5 6]]

Una forma alternativa de crear un arreglo multidimensional es cambiando la forma del arreglo 1-D inicial. Utilizando np.reshape() puedes reorganizar los elementos del arreglo anterior en una nueva forma.

# 1-D array 
one_dim_arr = np.array([1, 2, 3, 4, 5, 6])

# Multidimensional array using reshape()
multi_dim_arr = np.reshape(
                one_dim_arr, # the array to be reshaped
               (2,3) # dimensions of the new array
              )
# Print the new 2-D array with two rows and three columns
print(multi_dim_arr)
[[1 2 3]
 [4 5 6]]

Encontrar el tamaño, la forma y la dimensión.

Necesitarás saber cómo encontrar el tamaño, la dimensión y la forma de un array. Todos estos son atributos de un ndarray y se puede acceder a ellos de la siguiente manera:

  • ndarray.ndim – Almacena el número de dimensiones del arreglo.
  • ndarray.shape – Almacena la forma del arreglo. Cada número en la tupla denota la longitud de cada dimensión correspondiente.
  • ndarray.size – Almacena el número de elementos en el arreglo.

Operaciones matemáticas con arrays

En esta sección, verás que NumPy te permite realizar rápidamente sumas, restas, multiplicaciones y divisiones elemento por elemento, tanto para arreglos 1-D como multidimensionales.

Las operaciones se realizan utilizando el símbolo matemático correspondiente: ‘+’, ‘-‘ y ‘*’. Recuerda que la suma de listas de Python funciona de manera completamente diferente, ya que concatena las listas creando una lista más larga; además, la resta y la multiplicación de listas de Python no funcionan.

arr_1 = np.array([2, 4, 6])
arr_2 = np.array([1, 3, 5])

# Adding two 1-D arrays
addition = arr_1 + arr_2
print(addition)

# Subtracting two 1-D arrays
subtraction = arr_1 - arr_2
print(subtraction)

# Multiplying two 1-D arrays elementwise
multiplication = arr_1 * arr_2
print(multiplication)
[ 3  7 11]
[1 1 1]
[ 2 12 30]

Multiplicación de un vector por un escalar (broadcasting)

Supón que necesitas convertir millas a kilómetros. Para hacerlo, puedes usar las funciones de arreglos de NumPy que has aprendido hasta ahora. Puedes lograrlo realizando una operación entre un arreglo (millas) y un solo número (la tasa de conversión, que es un escalar). Dado que 1 milla = 1.6 km, NumPy calcula cada multiplicación dentro de cada celda. 

Este concepto se llama broadcasting (transmisión), el cual te permite realizar operaciones específicamente en arreglos de diferentes formas.

vector = np.array([1, 2])
vector * 1.6
array([1.6, 3.2])

Indexación y segmentación (slicing)

La indexación es muy útil ya que te permite seleccionar elementos específicos de un array. También te permite seleccionar filas, columnas o planos completos, como verás en futuras tareas para arreglos multidimensionales. 

Indexación

Seleccionemos elementos específicos de los arreglos dados. 

# Seleccionar el tercer elemento del array.
a = ([1, 2, 3, 4, 5])
print(a[2])

# Seleccionar el primer elemento.
print(a[0])
3
1

Para arrays multidimensionales de forma \(n\), para indexar un elemento específico, debes ingresar \(n\) índices, uno por cada dimensión.

# Indexing on a 2-D array
two_dim = np.array(([1, 2, 3],
          [4, 5, 6], 
          [7, 8, 9]))

# Select element number 8 from the 2-D array using indices i, j.
print(two_dim[2][1])
8

Segmentación (Slicing)

La segmentación te devuelve una sublista de los elementos que especifiques del array. La notación de segmentación define un valor de inicio y uno de fin, y copia la lista desde el inicio hasta el final, pero sin incluir este último (el final es exclusivo).

La sintaxis es:

arreglo[inicio:fin:paso]

Si no se pasa ningún valor al inicio, se asume que inicio = 0; si no se pasa ningún valor para el fin, se asume que fin = longitud del arreglo y si no se pasa ningún valor para el paso, se asume que paso = 1.

# Slice the array a to get the array [2,3,4]
sliced_arr = a[1:4]
print(sliced_arr)
[2, 3, 4]

Apilamiento (Stacking)

Finalmente, el apilamiento es una característica de NumPy que permite una mayor personalización de los arrays. Consiste en unir dos o más arreglos, ya sea de forma horizontal o vertical, lo que significa que se realiza a lo largo de un nuevo eje.

  • np.vstack() – apila verticalmente.
  • np.hstack() – apila horizontalmente.
  • np.hsplit() – divide un arreglo en varios arreglos más pequeños.
a1 = np.array([[1,1], 
               [2,2]])
a2 = np.array([[3,3],
              [4,4]])
print(f'a1:\n{a1}')
print(f'a2:\n{a2}')
a1:
[[1 1]
 [2 2]]
a2:
[[3 3]
 [4 4]]

Apilar los arrays verticalmente

# Stack the arrays vertically
vert_stack = np.vstack((a1, a2))
print(vert_stack)
[[1 1]
 [2 2]
 [3 3]
 [4 4]]

Apilar horizontalmente:

# Stack the arrays horizontally
horz_stack = np.hstack((a1, a2))
print(horz_stack)
[[1 1 3 3]
 [2 2 4 4]]