Substituição de elementos Numpy se a condição for atendida

95

Eu tenho uma grande matriz numpy que preciso manipular para que cada elemento seja alterado para 1 ou 0 se uma condição for atendida (será usada como uma máscara de pixel posteriormente). Existem cerca de 8 milhões de elementos na matriz e meu método atual leva muito tempo para o pipeline de redução:

for (y,x), value in numpy.ndenumerate(mask_data): 

    if mask_data[y,x]<3: #Good Pixel
        mask_data[y,x]=1
    elif mask_data[y,x]>3: #Bad Pixel
        mask_data[y,x]=0

Existe uma função numpy que aceleraria isso?

ChrisFro
fonte
1
O que você quer que aconteça mask_data[y,x]==3?
DSM
Bom ponto, isso ainda seria um pixel ruim. Vou mudar a condição paraif mask_data[y,x]>=3:
ChrisFro

Respostas:

131
>>> import numpy as np
>>> a = np.random.randint(0, 5, size=(5, 4))
>>> a
array([[4, 2, 1, 1],
       [3, 0, 1, 2],
       [2, 0, 1, 1],
       [4, 0, 2, 3],
       [0, 0, 0, 2]])
>>> b = a < 3
>>> b
array([[False,  True,  True,  True],
       [False,  True,  True,  True],
       [ True,  True,  True,  True],
       [False,  True,  True, False],
       [ True,  True,  True,  True]], dtype=bool)
>>> 
>>> c = b.astype(int)
>>> c
array([[0, 1, 1, 1],
       [0, 1, 1, 1],
       [1, 1, 1, 1],
       [0, 1, 1, 0],
       [1, 1, 1, 1]])

Você pode encurtar isso com:

>>> c = (a < 3).astype(int)
Steve Barnes
fonte
2
como fazer isso acontecer com colunas específicas sem nunca cortar algumas colunas e depois atribuir novamente? por exemplo, apenas os elementos nas colunas [2, 3] devem mudar de valor quando as condições forem atendidas, enquanto outras colunas não mudarão, independentemente das condições serem atendidas ou não.
kuixiong
Verdade, mas apenas para o caso de zeros e uns. Veja a resposta mais geral abaixo (com custo de eficiência)
borgr
94
>>> a = np.random.randint(0, 5, size=(5, 4))
>>> a
array([[0, 3, 3, 2],
       [4, 1, 1, 2],
       [3, 4, 2, 4],
       [2, 4, 3, 0],
       [1, 2, 3, 4]])
>>> 
>>> a[a > 3] = -101
>>> a
array([[   0,    3,    3,    2],
       [-101,    1,    1,    2],
       [   3, -101,    2, -101],
       [   2, -101,    3,    0],
       [   1,    2,    3, -101]])
>>>

Veja, por exemplo, Indexação com matrizes booleanas .

ev-br
fonte
4
grande coisa, obrigado! Se você quiser se referir ao valor alterado, pode usar algo como a[a > 3] = -101+a[a > 3].
pexmar
2
@pexmar Embora se você a[a > 3] = -101+a[a > 3]em vez de a[a > 3] += -101você provavelmente irá vazamento de memória face.
Samuel Prevost
2
como você se refere ao valor que você altera conforme pexmar perguntou ??
Juan
34

A maneira mais rápida (e mais flexível) é usar np.where , que escolhe entre duas matrizes de acordo com uma máscara (matriz de valores verdadeiros e falsos):

import numpy as np
a = np.random.randint(0, 5, size=(5, 4))
b = np.where(a<3,0,1)
print('a:',a)
print()
print('b:',b)

que irá produzir:

a: [[1 4 0 1]
 [1 3 2 4]
 [1 0 2 1]
 [3 1 0 0]
 [1 4 0 1]]

b: [[0 1 0 0]
 [0 1 0 1]
 [0 0 0 0]
 [1 0 0 0]
 [0 1 0 0]]
Markus Dutschke
fonte
2
Qual será a melhor maneira se eu não quiser substituir por nada se a condição não for atendida? Ou seja, apenas substituir pelo valor fornecido quando a condição for atendida, se não deixar o número original como está ....
Abhishek Sengupta
2
para substituir todos os valores em a, que são menores que 3 e manter o resto como está, usea[a<3] = 0
Markus Dutschke
3

Você pode criar sua matriz de máscara em uma etapa como esta

mask_data = input_mask_data < 3

Isso cria uma matriz booleana que pode ser usada como uma máscara de pixel. Observe que não alteramos a matriz de entrada (como em seu código), mas criamos uma nova matriz para armazenar os dados da máscara - eu recomendaria fazer dessa forma.

>>> input_mask_data = np.random.randint(0, 5, (3, 4))
>>> input_mask_data
array([[1, 3, 4, 0],
       [4, 1, 2, 2],
       [1, 2, 3, 0]])
>>> mask_data = input_mask_data < 3
>>> mask_data
array([[ True, False, False,  True],
       [False,  True,  True,  True],
       [ True,  True, False,  True]], dtype=bool)
>>> 
YXD
fonte
1
Sim. Se o OP realmente quiser 0s e 1s, ele poderia usar .astype(int)ou *1, mas um array de Truee Falseé tão bom quanto é.
DSM
-4

Não tenho certeza se entendi sua pergunta, mas se você escrever:

mask_data[:3, :3] = 1
mask_data[3:, 3:] = 0

Isso fará com que todos os valores dos dados de máscara cujos índices x e y sejam menores que 3 sejam iguais a 1 e todos os restantes sejam iguais a 0

mamalos
fonte