6.3. Criando arrays¶
Todo exemplo no restante destas páginas começa com um ndarray já em mãos. Esta página é o catálogo de como esse array passa a existir. Há quatro famílias de construtor:
A partir de um iterável Python – a forma usual de literal / list / tuple.
Pré-preenchido com uma dada forma – zeros, uns, um valor constante, uma matriz identidade.
Gerado como uma sequência – valores em faixa ou uniformemente espaçados.
Encapsulando um buffer já na RAM – o caso de periférico.
Todo construtor aceita uma palavra-chave dtype= e usa por padrão float. Dados de sensor quase sempre querem um dtype menor que o padrão.
Todo exemplo abaixo começa com:
from ulab import numpy as np
6.3.1. A partir de um iterável Python¶
array() constrói um ndarray a partir de qualquer iterável de números:
a = np.array([1, 2, 3, 4])
print(a)
Saída:
array([1.0, 2.0, 3.0, 4.0], dtype=float)
Iteráveis aninhados produzem arrays multidimensionais. Os iteráveis internos devem todos ter o mesmo comprimento, ou ValueError é levantado:
m = np.array([[1, 2, 3],
[4, 5, 6]], dtype=np.uint8)
Um ndarray pré-existente também é uma entrada válida; array() sempre copia. Para evitar a cópia quando ela não é necessária, use asarray()
b = np.asarray(a, dtype=np.float) # same dtype -> no copy
6.3.2. Pré-preenchido com uma dada forma¶
Quando a forma de destino é conhecida mas o conteúdo ainda não, aloque o buffer antecipadamente e escreva nele depois:
zeros()– preenchido com zeros.ones()– preenchido com uns.full()– preenchido com um dado valor.empty()– alias parazeros()(oulabnão deixa o buffer não inicializado).eye()– matrizN-por-Msemelhante à identidade com uns nak-ésima diagonal.diag()– uma matriz diagonal a partir de um vetor, ou a diagonal de uma matriz.
np.zeros((3, 3)) # 3x3 of zeros
np.ones(5, dtype=np.uint8) # length-5 vector of ones
np.full((2, 3), 7, dtype=np.int8) # 2x3, all 7
np.eye(4) # 4x4 identity
np.diag([1, 2, 3]) # 3x3, [1, 2, 3] on the diagonal
O argumento shape é um único inteiro (para um array 1-D) ou uma tupla.
6.3.3. Gerado como uma sequência¶
arange()– valores uniformemente espaçados como o built-inrange(), mas sempre retornando umndarraynp.arange(0, 10, 2) # array([0, 2, 4, 6, 8])linspace()–numpontos uniformemente espaçados entre dois limites, com o limite superior incluído quandoendpoint=Truenp.linspace(0, 1, num=11) # 0.0, 0.1, ..., 1.0logspace()– pontos geometricamente espaçados.startestopsão expoentes, não extremos; o resultado vai debase ** startabase ** stopnp.logspace(0, 3, num=4) # 1.0, 10.0, 100.0, 1000.0meshgrid()– constrói duas matrizes de coordenadas a partir de dois arrays 1-D para que uma função por pixelf(x, y)possa ser avaliada sobre uma grade inteira em uma única chamada vetorizada. Dado um vetor x de comprimentoWe um vetor y de comprimentoH,meshgridretorna duas matrizesH-por-W:Xé o vetor x repetido em cada linha,Yé o vetor y repetido em cada coluna, de modo queX[i, j]é a coordenada x eY[i, j]é a coordenada y da célula na linhaie colunajx = np.arange(4) # [0, 1, 2, 3] y = np.arange(3) # [0, 1, 2] X, Y = np.meshgrid(x, y) # X = [[0, 1, 2, 3], # [0, 1, 2, 3], # [0, 1, 2, 3]] # Y = [[0, 0, 0, 0], # [1, 1, 1, 1], # [2, 2, 2, 2]]
f(X, Y)então avalia a função em cada célula da grade em uma única expressão. Um mapa de distância-do-centro sobre um quadro(H, W), por exemplo, énp.sqrt((X - cx)**2 + (Y - cy)**2)contra as matrizes quemeshgrid()retornou.
6.3.4. Juntando¶
concatenate() junta uma tupla de arrays ao longo de um eixo existente:
a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
b = np.array([[5, 6]], dtype=np.uint8)
np.concatenate((a, b), axis=0)
# array([[1, 2], [3, 4], [5, 6]], dtype=uint8)
Todas as entradas devem compartilhar o mesmo dtype e ndim, e coincidir em todos os eixos exceto o de junção. concatenate() aloca um array novo grande o suficiente para conter todas as entradas e copia os dados para dentro, então é a ferramenta certa para junção única de arrays que já existem; é a ferramenta errada dentro de um laço de streaming, onde pré-alocar o destino uma vez e escrever nele por atribuição de fatia é o padrão.
6.3.5. Encapsulando um buffer existente¶
O construtor mais útil em uma câmera é frombuffer(). Ele reinterpreta um buffer existente do tipo bytes como um ndarray 1-D sem copiar um único byte:
buf = bytearray(8)
audio = np.frombuffer(buf, dtype=np.int16)
# 4 int16 samples, sharing memory with buf
Escritas através de audio são visíveis em buf e vice-versa. O dtype escolhido deve dividir uniformemente o comprimento do buffer.
offset= pula um cabeçalho no início do buffer; count= limita quantos elementos são lidos:
np.frombuffer(buf, dtype=np.uint8, offset=2, count=4)
Este é o construtor certo sempre que um periférico entrega à aplicação um buffer bruto – amostras de ADC em um bytearray, um payload extraído de SPI. Os bytes que o periférico escreveu são o array.
Quando um periférico escreve valores de múltiplos bytes em uma ordem de bytes que a CPU da câmera não lê nativamente, byteswap() inverte a ordem de bytes de cada elemento para que os valores sejam lidos corretamente. Por padrão retorna um novo array; passar inplace=True modifica a origem no lugar.
frombuffer() só lida com os dtypes que o próprio numpy define. Para periféricos que produzem amostras de inteiros de 32 bits, from_int32_buffer() e funções relacionadas convertem para float em uma única passagem.
6.3.6. Truncamento de impressão¶
Imprimir um array grande mostra apenas seus primeiros e últimos elementos, com ... no meio, para que o terminal da IDE não fique cheio de milhares de valores:
>>> print(np.arange(1000, dtype=np.uint16))
array([0, 1, 2, ..., 997, 998, 999], dtype=uint16)
set_printoptions() sobrescreve os limiares quando a depuração precisa do buffer inteiro:
np.set_printoptions(threshold=2000) # print up to 2000 elements in full
np.set_printoptions(edgeitems=10) # 10 items at each end, not 3
get_printoptions() lê de volta as configurações atuais como um dict.