NumPy práctico (VI): Álgebra Lineal

El álgebra lineal tiene muchas aplicaciones útiles en ciencia e ingeniería. Si estás haciendo computación científica, es muy probable que tarde o temprano necesites usar álgebra lineal para resolver problemas.

Si tu álgebra lineal está un poco oxidada, puedes echar un vistazo al curso de álgebra lineal de Khan Academy, es gratis y hace un gran trabajo explicando los conceptos:

NumPy tiene una serie de utilidades de álgebra lineal muy útiles que puedes aprovechar, y en este artículo, echaremos un vistazo a algunas de ellas.

Antes de empezar, crearemos un par de matrices y vectores para usar en los ejemplos.

import numpy as np

# Creemos 2 vectores con valores de -10 a 10, 4 entradas cada uno
vector_a = np.random.randint(-10, 10, 4)
vector_b = np.random.randint(-10, 10, 4)

print(vector_a)
print(vector_b)
[ 8  9 -3 -7]
[ 1  8 -4  8]
# También creemos dos matrices 3x3 con valores aleatorios
matrix_a = np.random.randint(-10, 10, 9).reshape(3, 3)
matrix_b = np.random.randint(-10, 10, 9).reshape(3, 3)

print(matrix_a)
print(matrix_b)
[[ -3   5   8]
 [  6  -8  -5]
 [  2   5 -10]]
[[-8 -2 -1]
 [-1  7 -6]
 [-3  8  0]]

Ok, empecemos, primero jugaremos un poco con los dos vectores y luego pasaremos a las matrices.

# Puedes calcular el producto punto de dos vectores usando la función .dot (o la función .inner)
result = np.dot(vector_a, vector_b)
print(result)
36
# También puedes calcular la norma de un vector
result = np.linalg.norm(vector_a)
print(result)
14.247806848775006
# También puedes calcular el producto exterior de dos vectores
result = np.outer(vector_a, vector_b)
print(result)
[[  8  64 -32  64]
 [  9  72 -36  72]
 [ -3 -24  12 -24]
 [ -7 -56  28 -56]]

Matrices

# Puedes calcular el producto interior de dos matrices
result = np.inner(matrix_a, matrix_b)
print(result)
[[  6 -10  49]
 [-27 -32 -82]
 [-16  93  34]]
# Y también el producto exterior
result = np.outer(matrix_a, matrix_b)
print(result)
[[ 24   6   3   3 -21  18   9 -24   0]
 [-40 -10  -5  -5  35 -30 -15  40   0]
 [-64 -16  -8  -8  56 -48 -24  64   0]
 [-48 -12  -6  -6  42 -36 -18  48   0]
 [ 64  16   8   8 -56  48  24 -64   0]
 [ 40  10   5   5 -35  30  15 -40   0]
 [-16  -4  -2  -2  14 -12  -6  16   0]
 [-40 -10  -5  -5  35 -30 -15  40   0]
 [ 80  20  10  10 -70  60  30 -80   0]]
# Puedes realizar producto matricial típico usando matmul
result = np.matmul(matrix_a, matrix_b)
print(result)
[[  -5  105  -27]
 [ -25 -108   42]
 [   9  -49  -32]]
# Como con vectores, puedes calcular la norma de la matriz usando .norm
result = np.linalg.norm(matrix_a)
print(result)
18.76166303929372
# Puedes elevar una matriz a la potencia n con linalg.matrix_power
result = np.linalg.matrix_power(matrix_a, 3)
print(result)
[[ -513  -250  1805]
 [  918  -242 -2333]
 [ -310  1115  -478]]
# Si tu matriz es cuadrada, puedes calcular los valores propios y vectores propios derechos con linalg.eig(a)
# Hay una versión llamada eigvals que opera sobre una matriz general
w,v = np.linalg.eig(matrix_a)
print(w)
print(v)
[  1.92755345+0.j         -11.46377672+5.07699792j
 -11.46377672-5.07699792j]
[[-0.87532836+0.j         -0.46537447+0.18724549j -0.46537447-0.18724549j]
 [-0.37577112+0.j          0.70724201+0.j          0.70724201-0.j        ]
 [-0.30429646+0.j         -0.06850368-0.49343866j -0.06850368+0.49343866j]]
# Puedes calcular el determinante usando .linalg.det
result = np.linalg.det(matrix_a)
print(result)
303.00000000000017
# Y calcular el rango de la matriz usando linalg.matrix_rank
result = np.linalg.matrix_rank(matrix_a)
print(result)
3
# También puedes calcular la traza (suma a lo largo de las diagonales)
result = np.trace(matrix_a)
print(result)
-21
# Y calcular la inversa de la matriz usando linalg.inv
matrix_to_invert = np.array([[1,0,5],[2,1,6],[3,4,0]])
inverse = np.linalg.inv(matrix_to_invert)
print(inverse)
[[-24.  20.  -5.]
 [ 18. -15.   4.]
 [  5.  -4.   1.]]
# Veamos si es correcto verificando que A*inv(A) = I
result = np.matmul(matrix_to_invert, inverse).astype(np.int64)
print(result)
[[1 0 0]
 [0 1 0]
 [0 0 1]]
# También hay diferentes métodos para resolver ecuaciones matriciales, el más simple es linalg.solve(a,b)
# Que resuelve la ecuación ax=b

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

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

x = np.linalg.solve(a, b)

print(x)
[-5.  4.  0.]
# Podemos verificar que el resultado está bien multiplicando a*x, y verificando que el resultado igual b
result = np.matmul(a,x)
print(result)
[2. 3. 1.]
# Finalmente, puedes acceder a la transpuesta de tanto matrices como vectores con el atributo .T
print(vector_a.T)
print(matrix_a.T)
[ 8  9 -3 -7]
[[ -3   6   2]
 [  5  -8   5]
 [  8  -5 -10]]

Creo que eso es todo por ahora

Acabamos de usar algunas de las funciones más comunes del módulo linalg, pero hay muchas otras cosas geniales que puedes hacer con ellas. Te recomiendo que eches un vistazo a las descomposiciones disponibles, ya que podrían ser útiles en el futuro.

Si quieres una lista completa de las cosas que puedes hacer con el módulo linalg, revisa este enlace:

Y con esto, concluimos NumPy práctico. Esta serie de ninguna manera fue un enfoque comprehensivo de NumPy, pero espero que te haya dado una idea básica de lo que se puede hacer con él. Solo exploramos algo de la funcionalidad más común, pero hay muchas otras cosas útiles ya implementadas.

Entonces, ¿qué estás esperando? Ve y escribe soluciones a tus problemas, un poco de documentación y mucha curiosidad pueden ayudarte a lograr cualquier cosa.

¡Gracias por leer!

Qué hacer después

Juan Luis Orozco Villalobos

¡Hola! Soy Juan, ingeniero de software y consultor en Budapest. Me especializo en computación en la nube e IA, y me encanta ayudar a otros a aprender sobre tecnología e ingeniería