Numpy: Operações Básicas#
A biblioteca numpy fornece uma classe para arrays n-dimensionais de dados. Este notebook é baseado no tutorial do numpy da página do scipy.
import numpy as np
O Numpy fornece uma classe de array multidimensional, assim como um grande número de funções que operam sobre arrays.
Os arrays do Numpy permitem que você escreva código rápido (otimizado) que funciona com arrays de dados. Para fazer isso, há algumas restrições nos arrays:
todos os elementos são do mesmo tipo de dado (por exemplo, float)
o tamanho do array é fixo na memória e especificado quando você cria o array (por exemplo, você não pode aumentar o tamanho do array como faz com listas)
A parte interessante é que as operações aritméticas funcionam sobre arrays inteiros — isso significa que você pode evitar escrever loops em Python (que tendem a ser mais lentos). Em vez disso, o «looping» é feito no código compilado subjacente.
A crítica comum contra o Python é que ele é lento (porque é uma linguagem interpretada, não compilada como o C). Bem, isso não é verdade; o Numpy atinge uma velocidade equivalente à do C.
Sempre lembre-se:
Tempo até o papel = tempo necessário para escrever o código + tempo de execução do código.
Não adianta perder dias depurando o código para ganhar 1ms de tempo de execução.
Criação de Arrays#
Existem várias maneiras de criar arrays. Vamos ver algumas.
A primeira que vamos ver é a criação de uma array através de listas, com o tipo dos dados sendo deduzido através dos tipos dos elementos da sequência.
b = np.array( [1, 2.1, 3, 4] )
print(b)
print(b.dtype)
print(type(b))
[1. 2.1 3. 4. ]
float64
<class 'numpy.ndarray'>
Uma lista contendo listas resultará em um array de duas dimensões, enquanto uma lista de listas de listas resultará em um array tridimensional, e assim por diante. Também é possível definir o tipo de dado do array no momento de sua criação:
b = np.array(
[
[1., 0., 1.],
[0., 1., 2.]
],
)
print(b)
[[1. 0. 1.]
[0. 1. 2.]]
Podemos usar o parâmetro dtype para especificar o tipo de dados desejado para os elementos de um array. Quando você cria um array, o dtype define o tipo de dados, como inteiros (int), números de ponto flutuante (float), entre outros.
b = np.array(
[
[1., 0., 1.],
[0., 1., 2.]
],
dtype=complex
)
print(b)
[[1.+0.j 0.+0.j 1.+0.j]
[0.+0.j 1.+0.j 2.+0.j]]
É comum precisar criar arrays com um tamanho específico e valores iniciais, os quais poderão ser alterados posteriormente. Para facilitar esse processo, o NumPy oferece várias funções, como a função:
zeros, que gera um array preenchido com zeros;ones, que cria um array com valores iguais a um;empty, que cria um array com valores aleatórios, dependendo do estado da memória;eye, que gera uma matriz identidade.
a = np.zeros((10,8),dtype=int)
a
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],
[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]])
Note que as dimensões são passadas como uma tupla
b = np.ones((3, 4))
print(b)
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
c = np.empty((3, 4))
print(c) # Saída pode variar
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
Diferente das listas em Python, todos os elementos de um array do numpy são do mesmo tipo de dado.
d = np.eye(10, dtype=np.float64) # Basta passar uma dimensão, pois a matriz será quadrada
d
array([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])
Podemos criamos um array usando arange é usada para criar sequências de números, tal função é análoga à função range, mas retorna arrays e aceita criar intervalos de pontos flutuantes.
a = np.arange(15)
a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
Também podemos criar array de dimensões maiores
print(np.arange(5, 20, 5))
print()
print(np.arange(0, 2, 0.4))
[ 5 10 15]
[0. 0.4 0.8 1.2 1.6]
Devido a questões relacionadas ao arredondamento, pode não ser preciso determinar a quantidade exata de elementos gerados pela função arange quando pontos flutuantes são usados como parâmetros.
Nesses casos, é recomendado utilizar a função linspace que cria um array com números espaçados igualmente.
Vamos analisar cada parte da coleção range:
linspace(start, stop, num, endpoint): A funçãolinspace()gera uma sequência de números. Ela aceita três argumentos:start: O valor inicial da sequência (inclusivo).stop: O valor final da sequência (dependendo do parâmetroendpoint, pode ser ou não incluído).num: O número de pontos a serem gerados. O padrão é 50.endpoint: SeTrue(padrão), o valor final (stop) será incluído na sequência. SeFalse, o valor final será excluído.
O argumento opcional endpoint especifica se o valor superior do intervalo estará ou não no array.
d = np.linspace(0, 1, 10, endpoint=True)
print(d)
[0. 0.11111111 0.22222222 0.33333333 0.44444444 0.55555556
0.66666667 0.77777778 0.88888889 1. ]
print(np.linspace(0, 1, 9)) # 9 números começando em 0 e terminando em 1
[0. 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1. ]
Exercício Rapído
Análogo ao linspace(), existe a função logspace() que cria um array com elementos igualmente espaçados em escala logarítmica. Use help(np.logspace) para ver os argumentos e criar um array com 10 elementos de \(10^{-6}\) até \(10^3\). Em seguida, faça o mesmo com geomspace e descubra a diferença.
A função linspace é útil para avaliar funções em muitos pontos.
x = np.linspace(0, 2 * np.pi, 5, endpoint=True)
f = np.sin(x)
print(x)
print(f)
[0. 1.57079633 3.14159265 4.71238898 6.28318531]
[ 0.0000000e+00 1.0000000e+00 1.2246468e-16 -1.0000000e+00
-2.4492936e-16]
Propriedades de Arrays#
Um array do numpy possui muitas operações associados a ele, descrevendo sua forma, tipo de dado, etc.
Função |
Descrição |
|---|---|
a.ndim |
Retorna o número de dimensões (ou eixos) de um array. |
a.shape |
Retorna uma tupla que representa as dimensões do array. |
a.size |
Retorna o número total de elementos presentes no array. |
a.dtype |
Retorna o tipo de dados dos elementos do array. |
a.itemsize |
Retorna o tamanho (em bytes) de cada elemento do array. |
type(a) |
Retorna o tipo do objeto, mostrando se é um |
print(a.ndim)
print(a.shape)
print(a.size)
print(a.dtype)
print(a.itemsize)
print(type(a))
1
(15,)
15
int64
8
<class 'numpy.ndarray'>
# Como sempre, se tiver dúvidas:
#help(a)
Operações com Arrays#
A maioria das operações (+, -, *, /) funcionará em um array inteiro de uma vez, elemento por elemento.
Note que o operador de multiplicação não é uma multiplicação de matrizes (existe um novo operador no Python 3.5+, @, para multiplicação de matrizes).
No caso das listas, tinhamos:
l=[1,2,3]
l*2
[1, 2, 3, 1, 2, 3]
Agora considerando o array dessa lista:
np.array(l)*2
array([2, 4, 6])
Vamos criar um array simples para começar as explicações das operações.
a = np.array([10,20,30,40])
print(a)
[10 20 30 40]
A multiplicação por um escalar multiplica cada elemento.
2*a
array([20, 40, 60, 80])
Adicionar dois arrays soma elemento por elemento.
a + a
array([20, 40, 60, 80])
Multiplicar dois arrays multiplica elemento por elemento.
a**2
array([ 100, 400, 900, 1600])
Verificar quais elementos satisfazem uma determinada condição
a < 25
array([ True, True, False, False])
Exercício Rapído
O que você acha que 1./a vai fazer? Tente e veja.
Podemos pensar no nosso array 2D como uma matriz e assim realizar algumas operações básicas de álgebra linear
Começando pela transposição de matrizes
a = np.array(
[
[1, 1],
[0, 1]
]
)
print(a)
[[1 1]
[0 1]]
b = a.transpose() ## Também a.T
b
array([[1, 0],
[1, 1]])
O operador “*” multiplica os arrays através dos elementos. O operador @ é um operador de multiplicação de matrizes / produto escalar.
a @ b
array([[2, 1],
[1, 1]])
Também podemos obter facilmente os extremos, ou seja, os maiores e menores valores dentro de uma array
a = np.array(
[
[10, 12],
[0, 41]
]
)
print(a.min(), a.max())
0 41
Algumas operações, como as de atribuição aritmética «+=» e «*=», modificam o array diretamente, ao invés de criar outro com o resultado.
a = np.array(
[
[1, 1],
[0, 1]
]
)
a += 1
print(a)
[[2 2]
[1 2]]
Operações com arrays de tipos diferentes resultam em um array do tipo mais geral ou preciso (upcasting). Exemplo:
a = np.ones(3, dtype=int)
b = np.ones(3, dtype=float)
print((a + b).dtype)
float64
Muitas operações são computadas como métodos da classe array, por exemplo a soma de todos os elementos, a média ou o desvio-padrão:
a = np.random.random((5, 3))
print(a)
print(
'Soma: {}, Média: {}, Desvio-padrão: {}'.format(
a.sum(), a.mean(), a.std()
)
)
[[0.46491385 0.65428041 0.83622928]
[0.4847881 0.60334199 0.89804622]
[0.05087664 0.34776036 0.97474434]
[0.92446802 0.96410918 0.95749556]
[0.69163477 0.20069438 0.92357783]]
Soma: 9.976960936568066, Média: 0.6651307291045377, Desvio-padrão: 0.2897449957634456
Por padrão, essas operações são computadas sobre todos os elementos do array, independente das suas dimensões. No entanto, é possível especificar a dimensão desejada, usando o parâmetro axis. O parâmetro axis determina ao longo de qual dimensão a soma será realizada:
axis=0: Soma ao longo das colunas (soma vertical).axis=1: Soma ao longo das linhas (soma horizontal).
a = np.array(
[
[1, 1],
[0, 1]
]
)
# Soma ao longo do eixo 0 (colunas)
print(a.sum(axis=0))
# Soma ao longo do eixo 1 (linhas)
print(a.sum(axis=1))
print(a.mean(axis=1)) # média de cada linha
print(a.cumsum(axis=0)) # soma acumulada de cada coluna
[1 2]
[2 1]
[1. 0.5]
[[1 1]
[1 2]]
Funções Universais#
As funções universais funcionam elemento por elemento. Vamos criar um novo array escalado por pi / 12.
b = a*np.pi/12.0
print(b)
[[0.26179939 0.26179939]
[0. 0.26179939]]
c = np.cos(b)
print(c)
[[0.96592583 0.96592583]
[1. 0.96592583]]
d = b + c
print(d)
[[1.22772521 1.22772521]
[1. 1.22772521]]
Exercício Rapído
Frequentemente, queremos escrever nossa própria função que opere em um array e retorne um novo array. Podemos fazer isso da mesma forma que fizemos com funções anteriormente — a chave é usar os métodos do módulo np para realizar as operações, pois eles trabalham com, e retornam, arrays.
Escreva uma função simples que retorne \(\sin(2\pi x)\) para um array de entrada x. Em seguida, teste-a passando um array x que você cria usando linspace().
Indexamento e Fatiamento#
O fatiamento funciona de forma muito semelhante ao que vimos com strings. Lembre-se de que o Python usa indexação baseada em 0.

