Capítulo 1. Trabajar con vectores, matricesy matrices en NumPy

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

1.0 Introducción

NumPy es una herramienta fundacional de de la pila de aprendizaje automático de Python. NumPy permite realizar operaciones eficientes con las estructuras de datos que se utilizan a menudo en el aprendizaje automático: vectores, matrices y tensores. Aunque NumPy no es el tema central de este libro, aparecerá con frecuencia en los capítulos siguientes. Este capítulo cubre las operaciones NumPy más comunes con las que probablemente nos encontremos al trabajar en flujos de trabajo de aprendizaje automático.

1.1 Crear un vector

Problema

En tienes que crear un vector.

Solución

Utiliza NumPy para crear una matriz unidimensional:

# Load library
import numpy as np

# Create a vector as a row
vector_row = np.array([1, 2, 3])

# Create a vector as a column
vector_column = np.array([[1],
                          [2],
                          [3]])

Debate

La principal estructura de datos de NumPy es la matriz multidimensional. Un vector no es más que una matriz con una sola dimensión. Para crear un vector, simplemente creamos una matriz unidimensional. Al igual que los vectores, estas matrices pueden representarse horizontalmente (es decir, filas) o verticalmente (es decir, columnas).

1.2 Crear una matriz

Problema

Tienes que crear una matriz .

Solución

Utiliza NumPy para crear una matriz bidimensional :

# Load library
import numpy as np

# Create a matrix
matrix = np.array([[1, 2],
                   [1, 2],
                   [1, 2]])

Debate

Para crear una matriz podemos utilizar una matriz bidimensional NumPy. En nuestra solución, la matriz contiene tres filas y dos columnas (una columna de 1s y otra de 2s).

De hecho, NumPy tiene una estructura de datos matricial específica:

matrix_object = np.mat([[1, 2],
                        [1, 2],
                        [1, 2]])
matrix([[1, 2],
        [1, 2],
        [1, 2]])

Sin embargo, la estructura de datos matrices no es recomendable por dos razones. En primer lugar, las matrices son la estructura de datos estándar de facto de NumPy. En segundo lugar, la inmensa mayoría de las operaciones de NumPy devuelven matrices, no objetos matriz.

1.3 Crear una matriz dispersa

Problema

Dados los datos de con muy pocos valores distintos de cero, quieres representarlos eficientemente.

Solución

Crea una matriz dispersa:

# Load libraries
import numpy as np
from scipy import sparse

# Create a matrix
matrix = np.array([[0, 0],
                   [0, 1],
                   [3, 0]])

# Create compressed sparse row (CSR) matrix
matrix_sparse = sparse.csr_matrix(matrix)

Debate

Una situación frecuente en el aprendizaje automático es tener una enorme cantidad de datos; sin embargo, la mayoría de los elementos de los datos son ceros. Por ejemplo, imagina una matriz en la que las columnas sean todas las películas de Netflix, las filas sean todos los usuarios de Netflix y los valores sean cuántas veces ha visto un usuario esa película en concreto. ¡Esta matriz tendría decenas de miles de columnas y millones de filas! Sin embargo, como la mayoría de los usuarios no ven la mayoría de las películas, la inmensa mayoría de los elementos serían cero.

Una matriz dispersa es una matriz en la que la mayoría de los elementos son 0. Las matrices dispersas sólo almacenan elementos distintos de cero y asumen que todos los demás valores serán cero, lo que supone un importante ahorro computacional. En nuestra solución, creamos una matriz NumPy con dos valores distintos de cero, y luego la convertimos en una matriz dispersa. Si visualizamos la matriz dispersa, podemos ver que sólo se almacenan los valores distintos de cero:

# View sparse matrix
print(matrix_sparse)
  (1, 1)    1
  (2, 0)    3

Existen varios tipos de matrices dispersas. Sin embargo, en las matrices comprimidas de filas dispersas (CSR), (1, 1) y (2, 0) representan los índices (indexados a cero) de los valores distintos de cero 1 y 3, respectivamente. Por ejemplo, el elemento 1 está en la segunda fila y la segunda columna. Podemos ver la ventaja de las matrices dispersas si creamos una matriz mucho mayor con muchos más elementos cero y luego comparamos esta matriz mayor con nuestra matriz dispersa original:

