Reemplazo de elementos Numpy si se cumple la condición

94

Tengo una gran matriz numpy que necesito manipular para que cada elemento cambie a 1 o 0 si se cumple una condición (se usará como una máscara de píxeles más adelante). Hay alrededor de 8 millones de elementos en la matriz y mi método actual toma demasiado tiempo para la tubería de reducción:

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

¿Hay alguna función que acelere esto?

ChrisFro
fuente
1
¿Qué quieres que pase si mask_data[y,x]==3?
DSM
Buen punto, todavía sería un píxel malo. if mask_data[y,x]>=3:
Cambiaré

Respuestas:

128
>>> 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]])

Puede acortar esto con:

>>> c = (a < 3).astype(int)
Steve Barnes
fuente
2
¿Cómo hacer que esto suceda con columnas específicas sin tener que cortar algunas columnas y luego volver a asignarlas? por ejemplo, solo los elementos en las columnas [2, 3] deben cambiar de valor cuando se cumplen las condiciones, mientras que otras columnas no cambiarán independientemente de que se cumplan o no las condiciones.
kuixiong
Cierto, pero solo para el caso de ceros y unos. Vea una respuesta más general a continuación (a costo de eficiencia)
borgr
89
>>> 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]])
>>>

Ver, por ejemplo, Indexación con matrices booleanas .

ev-br
fuente
3
gran cosa, gracias! Si desea hacer referencia al valor que cambia, puede usar algo como a[a > 3] = -101+a[a > 3].
pexmar
1
@pexmar Aunque si lo hace en a[a > 3] = -101+a[a > 3]lugar de a[a > 3] += -101lo más probable es que se enfrente a una pérdida de memoria.
Samuel Prevost
1
¿cómo te refieres al valor que cambias como preguntaba pexmar?
Juan
34

La forma más rápida (y más flexible) es usar np.where , que elige entre dos matrices según una máscara (matriz de valores verdaderos y 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 producirá:

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
fuente
1
¿Cuál será la mejor manera si no quiero reemplazar con nada si no se cumple la condición? es decir, solo reemplace con el valor proporcionado cuando se cumpla la condición, si no, deje el número original como está ....
Abhishek Sengupta
1
para reemplazar todos los valores en a, que son más pequeños que 3 y mantener el resto como está, usea[a<3] = 0
Markus Dutschke
3

Puede crear su matriz de máscaras en un paso como este

mask_data = input_mask_data < 3

Esto crea una matriz booleana que luego se puede usar como máscara de píxeles. Tenga en cuenta que no hemos cambiado la matriz de entrada (como en su código) pero hemos creado una nueva matriz para contener los datos de la máscara; recomendaría hacerlo de esta manera.

>>> 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
fuente
1
Sí. Si el OP realmente quiere 0 y 1, podría usar .astype(int)o *1, pero una matriz de Truey Falsees tan buena como es.
DSM
-4

No estoy seguro de haber entendido su pregunta, pero si escribe:

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

Esto hará que todos los valores de los datos de máscara cuyos índices xey sean menores que 3 sean iguales a 1 y todos los demás sean iguales a 0

mamalos
fuente