Vamos criar este array a partir da imagem:
a = 2*np.arange(9)
a
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16])
Agora, observe o acesso a um único elemento vs. um intervalo (usando fatiamento).
a[2] #Acessando um único elemento
4
a[2:4] #Acessando um intervalo
array([4, 6])
Além disso, o símbolo: pode ser usado para especificar todos os elementos naquela dimensão.
a[:]
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16])
Arrays Multidimensionais#
Arrays multidimensionais são armazenados em um espaço contíguo na memória — isso significa que as colunas / linhas precisam ser desenroladas (achatadas) para que possam ser pensadas como um único array unidimensional. Diferentes linguagens de programação fazem isso por meio de convenções diferentes:
![]()
Ordem de armazenamento:
Python/C usam armazenamento linha-major: as linhas são armazenadas uma após a outra
Fortran/matlab usam armazenamento coluna-major: as colunas são armazenadas uma após a outra
A ordem de armazenamento é importante quando
Passando arrays entre linguagens
Fazendo loops sobre arrays — você quer acessar elementos que estão próximos uns dos outros na memória
por exemplo, em Fortran:
double precision :: A(M,N) do j = 1, N do i = 1, M A(i,j) = … enddo enddoem C
double A[M][N]; for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { A[i][j] = … } }
Em python, usando numpy, tentaremos evitar loops explícitos sobre os elementos sempre que possível.
Redimensionando arrays#
O tamanho das dimensões do array pode ser obtido por meio do atributo shape:
a = np.arange(15)
print(a)
print(a.shape)
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
(15,)
A forma do array não é fixa e pode ser alterada de várias maneiras, resultando em um novo array com os mesmos elementos do original, porém reorganizados para se ajustarem à nova estrutura.
a = np.arange(15)
c = a.reshape(3, 5)
print(a)
print(c)
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
Poderiamos também fazer a operação diretamente
a = np.arange(15).reshape(3,5)
a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
Observe que a saída de a mostra o armazenamento no formato linha-major. As linhas estão agrupadas dentro dos [...] internos.
Quando uma dimensão recebe o valor -1 em uma operação de redimensionamento, seu tamanho é calculado automaticamente. Por exemplo, se quisermos que a tenha 5 linhas e o número adequado de colunas, podemos fazer:
a.reshape(5, -1)
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14]])
Podemos facilmente transpor os arrays
print(a)
a.T
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])
Ao tentar transpor um array unidimensional usando o atributo T, ele continuará unidimensional. Para realizar a transposição, podemos utilizar o método reshape com -1 no número de linhas, o que criará um vetor coluna com a quantidade necessária de linhas:
a = np.arange(12)
print(a)
print(a.T)
print(a.reshape(-1, 1))
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[[ 0]
[ 1]
[ 2]
[ 3]
[ 4]
[ 5]
[ 6]
[ 7]
[ 8]
[ 9]
[10]
[11]]
Indexando e iterando sobre elementos#
Arrays multidimensionais podem ser acessados, fatiados e percorridos de maneira semelhante às listas e outras estruturas de dados.
Assim se quisermos acessar um elemento especifico, basta informarmos a tupla em questão.
a = np.arange(15).reshape(3,5)
a[2,3] # Elemento na terceira linha e quarta coluna
13
Observe que como o python tem sua indexação começando no zero, então sempre deveremos para pegarmos um elemento de uma linha (i,j) (de acordo com a nomenclatura de matrizes) devemos escrever a[i-1,j-1]
Ao fazer fatiamentos, você acessa um intervalo de elementos. O início e o fim do fatiamento podem ser vistos como as bordas esquerda e direita dos slots no array.
a[0:2,0:2] # Da primeira até a terceira linha pegando os elementos da primeira até a terceira coluna
array([[0, 1],
[5, 6]])
Podemos também acessar uma coluna específica
a[:,1] # A segunda coluna inteira
array([ 1, 6, 11])
Podemos além disso fazer
print(a[:4, 1]) # Do primeiro ao quarto elemento da segunda coluna
print()
print(a[1:3, :]) # Todas as colunas da segunda à terceira linha
[ 1 6 11]
[[ 5 6 7 8 9]
[10 11 12 13 14]]
Se os índices forem passados em uma tupla com menos elementos do que a quantidade de eixos, os índices não especificados são interpretados como fatias completas (ou seja, “:”). Também é possível usar reticências para representar os índices ausentes. Exemplo:
print(a[-1])
print(a[-1, :])
print(a[-1, ...])
[10 11 12 13 14]
[10 11 12 13 14]
[10 11 12 13 14]
Às vezes, queremos uma visão unidimensional do array — aqui vemos o layout de memória (linha-major) de forma mais explícita.
a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
a.flatten()
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
Também podemos iterar sobre arrays multidimensionais — tais operações são feitas ao longo do primeiro eixo (linhas).
a = np.arange(15).reshape(3,5)
for row in a:
print(row)
[0 1 2 3 4]
[5 6 7 8 9]
[10 11 12 13 14]
for row in a:
for el in row:
print(el)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ou elemento por elemento
for e in a.flat:
print(e)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
De maneira geral, queremos evitar fazer loops sobre os elementos de um array em python — isso é lento. Em vez disso, queremos escrever e usar funções que operem sobre o array inteiro de uma vez.
Exercício Rapído
Considere o array definido como:
q = np.array([[1, 2, 3, 2, 1],
[2, 4, 4, 4, 2],
[3, 4, 4, 4, 3],
[2, 4, 4, 4, 2],
[1, 2, 3, 2, 1]])
* Usando notação de fatiamento, crie um array que contenha apenas os `4`'s em `q` (isso será chamado de view, como veremos em breve)
* Zere todos os elementos da sua view
* Como `q` muda?