# Create larger matrix
matrix_large = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                         [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
                         [3, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

# Create compressed sparse row (CSR) matrix
matrix_large_sparse = sparse.csr_matrix(matrix_large)
# View original sparse matrix
print(matrix_sparse)
  (1, 1)    1
  (2, 0)    3
# View larger sparse matrix
print(matrix_large_sparse)
  (1, 1)    1
  (2, 0)    3

Como podemos ver, a pesar de que añadimos muchos más elementos cero en la matriz más grande, su representación dispersa es exactamente la misma que nuestra matriz dispersa original. Es decir, la adición de elementos cero no cambió el tamaño de la matriz dispersa.

Como ya se ha dicho, hay muchos tipos distintos de matrices dispersas, como la columna dispersa comprimida, la lista de listas y el diccionario de claves. Aunque una explicación de los distintos tipos y sus implicaciones queda fuera del alcance de este libro, merece la pena señalar que, aunque no existe el "mejor" tipo de matriz dispersa, sí hay diferencias significativas entre ellas, y debemos ser conscientes de por qué elegimos un tipo en lugar de otro.

1.4 Preasignar matrices NumPy

Problema

Necesitas preasignar matrices de un tamaño determinado con algún valor.

Solución

NumPy tiene funciones para generar vectores y matrices de cualquier tamaño utilizando 0s, 1s o valores de tu elección:

# Load library
import numpy as np

# Generate a vector of shape (1,5) containing all zeros
vector = np.zeros(shape=5)

# View the matrix
print(vector)
array([0., 0., 0., 0., 0.])
# Generate a matrix of shape (3,3) containing all ones
matrix = np.full(shape=(3,3), fill_value=1)

# View the vector
print(matrix)
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])

Debate

Generar matrices preasignadas con datos es útil para varios fines, como hacer que el código tenga un mayor rendimiento o utilizar datos sintéticos para probar algoritmos. En muchos lenguajes de programación, preasignar una matriz de valores por defecto (como 0s) se considera una práctica habitual.

1.5 Seleccionar elementos

Problema

Necesitas seleccionar uno o varios elementos de un vector o matriz.

Solución

Las matrices NumPy facilitan la selección de elementos en vectores o matrices:

# Load library
import numpy as np

# Create row vector
vector = np.array([1, 2, 3, 4, 5, 6])

# Create matrix
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# Select third element of vector
vector[2]
3
# Select second row, second column
matrix[1,1]
5

Debate

Como la mayoría de las cosas en Python, las matrices de NumPy tienen índice cero, lo que significa que el índice del primer elemento es 0, no 1. Con esa salvedad, NumPy ofrece una amplia variedad de métodos para seleccionar (es decir, indexar y trocear) elementos o grupos de elementos en matrices:

# Select all elements of a vector
vector[:]
array([1, 2, 3, 4, 5, 6])
# Select everything up to and including the third element
vector[:3]
array([1, 2, 3])
# Select everything after the third element
vector[3:]
array([4, 5, 6])
# Select the last element
vector[-1]
6
# Reverse the vector
vector[::-1]
array([6, 5, 4, 3, 2, 1])
# Select the first two rows and all columns of a matrix
matrix[:2,:]
array([[1, 2, 3],
       [4, 5, 6]])
# Select all rows and the second column
matrix[:,1:2]
array([[2],
       [5],
       [8]])

1.6 Describir una matriz

Problema

Quieres describir la forma, el tamaño y las dimensiones de una matriz.

Solución

Utiliza los atributos shape, size, y ndim de un objeto NumPy:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 2, 3, 4],
                   [5, 6, 7, 8],
                   [9, 10, 11, 12]])

# View number of rows and columns
matrix.shape
(3, 4)
# View number of elements (rows * columns)
matrix.size
12
# View number of dimensions
matrix.ndim
2

Debate

Esto puede parecer básico (y lo es); sin embargo, una y otra vez será valioso comprobar la forma y el tamaño de una matriz, tanto para cálculos posteriores como simplemente como comprobación visceral tras una operación.

1.7 Aplicar funciones sobre cada elemento

Problema

Quieres aplicar alguna función a todos los elementos de una matriz.

Solución

Utiliza el método NumPy vectorize:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# Create function that adds 100 to something
add_100 = lambda i: i + 100

# Create vectorized function
vectorized_add_100 = np.vectorize(add_100)

