6.12. Seleção e reordenação

As reduções colapsavam um array em um escalar ou em um resultado de dimensão menor. A seleção cobre as operações que escolhem quais elementos sobrevivem e onde eles vão parar: escolha condicional, clipagem, ordenação, busca de índices, reordenação ao longo de um eixo.

6.12.1. Escolha condicional

where() retorna um array que pega elementos de x onde a condição é verdadeira e de y caso contrário. Os três operandos sofrem broadcasting em conjunto:

a = np.array([1, 2, 3, 4, 5], dtype=np.float)
np.where(a < 3, a, 0.0)
# array([1.0, 2.0, 0.0, 0.0, 0.0])

Essa é a ferramenta certa para um “if/else por elemento” sem escrever um laço Python.

clip() é uma abreviação para maximum(lo, minimum(a, hi)) – satura os valores a uma faixa:

np.clip(a, 2.0, 4.0)
# array([2.0, 2.0, 3.0, 4.0, 4.0])

maximum() e minimum() recebem dois operandos e retornam o maior / menor elemento a elemento:

np.maximum(a, 3.0)
np.minimum(a, np.array([5, 4, 3, 2, 1]))

6.12.2. Encontrando índices

nonzero() retorna as coordenadas de cada elemento não nulo, separadas em um array de índices por dimensão. Para uma entrada 2-D, o resultado é uma tupla de dois arrays: o primeiro contém os índices das linhas e o segundo contém os índices das colunas. Pareá-los coluna a coluna fornece o (row, col) de cada posição não nula:

m = np.array([[0, 2, 0],
              [3, 0, 0]], dtype=np.float)
np.nonzero(m)
# (array([0, 1], dtype=uint16), array([1, 0], dtype=uint16))

As entradas não nulas em m são m[0, 1] = 2 e m[1, 0] = 3. O primeiro array retornado [0, 1] fornece seus índices de linha; o segundo [1, 0] fornece seus índices de coluna. Lendo os dois arrays lado a lado, recuperamos as posições (0, 1) e (1, 0).

Duas reduções também produzem índices:

  • argmin() / argmax() – índice do menor / maior elemento.

  • argsort() – um array de inteiros que ordenaria a entrada ao longo do eixo dado (por padrão, o último):

    a = np.array([40, 10, 30, 20], dtype=np.uint8)
    idx = np.argsort(a)             # array([1, 3, 2, 0], dtype=uint16)
    a[idx]                          # array([10, 20, 30, 40])
    

    argsort sempre retorna uint16; portanto, o array sendo ordenado não pode ter mais que 65.535 elementos no eixo ordenado.

bincount() conta as ocorrências de cada inteiro não negativo em uma entrada 1-D uint8 / uint16

histogram = np.bincount(np.array([0, 1, 1, 2, 2, 2], dtype=np.uint8))
# array([1, 2, 3], dtype=uint16)

Útil para construir histogramas de valores de pixel de inteiros pequenos sem escrever um laço Python.

6.12.3. Ordenação e reordenação

sort() retorna uma cópia ordenada do array ao longo do eixo dado (o último por padrão). Use sort() diretamente no array para uma versão in-place:

np.sort(np.array([3, 1, 2], dtype=np.float))
# array([1.0, 2.0, 3.0])

flip() inverte a ordem ao longo do eixo dado (todos os eixos quando nenhum axis é passado):

np.flip(np.array([1, 2, 3, 4]))
# array([4, 3, 2, 1])

roll() desloca os elementos ciclicamente pela contagem dada. Útil para implementar um registrador de deslocamento no estilo ring-buffer:

np.roll(np.array([1, 2, 3, 4]), 1)
# array([4, 1, 2, 3])

take() é a forma explícita da indexação avançada (fancy indexing) – escolhe elementos em índices arbitrários:

a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.take(a, [0, 2, 4])
# array([10, 30, 50], dtype=uint8)

6.12.4. Filtragem e edições estruturais

compress() é a forma explícita da indexação booleana – retorna as fatias de a selecionadas pela condição booleana:

a = np.array([10, 20, 30, 40], dtype=np.uint8)
np.compress(a > 15, a)
# array([20, 30, 40], dtype=uint8)

delete() retorna uma cópia com as entradas nos índices dados removidas:

a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.delete(a, [1, 3])
# array([10, 30, 50], dtype=uint8)

diff() retorna a n-ésima diferença discreta progressiva do array ao longo de um eixo. Usada para calcular variações de primeira ordem entre amostras adjacentes:

samples = np.array([1, 3, 6, 10, 15], dtype=np.float)
np.diff(samples)
# array([2.0, 3.0, 4.0, 5.0])

6.12.5. O custo de cada operação

Quase toda função desta página retorna um array recém-alocado. Duas exceções:

  • sort() ordena in-place; a função livre sort() retorna uma cópia ordenada.

  • take() aceita uma palavra-chave out= para escrever em um buffer que já existe.

Em um laço que roda muitas vezes por segundo, prefira o sort() in-place e reaproveite buffers pré-alocados em todos os outros lugares. As próprias máscaras booleanas são alocadas toda vez que a comparação roda – construa uma máscara uma vez e reaproveite-a entre operações em vez de reconstruí-la dentro de cada iteração.