Cálculo matricial com NumPy
Aviso: Este post foi traduzido para o português usando um modelo de tradução automática. Por favor, me avise se encontrar algum erro.
1. Resumo
Vamos ver uma pequena introdução à biblioteca de cálculo matricial NumPy
. Esta biblioteca está desenhada para todo tipo de cálculo matricial, por isso vamos nos concentrar apenas na parte que será útil para entender os cálculos dentro das redes neurais, mas deixaremos de fora coisas interessantes como o uso da biblioteca para a álgebra linear.
2. O que é NumPy?
NumPy é uma biblioteca de Python projetada para realizar cálculo matricial. O cálculo matricial é algo que se utiliza muito em ciência em geral e em data science em particular, por isso é necessário ter uma biblioteca que faça isso muito bem.
Seu nome significa Numerical Python
Seu objeto principal é o ndarray
, que encapsula matrizes de dimensão n
de tipos de dados homogêneos, ao contrário das listas de Python, que podem conter dados de diferentes tipos.
NumPy tem o objetivo de realizar o cálculo matricial muito mais rápido do que com as listas de Python, mas como isso é possível?
- NumPy utiliza código compilado, enquanto Python utiliza código interpretado. A diferença é que Python no momento da execução tem que interpretar, compilar e executar o código, enquanto o NumPy já está compilado, por isso é executado mais rápido* Os
ndarray
s têm um tamanho fixo, ao contrário das listas de Python que são dinâmicas. Se no NumPy quiser modificar o tamanho de uma matriz, será criada uma nova e a antiga será eliminada* Todos os elementos dosndarray
s são do mesmo tipo de dado, ao contrário das listas do Python que podem ter elementos de diferentes tipos* Parte do código do NumPy está escrito em C/C++ (muito mais rápido que Python)* Os dados das matrizes são armazenados em memória de maneira contínua, ao contrário das listas de Python, o que faz com que seja muito mais rápido manipulá-los
NumPy oferece a facilidade de usar código simples de escrever e de ler, mas que está escrito e pré-compilado em C, o que o torna muito mais rápido.
Suponhamos que queremos multiplicar dois vetores, isso seria feito em C da seguinte maneira:
português
markdown
for (i = 0; i < rows; i++):
for (j = 0; j < columns; j++): {
``````markdown
c[i][j] = a[i][j]*b[i][j];
``````markdown
```}
NumPy oferece a possibilidade de executar este código por baixo, mas muito mais fácil de escrever e entender através
```python
``````python
c = a * b
NumPy oferece código vetorizado, que significa não ter que escrever loops, mas que no entanto estão sendo executados por baixo em código C otimizado e pré-compilado. Isso tem as seguintes vantagens:
- O código é mais fácil de escrever e ler* Ao necessitar de menos linhas de código, há menor probabilidade de introduzir erros* O código se parece mais com a notação matemática
2.1. NumPy como np
Geralmente, ao importar o NumPy, costuma-se importar com o alias de np
import numpy as npprint(np.__version__)
1.18.1
3. Velocidade do NumPy
Como foi explicado, o NumPy realiza o cálculo muito mais rápido do que as listas de Python, vejamos um exemplo em que é realizado o produto escalar de duas matrizes, através de listas de Python e através de ndarray
s
from time import time# Dimensión de las matricesdim = 1000shape = (dim, dim)# Se crean dos ndarrays de NumPy de dimensión dim x dimndarray_a = np.ones(shape=shape)ndarray_b = np.ones(shape=shape)# Se crean dos listas de Python de dimensión dim x dim a partir de los ndarrayslist_a = list(ndarray_a)list_b = list(ndarray_b)# Se crean el ndarray y la lista de Python donde se guardarán los resultadosndarray_c = np.empty(shape=shape)list_c = list(ndarray_c)# Producto escalar de dos listas de pythont0 = time()for fila in range(dim):for columna in range(dim):list_c[fila][columna] = list_a[fila][columna] * list_b[fila][columna]t = time()t_listas = t-t0print(f"Tiempo para realizar el producto escalar de dos listas de Python de dimensiones {dim}x{dim}: {t_listas:.4f} ms")# Producto escalar de dos ndarrays de NumPyt0 = time()ndarray_c = ndarray_a * ndarray_bt = time()t_ndarrays = t-t0print(f"Tiempo para realizar el producto escalar de dos ndarrays de NumPy de dimensiones {dim}x{dim}: {t_ndarrays:.4f} ms")# Comparación de tiemposprint(f"\nHacer el cálculo con listas de Python tarda {t_listas/t_ndarrays:.2f} veces más rápido que con ndarrays de NumPy")
Tiempo para realizar el producto escalar de dos listas de Python de dimensiones 1000x1000: 0.5234 msTiempo para realizar el producto escalar de dos ndarrays de NumPy de dimensiones 1000x1000: 0.0017 msHacer el cálculo con listas de Python tarda 316.66 veces más rápido que con ndarrays de NumPy
4. Matrizes no NumPy
Em NumPy uma matriz é um objeto ndarray
arr = np.array([1, 2, 3, 4, 5])print(arr)print(type(arr))
[1 2 3 4 5]<class 'numpy.ndarray'>
4.1. Como criar matrizes
Com o método array()
é possível criar ndarray
s introduzindo listas de Python (como o exemplo anterior) ou tuplas
arr = np.array((1, 2, 3, 4, 5))print(arr)print(type(arr))
[1 2 3 4 5]<class 'numpy.ndarray'>
Com o método zeros()
é possível criar matrizes cheias de zeros
arr = np.zeros((3, 4))print(arr)
[[0. 0. 0. 0.][0. 0. 0. 0.][0. 0. 0. 0.]]
O método zeros_like(A)
retorna uma matriz com a mesma forma que a matriz A, porém preenchida com zeros
A = np.array((1, 2, 3, 4, 5))arr = np.zeros_like(A)print(arr)
[0 0 0 0 0]
Com o método ones()
podem-se criar matrizes cheias de uns
arr = np.ones((4, 3))print(arr)
[[1. 1. 1.][1. 1. 1.][1. 1. 1.][1. 1. 1.]]
O método ones_like(A)
retorna uma matriz com a mesma forma que a matriz A, mas preenchida com uns
A = np.array((1, 2, 3, 4, 5))arr = np.ones_like(A)print(arr)
[1 1 1 1 1]
Com o método empty()
é possível criar matrizes com as dimensões que desejamos, mas inicializadas aleatoriamente
arr = np.empty((6, 3))print(arr)
[[4.66169180e-310 2.35541533e-312 2.41907520e-312][2.14321575e-312 2.46151512e-312 2.31297541e-312][2.35541533e-312 2.05833592e-312 2.22809558e-312][2.56761491e-312 2.48273508e-312 2.05833592e-312][2.05833592e-312 2.29175545e-312 2.07955588e-312][2.14321575e-312 0.00000000e+000 0.00000000e+000]]
O método empty_like(A)
retorna uma matriz com o mesmo formato da matriz A, mas inicializada de forma aleatória.
A = np.array((1, 2, 3, 4, 5))arr = np.empty_like(A)print(arr)
[4607182418800017408 4611686018427387904 46139378182410731524616189618054758400 4617315517961601024]
Com o método arange(start, stop, step)
é possível criar matrizes em um intervalo determinado. Este método é semelhante ao método range()
do Python.
arr = np.arange(10, 30, 5)print(arr)
[10 15 20 25]
Quando arange
é usado com argumentos de ponto flutuante, geralmente não é possível prever o número de elementos obtidos, devido à precisão finita do ponto flutuante.
Por este motivo, geralmente é melhor usar a função linspace(start, stop, n)
que recebe como argumento a quantidade de elementos que queremos, em vez do passo.
arr = np.linspace(0, 2, 9)print(arr)
[0. 0.25 0.5 0.75 1. 1.25 1.5 1.75 2. ]
Por último, se queremos criar matrizes com números aleatórios podemos usar a função random.rand
com uma tupla com as dimensões como parâmetro
arr = np.random.rand(2, 3)print(arr)
[[0.32726085 0.65571767 0.73126697][0.91938206 0.9862451 0.95033649]]
4.2. Dimensões das matrizes
Em NumPy podemos criar matrizes de qualquer dimensão. Para obter a dimensão de um array utilizamos o método ndim
Matriz de dimensão 0, o que equivaleria a um número
arr = np.array(42)print(arr)print(arr.ndim)
420
Matriz de dimensão 1, o que equivaleria a um vetor
arr = np.array([1, 2, 3, 4, 5])print(arr)print(arr.ndim)
[1 2 3 4 5]1
Matriz de dimensão 2, o que equivaleria a uma matriz
arr = np.array([[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]])print(arr)print(arr.ndim)
[[ 1 2 3 4 5][ 6 7 8 9 10]]2
Matriz de dimensão 3
arr = np.array([[[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]],[[11, 12, 13, 14, 15],[16, 17, 18, 19, 20]]])print(arr)print(arr.ndim)
[[[ 1 2 3 4 5][ 6 7 8 9 10]][[11 12 13 14 15][16 17 18 19 20]]]3
Matriz de dimensão N. Na hora de criar ndarray
s, é possível estabelecer o número de dimensões através do parâmetro ndim
arr = np.array([1, 2, 3, 4, 5], ndmin=6)print(arr)print(arr.ndim)
[[[[[[1 2 3 4 5]]]]]]6
4.3. Tamanho das matrizes
Se, em vez da dimensão da matriz, quisermos ver seu tamanho, podemos usar o método shape
arr = np.array([[[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]],[[11, 12, 13, 14, 15],[16, 17, 18, 19, 20]]])print(arr.shape)
(2, 2, 5)
5. Tipo de dados
Os dados que podem armazenar os arrays do NumPy são os seguintes:
i
- Inteiro*b
- Booleano*u
- Inteiro sem sinal*f
- Flutuante*c
- Flutuante complexo*m
- Timedelta*M
- DateTime*O
- Objeto*S
- String*U
- String Unicode*V
- Fragmento de memória fixo para outro tipo (void)
Podemos verificar o tipo de dados que uma matriz possui através de dtype
arr = np.array([1, 2, 3, 4])print(arr.dtype)arr = np.array(['apple', 'banana', 'cherry'])print(arr.dtype)
int64<U6
Também podemos criar matrizes indicando o tipo de dado que queremos que tenha mediante dtype
arr = np.array([1, 2, 3, 4], dtype='i')print("Enteros:")print(arr)print(arr.dtype)arr = np.array([1, 2, 3, 4], dtype='f')print("\nFloat:")print(arr)print(arr.dtype)arr = np.array([1, 2, 3, 4], dtype='f')print("\nComplejos:")print(arr)print(arr.dtype)arr = np.array([1, 2, 3, 4], dtype='S')print("\nString:")print(arr)print(arr.dtype)arr = np.array([1, 2, 3, 4], dtype='U')print("\nUnicode string:")print(arr)print(arr.dtype)arr = np.array([1, 2, 3, 4], dtype='O')print("\nObjeto:")print(arr)print(arr.dtype)
Enteros:[1 2 3 4]int32Float:[1. 2. 3. 4.]float32Complejos:[1. 2. 3. 4.]float32String:[b'1' b'2' b'3' b'4']|S1Unicode string:['1' '2' '3' '4']<U1Objeto:[1 2 3 4]object
6. Operações matemáticas
6.1. Operações básicas
As operações matriciais são realizadas por elementos, por exemplo, se somarmos duas matrizes, serão somados os elementos de cada matriz na mesma posição, assim como na soma matemática de duas matrizes.
A = np.array([1, 2, 3])B = np.array([1, 2, 3])print(f"Matriz A: tamaño {A.shape}\n{A}\n")print(f"Matriz B: tamaño {B.shape}\n{B}\n")C = A + Bprint(f"Matriz C: tamaño {C.shape}\n{C}\n")D = A - Bprint(f"Matriz D: tamaño {D.shape}\n{D}")
Matriz A: tamaño (3,)[1 2 3]Matriz B: tamaño (3,)[1 2 3]Matriz C: tamaño (3,)[2 4 6]Matriz D: tamaño (3,)[0 0 0]
No entanto, se fazemos a multiplicação de duas matrizes, também é feita a multiplicação de cada elemento das matrizes (produto escalar)
A = np.array([[3, 5], [4, 1]])B = np.array([[1, 2], [-3, 0]])print(f"Matriz A: tamaño {A.shape}\n{A}\n")print(f"Matriz B: tamaño {B.shape}\n{B}\n")C = A * Bprint(f"Matriz C: tamaño {C.shape}\n{C}\n")
Matriz A: tamaño (2, 2)[[3 5][4 1]]Matriz B: tamaño (2, 2)[[ 1 2][-3 0]]Matriz C: tamaño (2, 2)[[ 3 10][-12 0]]
Para fazer o produto matricial que foi ensinado em matemática toda a vida, é necessário usar o operador @
ou o método dot
A = np.array([[3, 5], [4, 1], [6, -1]])B = np.array([[1, 2, 3], [-3, 0, 4]])print(f"Matriz A: tamaño {A.shape}\n{A}\n")print(f"Matriz B: tamaño {B.shape}\n{B}\n")C = A @ Bprint(f"Matriz C: tamaño {C.shape}\n{C}\n")D = A.dot(B)print(f"Matriz D: tamaño {D.shape}\n{D}")
Matriz A: tamaño (3, 2)[[ 3 5][ 4 1][ 6 -1]]Matriz B: tamaño (2, 3)[[ 1 2 3][-3 0 4]]Matriz C: tamaño (3, 3)[[-12 6 29][ 1 8 16][ 9 12 14]]Matriz D: tamaño (3, 3)[[-12 6 29][ 1 8 16][ 9 12 14]]
Se em vez de criar uma matriz nova, se quiser modificar alguma existente, pode-se usar os operadores +=
, -=
ou *=
.
A = np.array([[3, 5], [4, 1]])B = np.array([[1, 2], [-3, 0]])print(f"Matriz A: tamaño {A.shape}\n{A}\n")print(f"Matriz B: tamaño {B.shape}\n{B}\n")A += Bprint(f"Matriz A tras suma: tamaño {A.shape}\n{A}\n")A -= Bprint(f"Matriz A tras resta: tamaño {A.shape}\n{A}\n")A *= Bprint(f"Matriz A tras multiplicación: tamaño {A.shape}\n{A}\n")
Matriz A: tamaño (2, 2)[[3 5][4 1]]Matriz B: tamaño (2, 2)[[ 1 2][-3 0]]Matriz A tras suma: tamaño (2, 2)[[4 7][1 1]]Matriz A tras resta: tamaño (2, 2)[[3 5][4 1]]Matriz A tras multiplicación: tamaño (2, 2)[[ 3 10][-12 0]]
Podem ser realizadas operações sobre todos os elementos de uma matriz, isto é graças a uma propriedade chamada broadcasting
que veremos depois mais a fundo
A = np.array([[3, 5], [4, 1]])print(f"Matriz A: tamaño {A.shape}\n{A}\n")B = A * 2print(f"Matriz B: tamaño {B.shape}\n{B}\n")C = A ** 2print(f"Matriz C: tamaño {C.shape}\n{C}\n")D = 2*np.sin(A)print(f"Matriz D: tamaño {D.shape}\n{D}")
Matriz A: tamaño (2, 2)[[3 5][4 1]]Matriz B: tamaño (2, 2)[[ 6 10][ 8 2]]Matriz C: tamaño (2, 2)[[ 9 25][16 1]]Matriz D: tamaño (2, 2)[[ 0.28224002 -1.91784855][-1.51360499 1.68294197]]
6.2. Funções sobre matrizes
Como se pode ver no último cálculo, o NumPy oferece operadores de funções sobre matrizes, há um monte de funções que se podem realizar sobre matrizes, matemáticas, lógicas, de álgebra linear, etc. A seguir mostramos algumas.
A = np.array([[3, 5], [4, 1]])print(f"A\n{A}\n")print(f"exp(A)\n{np.exp(A)}\n")print(f"sqrt(A)\n{np.sqrt(A)}\n")print(f"cos(A)\n{np.cos(A)}\n")
A[[3 5][4 1]]exp(A)[[ 20.08553692 148.4131591 ][ 54.59815003 2.71828183]]sqrt(A)[[1.73205081 2.23606798][2. 1. ]]cos(A)[[-0.9899925 0.28366219][-0.65364362 0.54030231]]
Há algumas funções que retornam informações das matrizes, como a média
A = np.array([[3, 5], [4, 1]])print(f"A\n{A}\n")print(f"A.mean()\n{A.mean()}\n")
A[[3 5][4 1]]A.mean()3.25
No entanto, podemos obter essa informação de cada eixo através do atributo axis
; se este for 0, é feito sobre cada coluna, enquanto que se for 1, é feito sobre cada linha
A = np.array([[3, 5], [4, 1]])print(f"A\n{A}\n")print(f"A.mean() columnas\n{A.mean(axis=0)}\n")print(f"A.mean() filas\n{A.mean(axis=1)}\n")
A[[3 5][4 1]]A.mean() columnas[3.5 3. ]A.mean() filas[4. 2.5]
6.3. Broadcasting
Podem ser realizadas operações matriciais com matrizes de diferentes dimensões. Neste caso, o NumPy detectará isso e fará uma projeção da menor matriz até igualá-la à maior. Esta é uma grande qualidade do NumPy, que faz com que se possam realizar cálculos sobre matrizes sem ter que se preocupar em igualar as dimensões destas.
A = np.array([1, 2, 3])print(f"A\n{A}\n")B = A + 5print(f"B\n{B}\n")
A[1 2 3]B[6 7 8]
A = np.array([1, 2, 3])B = np.ones((3,3))print(f"A\n{A}\n")print(f"B\n{B}\n")C = A + Bprint(f"C\n{C}\n")
A[1 2 3]B[[1. 1. 1.][1. 1. 1.][1. 1. 1.]]C[[2. 3. 4.][2. 3. 4.][2. 3. 4.]]
A = np.array([1, 2, 3])B = np.array([[1], [2], [3]])print(f"A\n{A}\n")print(f"B\n{B}\n")C = A + Bprint(f"C\n{C}\n")
A[1 2 3]B[[1][2][3]]C[[2 3 4][3 4 5][4 5 6]]
7. Indexação de matrizes
O indexamento de matrizes é feito da mesma forma que com as listas de Python
arr = np.array([1, 2, 3, 4, 5])arr[3]
4
No caso de ter mais de uma dimensão, é preciso indicar o índice em cada uma delas
arr = np.array([[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]])arr[1, 2]
8
Pode-se usar a indexação negativa
arr[-1, -2]
9
Em caso de não indicar um dos eixos, considera-se que se quer inteiro
arr = np.array([[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]])arr[1]
array([ 6, 7, 8, 9, 10])
7.1. Pedaços de matrizes
Na hora de indexar, podemos ficar com partes de matrizes da mesma forma que se fazia com as listas de Python.
Lembrar que se fazia da seguinte maneira:
início:parada:passo
Onde o intervalo vai desde o start
(incluído) até o stop
(sem incluir) com um passo de step
Se step
não for indicado, o padrão é 1
Por exemplo, se quisermos itens da segunda linha e da segunda à quarta coluna:
- Selecionamos a segunda linha com um 1 (já que começamos a contar a partir de 0)* Selecionamos da segunda à quarta linha através de 1:4, o 1 para indicar a segunda coluna e o 4 para indicar a quinta (já que o segundo número indica a coluna em que se termina sem incluir essa coluna). Os dois números tendo em conta que se começa a contar do 0
print(arr)print(arr[1, 1:4])
[[ 1 2 3 4 5][ 6 7 8 9 10]][7 8 9]
Podemos levá-lo de uma posição até o final
arr[1, 2:]
array([ 8, 9, 10])
Desde o início até uma posição
arr[1, :3]
array([6, 7, 8])
Estabelecer o intervalo com números negativos
# Definir o intervalo usando números negativos
range_negativo = range(-10, 0)
# Imprimir os valores dentro do intervalo
for num in range_negativo:
print(num)
Neste exemplo, o intervalo range_negativo
começa em -10 e vai até -1 (não inclui 0), em incrementos de 1. O loop for
então imprime cada um desses valores.
arr[1, -3:-1]
array([8, 9])
Escolher o passo
arr[1, 1:4:2]
array([7, 9])
7.2. Iteração sobre matrizes
A iteração sobre matrizes multidimensionais é realizada em relação ao primeiro eixo
M = np.array( [[[ 0, 1, 2],[ 10, 12, 13]],[[100,101,102],[110,112,113]]])print(f'Matriz de dimensión: {M.shape}\n')i = 0for fila in M:print(f'Fila {i}: {fila}')i += 1
Matriz de dimensión: (2, 2, 3)Fila 0: [[ 0 1 2][10 12 13]]Fila 1: [[100 101 102][110 112 113]]
No entanto, se o que queremos é iterar por cada item, podemos usar o método 'flat'
i = 0for fila in M.flat:print(f'Elemento {i}: {fila}')i += 1
Elemento 0: 0Elemento 1: 1Elemento 2: 2Elemento 3: 10Elemento 4: 12Elemento 5: 13Elemento 6: 100Elemento 7: 101Elemento 8: 102Elemento 9: 110Elemento 10: 112Elemento 11: 113
8. Cópia de matrizes
Em NumPy temos duas maneiras de copiar matrizes, através de copy
, que realiza uma cópia nova da matriz, e através de view
que realiza uma visualização da matriz original
A cópia é proprietária dos dados e qualquer mudança feita na cópia não afetará a matriz original, e qualquer mudança feita na matriz original não afetará a cópia.
A visualização não é proprietária dos dados, e qualquer alteração feita na cópia afetará a matriz original, e qualquer alteração feita na matriz original afetará a cópia.
8.1. Copy
arr = np.array([1, 2, 3, 4, 5])copy_arr = arr.copy()arr[0] = 42copy_arr[1] = 43print(f'Original: {arr}')print(f'Copia: {copy_arr}')
Original: [42 2 3 4 5]Copia: [ 1 43 3 4 5]
8.2. Visualizar
arr = np.array([1, 2, 3, 4, 5])view_arr = arr.view()arr[0] = 42view_arr[1] = 43print(f'Original: {arr}')print(f'Vista: {view_arr}')
Original: [42 43 3 4 5]Vista: [42 43 3 4 5]
8.3. Proprietário dos dados
Ante a dúvida de se temos uma cópia ou uma visualização, podemos usar `base`
arr = np.array([1, 2, 3, 4, 5])copy_arr = arr.copy()view_arr = arr.view()print(copy_arr.base)print(view_arr.base)
None[1 2 3 4 5]
9. Forma das matrizes
Podemos saber a forma que tem a matriz através do método shape
. Este nos devolverá uma tupla, o tamanho da tupla representa as dimensões da matriz, em cada elemento da tupla se indica o número de itens em cada uma das dimensões da matriz
arr = np.array([[[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]],[[11, 12, 13, 14, 15],[16, 17, 18, 19, 20]]])print(arr)print(arr.shape)
[[[ 1 2 3 4 5][ 6 7 8 9 10]][[11 12 13 14 15][16 17 18 19 20]]](2, 2, 5)
9.1. Redefinir Forma
Podemos mudar a forma das matrizes para a que quisermos através do método reshape
.
Por exemplo, a matriz anterior, que tem uma forma de (2, 2, 4)
. Podemos passá-la para (5, 4)
arr_reshape = arr.reshape(5, 4)print(arr_reshape)print(arr_reshape.shape)
[[ 1 2 3 4][ 5 6 7 8][ 9 10 11 12][13 14 15 16][17 18 19 20]](5, 4)
É preciso ter em mente que, para redimensionar as matrizes, o número de itens da nova forma deve ter o mesmo número de itens da primeira forma Ou seja, no exemplo anterior, a primeira matriz tinha 20 itens (2x2x4), e a nova matriz tem 20 itens (5x4). O que não podemos é redimensionar para uma matriz de tamanho (3, 4), pois no total haveria 12 itens.
arr_reshape = arr.reshape(3, 4)
---------------------------------------------------------------------------ValueError Traceback (most recent call last)<ipython-input-12-29e85875d1df> in <module>()----> 1 arr_reshape = arr.reshape(3, 4)ValueError: cannot reshape array of size 20 into shape (3,4)
9.2. Dimensão desconhecida
No caso de querermos mudar a forma de uma matriz e uma das dimensões não nos importar, ou não a conhecermos, podemos fazer com que o NumPy a calcule por nós introduzindo um -1
como parâmetro.
arr = np.array([[[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]],[[11, 12, 13, 14, 15],[16, 17, 18, 19, 20]]])arr_reshape = arr.reshape(2, -1)print(arr_reshape)print(arr_reshape.shape)
[[ 1 2 3 4 5 6 7 8 9 10][11 12 13 14 15 16 17 18 19 20]](2, 10)
É preciso ter em mente que não se pode colocar qualquer número nas dimensões conhecidas. O número de itens da matriz original tem que ser um múltiplo das dimensões conhecidas. No exemplo anterior, a matriz tem 20 itens, que é múltiplo de 2, dimensão conhecida introduzida. Não seria possível colocar 3 como dimensão conhecida, uma vez que 20 não é múltiplo de 3, e não haveria nenhum número que pudesse ser colocado na dimensão desconhecida que fizesse com que no total houvesse 20 itens.
9.3. Aplainamento de matrizes
Podemos achatar as matrizes, ou seja, transformá-las em uma única dimensão usando reshape(-1)
. Desta forma, independentemente das dimensões da matriz original, a nova sempre terá uma única dimensão.
arr = np.array([[[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]],[[11, 12, 13, 14, 15],[16, 17, 18, 19, 20]]])arr_flatten = arr.reshape(-1)print(arr_flatten)print(arr_flatten.shape)
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20](20,)
Outra forma de aplanar uma matriz é através do método ravel()
arr = np.array([[[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]],[[11, 12, 13, 14, 15],[16, 17, 18, 19, 20]]])arr_flatten = arr.ravel()print(arr_flatten)print(arr_flatten.shape)
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20](20,)
9.4. Matriz transposta
Pode-se obter a transposta de uma matriz através do método T
. Fazer a transposta de uma matriz é trocar as linhas e as colunas da matriz, na imagem abaixo vê-se um exemplo que esclarece melhor.
arr = np.array([[1, 0, 4],[0, 5, 0],[6, 0, -9]])arr_t = arr.Tprint(arr_t)print(arr_t.shape)
[[ 1 0 6][ 0 5 0][ 4 0 -9]](3, 3)
10. Empilhamento de matrizes
10.1. Empilhamento vertical
Podem-se empilhar matrizes verticalmente (unindo linhas) através do método vstack()
a = np.array([[1, 1, 1],[2, 2, 2],[3, 3, 3]])b = np.array([[4, 4, 4],[5, 5, 5],[6, 6, 6]])c = np.vstack((a,b))c
array([[1, 1, 1],[2, 2, 2],[3, 3, 3],[4, 4, 4],[5, 5, 5],[6, 6, 6]])
Se você tiver matrizes de mais de 2 dimensões, vstack()
empilhará ao longo da primeira dimensão
a = np.array([[[1, 1],[2, 2]],[[3, 3],[4, 4]]])b = np.array([[[5, 5],[6, 6]],[[7, 7],[8, 8]]])c = np.vstack((a,b))c
array([[[1, 1],[2, 2]],[[3, 3],[4, 4]],[[5, 5],[6, 6]],[[7, 7],[8, 8]]])
10.2. Empilhamento horizontal
Podem-se empilhar matrizes horizontalmente (unindo colunas) através do método hstack()
a = np.array([[1, 2, 3],[1, 2, 3],[1, 2, 3]])b = np.array([[4, 5, 6],[4, 5, 6],[4, 5, 6]])c = np.hstack((a,b))c
array([[1, 2, 3, 4, 5, 6],[1, 2, 3, 4, 5, 6],[1, 2, 3, 4, 5, 6]])
Se você tem matrizes de mais de 2 dimensões, hstack()
empilhará ao longo da segunda dimensão
a = np.array([[[1, 1],[2, 2]],[[3, 3],[4, 4]]])b = np.array([[[5, 5],[6, 6]],[[7, 7],[8, 8]]])c = np.hstack((a,b))c
array([[[1, 1],[2, 2],[5, 5],[6, 6]],[[3, 3],[4, 4],[7, 7],[8, 8]]])
Outra maneira de adicionar colunas a uma matriz é através do método column_stack()
a = np.array([[1, 2, 3],[1, 2, 3],[1, 2, 3]])b = np.array([4, 4, 4])c = np.column_stack((a,b))c
array([[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4]])
10.3. Empilhamento em profundidade
Podem ser empilhadas matrizes em profundidade (terceira dimensão) através do método dstack()
a = np.array([[[1, 1],[2, 2]],[[3, 3],[4, 4]]])b = np.array([[[1, 1],[2, 2]],[[3, 3],[4, 4]]])c = np.dstack((a,b))print(f"c: {c}\n")print(f"a.shape: {a.shape}, b.shape: {b.shape}, c.shape: {c.shape}")
c: [[[1 1 1 1][2 2 2 2]][[3 3 3 3][4 4 4 4]]]a.shape: (2, 2, 2), b.shape: (2, 2, 2), c.shape: (2, 2, 4)
Se se possuem matrizes de mais de 4 dimensões, dstack()
as empilhará ao longo da terceira dimensão
a = np.array([1, 2, 3, 4, 5], ndmin=4)b = np.array([1, 2, 3, 4, 5], ndmin=4)c = np.dstack((a,b))print(f"a.shape: {a.shape}, b.shape: {b.shape}, c.shape: {c.shape}")
a.shape: (1, 1, 1, 5), b.shape: (1, 1, 1, 5), c.shape: (1, 1, 2, 5)
10.3. Empilhamento personalizado
Mediante o método concatenate()
é possível escolher o eixo no qual se deseja empilhar as matrizes
a = np.array([[[1, 1],[2, 2]],[[3, 3],[4, 4]]])b = np.array([[[5, 5],[6, 6]],[[7, 7],[8, 8]]])conc0 = np.concatenate((a,b), axis=0) # concatenamiento en el primer ejeconc1 = np.concatenate((a,b), axis=1) # concatenamiento en el segundo ejeconc2 = np.concatenate((a,b), axis=2) # concatenamiento en el tercer ejeprint(f"conc0: {conc0}\n")print(f"conc1: {conc1}\n")print(f"conc2: {conc2}")
conc0: [[[1 1][2 2]][[3 3][4 4]][[5 5][6 6]][[7 7][8 8]]]conc1: [[[1 1][2 2][5 5][6 6]][[3 3][4 4][7 7][8 8]]]conc2: [[[1 1 5 5][2 2 6 6]][[3 3 7 7][4 4 8 8]]]
11. Dividir matrizes
11.1. Dividir verticalmente
Podem-se dividir matrizes verticalmente (separando linhas) através do método vsplit()
a = np.array([[1.1, 1.2, 1.3, 1.4],[2.1, 2.2, 2.3, 2.4],[3.1, 3.2, 3.3, 3.4],[4.1, 4.2, 4.3, 4.4]])[a1, a2] = np.vsplit(a, 2)print(f"a1: {a1}\n")print(f"a2: {a2}")
a1: [[1.1 1.2 1.3 1.4][2.1 2.2 2.3 2.4]]a2: [[3.1 3.2 3.3 3.4][4.1 4.2 4.3 4.4]]
Se houver matrizes com mais de 2 dimensões, vsplit()
dividirá ao longo da primeira dimensão
a = np.array([[[1, 1],[2, 2]],[[3, 3],[4, 4]]])[a1, a2] = np.vsplit(a, 2)print(f"a1: {a1}\n")print(f"a2: {a2}")
a1: [[[1 1][2 2]]]a2: [[[3 3][4 4]]]
11.2. Dividir horizontalmente
Podem-se dividir matrizes horizontalmente (separando colunas) por meio do método hsplit()
a = np.array([[1.1, 1.2, 1.3, 1.4],[2.1, 2.2, 2.3, 2.4],[3.1, 3.2, 3.3, 3.4],[4.1, 4.2, 4.3, 4.4]])[a1, a2] = np.hsplit(a, 2)print(f"a1: {a1}\n")print(f"a2: {a2}")
a1: [[1.1 1.2][2.1 2.2][3.1 3.2][4.1 4.2]]a2: [[1.3 1.4][2.3 2.4][3.3 3.4][4.3 4.4]]
Se você tiver matrizes de mais de 2 dimensões, hsplit()
dividirá ao longo da segunda dimensão
a = np.array([[[1, 1],[2, 2]],[[3, 3],[4, 4]]])[a1, a2] = np.hsplit(a, 2)print(f"a1: {a1}\n")print(f"a2: {a2}")
a1: [[[1 1]][[3 3]]]a2: [[[2 2]][[4 4]]]
11.3 Divisão personalizada
Mediante o método array_split()
pode-se escolher o eixo no qual se desejam dividir as matrizes
a = np.array([[[1, 1],[2, 2]],[[3, 3],[4, 4]]])[a1_eje0, a2_eje0] = np.array_split(a, 2, axis=0)[a1_eje1, a2_eje1] = np.array_split(a, 2, axis=1)[a1_eje2, a2_eje2] = np.array_split(a, 2, axis=2)print(f"a1_eje0: {a1_eje0}\n")print(f"a2_eje0: {a2_eje0} ")print(f"a1_eje1: {a1_eje1}\n")print(f"a2_eje1: {a2_eje1} ")print(f"a1_eje2: {a1_eje2}\n")print(f"a2_eje2: {a2_eje2}")
a1_eje0: [[[1 1][2 2]]]a2_eje0: [[[3 3][4 4]]]a1_eje1: [[[1 1]][[3 3]]]a2_eje1: [[[2 2]][[4 4]]]a1_eje2: [[[1][2]][[3][4]]]a2_eje2: [[[1][2]][[3][4]]]
12. Busca em matrizes
Se se quer buscar um valor dentro de uma matriz, pode-se usar o método where()
que devolve as posições onde a matriz tem o valor que estamos procurando
arr = np.array([1, 2, 3, 4, 5, 4, 4])ids = np.where(arr == 4)ids
(array([3, 5, 6]),)
Podem-se usar funções para procurar, por exemplo, se quisermos procurar em quais posições os valores são pares
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])ids = np.where(arr%2)ids
(array([0, 2, 4, 6]),)
13. Ordenar matrizes
Mediante o método sort()
podemos ordenar matrizes
arr = np.array([3, 2, 0, 1])arr_ordenado = np.sort(arr)arr_ordenado
array([0, 1, 2, 3])
Se o que temos são strings, ele as ordena em ordem alfabética
arr = np.array(['banana', 'apple', 'cherry'])arr_ordenado = np.sort(arr)arr_ordenado
array(['apple', 'banana', 'cherry'], dtype='<U6')
E as matrizes de booleanos também as ordena
arr = np.array([True, False, True])arr_ordenado = np.sort(arr)arr_ordenado
array([False, True, True])
Se se tiver matrizes de mais de uma dimensão, ordena-as por dimensões, ou seja, se se tiver uma matriz de 2 dimensões, ordena os números da primeira fila entre eles e os da segunda fila entre eles
arr = np.array([[3, 2, 4], [5, 0, 1]])arr_ordenado = np.sort(arr)arr_ordenado
array([[2, 3, 4],[0, 1, 5]])
Por padrão, ordena sempre em relação às linhas, mas se você quiser que ordene em relação a outra dimensão, deve especificar através da variável axis
arr = np.array([[3, 2, 4], [5, 0, 1]])arr_ordenado0 = np.sort(arr, axis=0) # Se ordena con respecto a la primera dimensiónarr_ordenado1 = np.sort(arr, axis=1) # Se ordena con respecto a la segunda dimensiónprint(f"arr_ordenado0: {arr_ordenado0}\n")print(f"arr_ordenado1: {arr_ordenado1}\n")
arr_ordenado0: [[3 0 1][5 2 4]]arr_ordenado1: [[2 3 4][0 1 5]]
14. Filtros em matrizes
NumPy oferece a possibilidade de buscar certos elementos de uma matriz e criar uma nova Isso é feito criando uma matriz de índices booleanos, ou seja, cria uma nova matriz que indica com quais posições ficamos da matriz e com quais não. Vamos dar uma olhada em um exemplo de uma matriz de índices booleanos
arr = np.array([37, 85, 12, 45, 69, 22])indices_booleanos = [False, False, True, False, False, True]arr_filter = arr[indices_booleanos]print(f"Array original: {arr}")print(f"indices booleanos: {indices_booleanos}")print(f"Array filtrado: {arr_filter}")
Array original: [37 85 12 45 69 22]indices booleanos: [False, False, True, False, False, True]Array filtrado: [12 22]
Como se pode ver, o array filtrado (arr_filtr
), apenas ficou do array original (arr
) com os elementos que coincidem com aqueles em que o array indices_booleanos
vale True
.
Outra coisa que podemos ver é que ele ficou apenas com os elementos pares, então agora vamos ver como fazer para ficar com os elementos pares de uma matriz, sem ter que fazer manualmente como fizemos no exemplo anterior.
arr = np.array([[1, 2, 3, 4, 5],[6, 7, 8, 9, 10]])indices_booleanos = arr % 2 == 0arr_filter = arr[indices_booleanos]print(f"Array original: {arr}\n")print(f"indices booleanos: {indices_booleanos}\n")print(f"Array filtrado: {arr_filter}")
Array original: [[ 1 2 3 4 5][ 6 7 8 9 10]]indices booleanos: [[False True False True False][ True False True False True]]Array filtrado: [ 2 4 6 8 10]