# Apply function to all elements in matrix
vectorized_add_100(matrix)
array([[101, 102, 103],
       [104, 105, 106],
       [107, 108, 109]])

Debate

El método vectorize de NumPy convierte una función en una función que puede aplicarse a todos los elementos de una matriz o porción de una matriz. Cabe señalar que vectorize es esencialmente un bucle for sobre los elementos y no aumenta el rendimiento. Además, las matrices de NumPy nos permiten realizar operaciones entre matrices aunque sus dimensiones no sean las mismas (un proceso llamado difusión). Por ejemplo, podemos crear una versión mucho más sencilla de nuestra solución utilizando la difusión:

# Add 100 to all elements
matrix + 100
array([[101, 102, 103],
       [104, 105, 106],
       [107, 108, 109]])

La difusión no funciona para todas las formas y situaciones, pero es una forma habitual de aplicar operaciones sencillas sobre todos los elementos de una matriz NumPy.

1.8 Encontrar los valores máximo y mínimo

Problema

Tienes que encontrar el valor máximo o mínimo de en una matriz.

Solución

Utiliza los métodos max y min de NumPy:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# Return maximum element
np.max(matrix)
9
# Return minimum element
np.min(matrix)
1

Debate

A menudo queremos conocer el valor máximo y mínimo de una matriz o subconjunto de una matriz. Esto se puede conseguir con los métodos max y min. Utilizando el parámetro axis, también podemos aplicar la operación a lo largo de un eje determinado:

# Find maximum element in each column
np.max(matrix, axis=0)
array([7, 8, 9])
# Find maximum element in each row
np.max(matrix, axis=1)
array([3, 6, 9])

1.9 Calcular la media, la varianza y la desviación típica

Problema

Quieres calcular algunos estadísticos descriptivos sobre una matriz.

Solución

Utiliza NumPy's mean, var, y std:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# Return mean
np.mean(matrix)
5.0
# Return variance
np.var(matrix)
6.666666666666667
# Return standard deviation
np.std(matrix)
2.5819888974716112

Debate

Al igual que con max y min, podemos obtener fácilmente estadísticas descriptivas de toda la matriz o hacer cálculos a lo largo de un solo eje:

# Find the mean value in each column
np.mean(matrix, axis=0)
array([ 4.,  5.,  6.])

1.10 Remodelar matrices

Problema

Quieres cambiar la forma (número de filas y columnas) de una matriz sin cambiar los valores de los elementos.

Solución

Utiliza la página reshape de NumPy:

# Load library
import numpy as np

# Create 4x3 matrix
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9],
                   [10, 11, 12]])

# Reshape matrix into 2x6 matrix
matrix.reshape(2, 6)
array([[ 1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12]])

Debate

reshape nos permite reestructurar una matriz de modo que mantengamos los mismos datos pero los organicemos con un número diferente de filas y columnas. El único requisito es que la forma de la matriz original y la nueva contengan el mismo número de elementos (es decir, tengan el mismo tamaño). Podemos ver el tamaño de una matriz utilizando size:

matrix.size
12

Un argumento útil en reshape es -1, que significa efectivamente "tantas como sean necesarias", por lo que reshape(1, -1) significa una fila y tantas columnas como sean necesarias:

matrix.reshape(1, -1)
array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]])

Por último, si proporcionamos un número entero, reshape devolverá una matriz unidimensional de esa longitud:

matrix.reshape(12)
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])

1.11 Transponer un vector o una matriz

Problema

Necesitas transponer un vector o una matriz.

Solución

Utiliza el método T:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# Transpose matrix
matrix.T
array([[1, 4, 7],
       [2, 5, 8],
       [3, 6, 9]])

Debate

La transposición es una operación habitual en álgebra lineal en la que se intercambian los índices de columna y fila de cada elemento. Un matiz que suele pasarse por alto fuera de una clase de álgebra lineal es que, técnicamente, un vector no puede transponerse porque no es más que una colección de valores:

# Transpose vector
np.array([1, 2, 3, 4, 5, 6]).T
array([1, 2, 3, 4, 5, 6])

Sin embargo, es habitual referirse a la transposición de un vector como la conversión de un vector fila en un vector columna (fíjate en el segundo par de corchetes) o viceversa:

