Arrays, matrizes e operações
NumPy possui algumas funções para criar arrays, veremos agoras as mais utilizadas:
array_simples = np.array([1, 2, 3])
array_simples
array([1, 2, 3])
Como visto anteriormente .array()
cria um array simples. Mas e se eu precisar de um array repleto de números 1
? NumPy fornece a função .ones()
:
ones = np.ones((10, 2))
ones
array([[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.],
[1., 1.]])
A função .ones()
recebe como parâmetro o shape do array que desejamos criar, nesse exemplo criamos um array de 10x2
.
Existe outra função para criar um array apenas de zeros. Exatamente, ela se chama .zeros()
e também recebe parâmetros para definir o tamanho do array:
zeros = np.zeros((4, 4, 3))
zeros
array([[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]]])
Mas, se precisarmos criar um array informando apenas um range de valores, podemos usar o .arange()
:
range_a1 = np.arange(0, 10, 2)
range_a1
array([0, 2, 4, 6, 8])
A função .arange()
assim como as outras também recebe parâmetros configuráveis, nesse caso temos 3 valores: Start, Stop e Step. No nosso exemplo o array começa em 0, conta até 10 com saltos de 2 em 2, ou seja, um array de números pares.
Outro recurso interessante é o de poder criar arrays com valores aleatórios:
random_1 = np.random.randint(10, size=(5, 3))
random_1
array([[2, 3, 8],
[6, 0, 2],
[4, 0, 0],
[2, 7, 1],
[9, 6, 7]])
A método .random
cria valores aleatórios para preencher o array, nesse caso estamos usando a função .randint()
para gerar valores inteiros, como parâmetro informamos que queremos valores aleatórios entre 0 e 10 inseridos em um array de tamanho 5x3
. Outra função muito utilizada é a própria .random()
que gera valores do tipo float, nesse caso a sua utilização fica .random.random()
:
random_2 = np.random.random((5, 3))
random_2
array([[0.55826932, 0.81632871, 0.90207164],
[0.78776093, 0.21937942, 0.88146314],
[0.01304884, 0.72247319, 0.15347852],
[0.0923776 , 0.32395553, 0.11888521],
[0.40217155, 0.04779815, 0.98891801]])
Como resultado temos outro array mas dessa vez de floats entre 0 e 1, também de tamanho 5x3
. NumPy utiliza números pseudo-aleatórios, significa que os números apenas parecem aleatórios, mas na verdade são predeterminados.
Para consistência em um experimento de Machine Learning, convém manter os números aleatórios gerados semelhantes ao longo dos experimentos. Fazer isso é simples e depende do uso da função .seed()
:
np.random.seed(42)
np.random.randint(10, size=(5, 3))
array([[6, 3, 7],
[4, 6, 9],
[2, 6, 7],
[4, 3, 7],
[7, 2, 5]])
Ao reproduzir o exemplo acima no seu ambiente, você deve obter o mesmo resultado.
Selecionando e visualizando elementos
Lembra dos nossos primeiros arrays ? Veremos agora como selecionar os elementos, mas primeiro vamos visualizar todos os elementos apenas para recordar:
a1
array([1, 2, 3])
a2
array([[4, 5, 6],
[7, 8, 9]])
a3
array([[[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9]],
[[10, 11, 12],
[13, 14, 15],
[16, 17, 18]]])
Bem, agora queremos selecionar apenas o primeiro elemento do a1
que por ser um vetor, tem apenas uma única linha:
a1[0]
1
Se fizermos o mesmo com a2
o resultado é diferente:
a2[0]
array([4, 5, 6])
Isso ocorre pois a2
é um array de duas dimensões, logo o que estamos recebendo como responta é a primeira linha do array. Para selecionar apenas o primeiro elemento é preciso informar a coluna:
a2[0,0]
4
Outra forma de selecionar os elementos é utilizando slice
, vejamos um exemplo no array a3
:
a3[:2, :2, :2]
array([[[ 1, 2],
[ 4, 5]],
[[10, 11],
[13, 14]]])
Acessar elementos em arrays requer um pouco de prática, especialmente quando as dimensões aumentam, como no caso do exemplo anterior.
Manipulando e comparando arrays
Veremos agora um pouco de aritmética com arrays. É possível realizar todas as operações (soma, subtração, multiplicação, potência, etc) e mais, para exemplificar vamos utilizar o nosso primeiro array criado, a1
e vamos criar um novo utilizando .ones()
:
a1
array([1, 2, 3])
ones = np.ones(3)
ones
array([1., 1., 1.])
Uma adição simples
a1 + ones
array([2., 3., 4.])
Subtração
a1 - ones
array([0., 1., 2.])
Multiplicação
a1 * ones
array([1., 2., 3.])
Divisão
a1 / ones
array([1., 2., 3.])
Divisão obtendo a parte inteira
a2 // a1
array([[4, 2, 2],
[7, 4, 3]])
Exponenciação
a1 ** 2
array([1, 4, 9])
Exponenciação utilizando a função
.square()
np.square(a1)
array([1, 4, 9])
Módulo
a1 % 2
array([1, 0, 1])
Logaritmo
np.log(a1)
array([0. , 0.69314718, 1.09861229])
Exponencial
np.exp(a1)
array([ 2.71828183, 7.3890561 , 20.08553692])
Aggregations
NumPy também disponibiliza várias funções para agregação, sum()
por exemplo, pode somar todos os elementos de um array:
sum(a1)
6
A mesma função disponibilizada pelo NumPy seria np.sum()
:
np.sum(a1)
6
O resultado é o mesmo, porém o desempenho é completamente diferente. Para o nosso teste, vamos criar um array com 1.000.000
de valores aleatório e comparar as duas funções de soma ( do Python e do NumPy ), medindo o tempo de processamento de cada uma:
grande_array = np.random.random(1000000)
grande_array.size
1000000
Ok, já temos o nosso array com 1 milhão de valores aleatórios, para medir o tempo de processamento das operações de soma, podemos utilizar a função mágica chamada %timeit
que vai exibir o tempo total de processamento de cada função, essa operação pode demorar um pouco:
%timeit sum(grande_array) # Python sum()
%timeit np.sum(grande_array) # NumPy np.sum()
10 loops, best of 5: 165 ms per loop
1000 loops, best of 5: 365 µs per loop
A função .sum()
do Python levou 165 milissegundos, enquanto a função np.sum()
do NumPy fez tudo em 365 microssegundos, um pouco mais de 450x mais rápido. Em outras palavras, se estiver trabalhando com NumPy, por questões de desempenho é melhor escolher as suas funções embutidas para grandes volumes de dados, do que utilizar as operações nativas do Python.
Vamos utilizar o array a2
e experimentar outras funções, por exemplo calcular a média dos valores:
a2
array([[4, 5, 6],
[7, 8, 9]])
np.mean(a2)
6.5
Encontrar o maior valor
.max()
np.max(a2)
9
Encontrar o menor valor
.min()
np.min(a2)
4
Calcular o desvio padrão
.std()
(é uma média de como os valores estão espalhados)
np.std(a2)
1.707825127659933
Calcular a variância
.var()
(é a média das diferenças quadradas da média)
np.var(a2)
2.9166666666666665
Reshaping e Transpose
Imagine que precisamos somar os arrays a2
e a3
:
a2 + a3
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-60-7e0cfe75ff3f> in <module>()
----> 1 a2 + a3
ValueError: operands could not be broadcast together with shapes (2,3) (2,3,3)
E para nossa surpresa recebemos esse erro informando que os shapes são diferentes e que por esse motivo a operação não pode ser realizada. Para resolver o problema podemos reordenar um dos arrays ou fazer o reshape
:
a2
array([[4, 5, 6],
[7, 8, 9]])
a2.reshape(2, 3, 1)
array([[[4],
[5],
[6]],
[[7],
[8],
[9]]])
O que fizemos com o reshape foi alterar o formato do nosso array para uma estrutura de uma só coluna, agora é possível somar a2
e a3
:
a2.reshape(2, 3, 1) + a3
array([[[ 5, 6, 7],
[ 9, 10, 11],
[13, 14, 15]],
[[17, 18, 19],
[21, 22, 23],
[25, 26, 27]]])
Já o transpose
inverte a estrutura do array, por exemplo:
a2
array([[4, 5, 6],
[7, 8, 9]])
a2.T
array([[4, 7],
[5, 8],
[6, 9]])
a2.shape
(2, 3)
a2.T.shape
(3, 2)
Assim, temos
a2
um array de2x3
quando invertido pelo método transpose vira um array de3x2
.
Comparando e ordenando arrays
Assim como podemos utilizar operadores aritméticos com arrays, também é possível utilizar operadores de comparação:
# Temos os arrays a1 e a2
a1
array([1, 2, 3])
a2
array([[4, 5, 6],
[7, 8, 9]])
# a1 é maior que a2 ?
a1 > a2
array([[False, False, False],
[False, False, False]])
# a1 é menor ou igual à a2 ?
a1 <= a2
array([[ True, True, True],
[ True, True, True]])
# a1 é maior que 5 ?
a1 > 5
array([False, False, False])
# a1 é igual à a2 ?
a1 == a2
array([[False, False, False],
[False, False, False]])
Agora um pouco de ordenação, lembra do nosso array aleatório random_1
?
Podemos utilizar a função .sort()
e ordenar os elementos:
random_1
array([[6, 6, 1],
[6, 4, 1],
[1, 1, 6],
[1, 6, 4],
[9, 0, 4]])
np.sort(random_1)
array([[1, 6, 6],
[1, 4, 6],
[1, 1, 6],
[1, 4, 6],
[0, 4, 9]])
WIP