Uma das principais vantagens da estrutura ndarray é sua habilidade de processamento matricial.
Assim, para se multiplicar todos os elementos de um array por um escalar basta escrever a 5 por
exemplo. Para se fazer qualquer operação lógica ou aritmética entre arrays, basta escrever a
In [89]:
a = np.arange(20).reshape(5,4)
b = 2 * np.ones((5,4))
c = np.arange(12,0,-1).reshape(4,3)
print('a=\n', a )
print('b=\n', b )
print('c=\n', c )
In [90]:
b5 = 5 * b
print('b5=\n', b5 )
In [91]:
amb = a + b
print('amb=\n', amb )
A transposta de uma matriz, troca os eixos das coordenadas. O elemento que estava na posição (r,c) vai agora estar na posição (c,r). O shape da matriz resultante ficará portanto com os valores trocados. A operação de transposição é feita através de cópia rasa, portanto é uma operação muito eficiente e deve ser utilizada sempre que possível.
Veja o exemplo a seguir:
In [92]:
at = a.T
print('a.shape=',a.shape )
print('a.T.shape=',a.T.shape )
print('a=\n', a )
print('at=\n', at )
A multiplicação de matrizes é feita através do operador dot. Para que a multiplicação seja possível é importante que o número de colunas do primeiro ndarray seja igual ao número de linhas do segundo. As dimensões do resultado será o número de linhas do primeiro ndarray pelo número de colunas do segundo ndarray. Confira:
In [93]:
ac = a.dot(c)
print('a.shape:',a.shape )
print('c.shape:',c.shape )
print('a=\n',a )
print('c=\n',c )
print('ac=\n', ac )
print('ac.shape:',ac.shape )
As funções do numpy linspace e arange tem o mesmo objetivo: gerar numpy.arrays linearmente espaçados em um intervalo indicado como parâmetro.
A diferença primordial entre essas funções é como será realizada a divisão no intervalo especificado. Na função linspace essa divisão é feita através da definição do intervalo fechado [inicio,fim], isto é, contém o início e o fim, e da quantidade de elementos que o numpy.array final terá. O passo portanto é calculado como (fim - inicio)/(n - 1). Dessa forma, se queremos gerar um numpy.array entre 0 e 1 com 10 elementos, utilizaremos o linspace da seguinte forma
In [94]:
# gera um numpy.array de 10 elementos, linearmente espaçados entre 0 a 1
print(np.linspace(0, 1.0, num=10).round(2) )
Já na função arange, define-se o intervalo semi-aberto [inicio,fim) e o passo que será dado entre um elemento e outro. Dessa forma, para gerar um numpy.array entre 0 e 1 com 10 elementos, temos que calcular o passo (0.1) e passar esse passo como parâmetro.
In [95]:
# gera um numpy.array linearmente espaçados entre 0 a 1 com passo 0.1
print(np.arange(0, 1.0, 0.1) )
Confirme que a principal diferença entre os dois que pode ser verificada nos exemplos acima é que no linspace o limite superior da distribuição é inclusivo (intervalo fechado), enquanto no arange isso não ocorre (intervalo semi-aberto).
As funções indices e meshgrid são extremamente úteis na geração de imagens sintéticas e o seu aprendizado permite também entender as vantagens de programação matricial, evitando-se a varredura seqüencial da imagem muito usual na programação na linguagem C.
A função indices recebe como parâmetros uma tupla com as dimensões (H,W) das matrizes a serem criadas. No exemplo a seguir, estamos gerando matrizes de 5 linhas e 10 colunas. Esta função retorna uma tupla de duas matrizes que podem ser obtidas fazendo suas atribuições como no exemplo a seguir onde criamos as matrizes r e c, ambas de tamanho (5,10), isto é, 5 linhas e 10 colunas:
In [96]:
r,c = np.indices( (5, 10) )
print('r=\n', r )
print('c=\n', c )
Note que a matriz r é uma matriz onde cada elemento é a sua coordenada linha e a matriz c é uma matriz onde cada elemento é a sua coordenada coluna. Desta forma, qualquer operação matricial feita com r e c, na realidade você está processando as coordenadas da matriz. Assim, é possível gerar diversas imagens sintéticas a partir de uma função de suas coordenadas.
Como o NumPy processa as matrizes diretamente, sem a necessidade de fazer um for explícito, a notação do programa fica bem simples e a eficiência também. O único inconveniente é o uso da memória para se calcular as matrizes de índices r e c. Iremos ver mais à frente que isto pode ser minimizado.
Por exemplo seja a função que seja a soma de suas coordenadas $f(r,c) = r + c$:
In [97]:
f = r + c
print('f=\n', f )
Ou ainda a função diferença entre a coordenada linha e coluna $f(r,c) = r - c$:
In [98]:
f = r - c
print('f=\n', f )
Ou ainda a função $f(r,c) = (r + c) \% 2$ onde % é operador módulo. Esta função retorna 1 se a soma das coordenadas for ímpar e 0 caso contrário. É uma imagem no estilo de um tabuleiro de xadrez de valores 0 e 1:
In [99]:
f = (r + c) % 2
print('f=\n', f )
Ou ainda a função de uma reta $f(r,c) = (r = \frac{1}{2}c)$:
In [100]:
f = (r == c//2)
print('f=\n', f )
Ou ainda a função parabólica dada pela soma do quadrado de suas coordenadas $f(r,c) = r^2 + c^2$:
In [101]:
f = r**2 + c**2
print('f=\n', f )
Ou ainda a função do círculo de raio 4, com centro em (0,0) $f(r,c) = (r^2 + c^2 < 4^2)$:
In [102]:
f = ((r**2 + c**2) < 4**2)
print('f=\n', f * 1 )
In [122]:
a = np.array([[0,1],[2,3]])
print('a = \n', a )
print()
print('np.resize(a,(1,7)) = \n', np.resize(a,(1,7)) )
print()
print('np.resize(a,(2,5)) = \n', np.resize(a,(2,5)) )
A função clip substitui os valores de um array que estejam abaixo de um limiar mínimo ou que estejam acima de um limiar máximo, por esses limiares mínimo e máximo, respectivamente. Esta função é especialmente útil em processamento de imagens para evitar que os índices ultrapassem os limites das imagens.
In [123]:
a = np.array([11,1,2,3,4,5,12,-3,-4,7,4])
print('a = ',a )
print('np.clip(a,0,10) = ', np.clip(a,0,10) )
In [124]:
a = np.arange(10).astype(np.int)
print('a=',a )
print('np.clip(a,2.5,7.5)=',np.clip(a,2.5,7.5) )
In [125]:
A = np.exp(np.linspace(0.1,10,32)).reshape(4,8)/3000.
print('A: \n', A )
É possível diminuir o número de casas decimais e suprimir a notação exponencial utilizando a função set_printoption do numpy:
In [126]:
np.set_printoptions(suppress=True, precision=3)
print('A: \n', A )
In [127]:
A = np.random.rand(5,10) > 0.5
print('A = \n', A )
Para facilitar a visualização destes arrays, é possível converter os valores para inteiros utilizando o método astype(int):
In [128]:
print ('A = \n', A.astype(int))