# Transpose row vector
np.array([[1, 2, 3, 4, 5, 6]]).T
array([[1],
       [2],
       [3],
       [4],
       [5],
       [6]])

1.12 Aplanar una matriz

Problema

Necesitas transformar una matriz en una matriz unidimensional.

Solución

Utiliza el método flatten:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

# Flatten matrix
matrix.flatten()
array([1, 2, 3, 4, 5, 6, 7, 8, 9])

Debate

flatten es un método sencillo para transformar una matriz en una matriz unidimensional. Alternativamente, podemos utilizar reshape para crear un vector de filas:

matrix.reshape(1, -1)
array([[1, 2, 3, 4, 5, 6, 7, 8, 9]])

Otra forma habitual de aplanar matrices es el método ravel. A diferencia de flatten, que devuelve una copia de la matriz original, ravel opera sobre el propio objeto original y, por tanto, es ligeramente más rápido. También nos permite aplanar listas de matrices, cosa que no podemos hacer con el método flatten. Esta operación es útil para aplanar matrices muy grandes y acelerar el código:

# Create one matrix
matrix_a = np.array([[1, 2],
                     [3, 4]])

# Create a second matrix
matrix_b = np.array([[5, 6],
                     [7, 8]])

# Create a list of matrices
matrix_list = [matrix_a, matrix_b]

# Flatten the entire list of matrices
np.ravel(matrix_list)
array([1, 2, 3, 4, 5, 6, 7, 8])

1.13 Hallar el rango de una matriz

Problema

Necesitas conocer el rango de una matriz.

Solución

Utiliza el método de álgebra lineal de NumPy matrix_rank :

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 1, 1],
                   [1, 1, 10],
                   [1, 1, 15]])

# Return matrix rank
np.linalg.matrix_rank(matrix)
2

Debate

El rango de una matriz son las dimensiones del espacio vectorial que abarcan sus columnas o filas. Encontrar el rango de una matriz es fácil en NumPy gracias a matrix_rank.

1.14 Obtener la diagonal de una matriz

Problema

Necesitas obtener los elementos diagonales de una matriz.

Solución

Utiliza la página diagonal de NumPy:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 2, 3],
                   [2, 4, 6],
                   [3, 8, 9]])

# Return diagonal elements
matrix.diagonal()
array([1, 4, 9])

Debate

NumPy facilita la obtención de los elementos diagonales de una matriz condiagonal. También es posible obtener una diagonal fuera de la diagonal principal utilizando el parámetro offset:

# Return diagonal one above the main diagonal
matrix.diagonal(offset=1)
array([2, 6])
# Return diagonal one below the main diagonal
matrix.diagonal(offset=-1)
array([2, 8])

1.15 Calcular la traza de una matriz

Problema

Necesitas calcular la traza de una matriz.

Solución

Utiliza trace:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 2, 3],
                   [2, 4, 6],
                   [3, 8, 9]])

# Return trace
matrix.trace()
14

Debate

La traza de una matriz es la suma de los elementos diagonales y se utiliza a menudo en métodos de aprendizaje automático. Dada una matriz multidimensional NumPy, podemos calcular la traza utilizando trace. Alternativamente, podemos devolver la diagonal de una matriz y calcular su suma:

# Return diagonal and sum elements
sum(matrix.diagonal())
14

1.16 Calcular productos de puntos

Problema

Necesitas calcular el producto punto de dos vectores.

Solución

Utiliza Función dot de NumPy:

# Load library
import numpy as np

# Create two vectors
vector_a = np.array([1,2,3])
vector_b = np.array([4,5,6])

# Calculate dot product
np.dot(vector_a, vector_b)
32

Debate

El producto punto de dos vectores, a y bse define como

i=1 n a i b i

donde ai es la ielemento del vectoray bi es el elemento ielemento del vectorb. Podemos utilizar la función dot de NumPy para calcular el producto punto. Como alternativa, en Python 3.5+ podemos utilizar el nuevo operador @:

# Calculate dot product
vector_a @ vector_b
32

1.17 Sumar y restar matrices

Problema

Quieres sumar o restar dos matrices.

Solución

Utiliza NumPy's add y subtract:

# Load library
import numpy as np

# Create matrix
matrix_a = np.array([[1, 1, 1],
                     [1, 1, 1],
                     [1, 1, 2]])

# Create matrix
matrix_b = np.array([[1, 3, 1],
                     [1, 3, 1],
                     [1, 3, 8]])

# Add two matrices
np.add(matrix_a, matrix_b)
array([[ 2,  4,  2],
       [ 2,  4,  2],
       [ 2,  4, 10]])
# Subtract two matrices
np.subtract(matrix_a, matrix_b)
array([[ 0, -2,  0],
       [ 0, -2,  0],
       [ 0, -2, -6]])

Debate

Alternativamente, podemos utilizar simplemente los operadores + y :

# Add two matrices
matrix_a + matrix_b
array([[ 2,  4,  2],
       [ 2,  4,  2],
       [ 2,  4, 10]])

1.18 Multiplicar matrices

Problema

Quieres multiplicar dos matrices.

Solución

Utiliza la página dot de NumPy:

# Load library
import numpy as np

# Create matrix
matrix_a = np.array([[1, 1],
                     [1, 2]])

# Create matrix
matrix_b = np.array([[1, 3],
                     [1, 2]])

# Multiply two matrices
np.dot(matrix_a, matrix_b)
array([[2, 5],
       [3, 7]])

Debate

Como alternativa, en Python 3.5+ podemos utilizar el operador @:

# Multiply two matrices
matrix_a @ matrix_b
array([[2, 5],
       [3, 7]])

Si queremos hacer una multiplicación por elementos, podemos utilizar el operador *:

# Multiply two matrices element-wise
matrix_a * matrix_b
array([[1, 3],
       [1, 4]])

1.19 Invertir una matriz

Problema

Quieres calcular la inversa de una matriz cuadrada.

Solución

Utiliza el método de álgebra lineal de NumPy inv:

# Load library
import numpy as np

# Create matrix
matrix = np.array([[1, 4],
                   [2, 5]])

# Calculate inverse of matrix
np.linalg.inv(matrix)
array([[-1.66666667,  1.33333333],
       [ 0.66666667, -0.33333333]])

Debate

La inversa de una matriz cuadrada, A, es una segunda matriz, A-1, tal que:

A A -1 = I

donde I es la matriz de identidad . En NumPy podemos utilizar linalg.inv para calcular A-1 si existe. Para ver esto en acción, podemos multiplicar una matriz por su inversa, y el resultado es la matriz identidad:

# Multiply matrix and its inverse
matrix @ np.linalg.inv(matrix)
array([[ 1.,  0.],
       [ 0.,  1.]])

Ver también

1.20 Generar valores aleatorios

Problema

Quieres generar valores pseudoaleatorios.

Solución

Utiliza NumPy's random:

# Load library
import numpy as np

# Set seed
np.random.seed(0)

# Generate three random floats between 0.0 and 1.0
np.random.random(3)
array([ 0.5488135 ,  0.71518937,  0.60276338])

Debate

NumPy ofrece una gran variedad de medios para generar números aleatorios, muchos más de los que se pueden abarcar aquí. En nuestra solución generamos flotantes; sin embargo, también es habitual generar enteros:

# Generate three random integers between 0 and 10
np.random.randint(0, 11, 3)
array([3, 7, 9])

Alternativamente, podemos generar números extrayéndolos de una distribución (ten en cuenta que esto no es técnicamente aleatorio):

# Draw three numbers from a normal distribution with mean 0.0
# and standard deviation of 1.0
np.random.normal(0.0, 1.0, 3)
array([-1.42232584,  1.52006949, -0.29139398])
# Draw three numbers from a logistic distribution with mean 0.0 and scale of 1.0
np.random.logistic(0.0, 1.0, 3)
array([-0.98118713, -0.08939902,  1.46416405])
# Draw three numbers greater than or equal to 1.0 and less than 2.0
np.random.uniform(1.0, 2.0, 3)
array([ 1.47997717,  1.3927848 ,  1.83607876])

Por último, a veces puede ser útil devolver los mismos números aleatorios varias veces para obtener resultados predecibles y repetibles. Podemos hacerlo fijando la "semilla" (un número entero) del generador pseudoaleatorio. Los procesos aleatorios con la misma semilla producirán siempre el mismo resultado. Utilizaremos semillas a lo largo de este libro para que el código que veas en el libro y el que ejecutes en tu ordenador produzcan los mismos resultados.

Get Recetario de Aprendizaje Automático con Python, 2ª Edición now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.