Wymiana elementów Numpy, jeśli warunek jest spełniony

95

Mam dużą tablicę numpy, którą muszę manipulować, aby każdy element był zmieniany na 1 lub 0, jeśli warunek zostanie spełniony (będzie później używany jako maska ​​pikseli). W tablicy jest około 8 milionów elementów, a moja obecna metoda zajmuje zbyt dużo czasu dla potoku redukcji:

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

Czy jest jakaś funkcja numpy, która przyspieszyłaby to?

ChrisFro
źródło
1
Co chcesz, żeby się stało mask_data[y,x]==3?
DSM
Słuszna uwaga, to nadal byłby zły piksel. Zmienię warunek naif mask_data[y,x]>=3:
ChrisFro

Odpowiedzi:

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

Możesz to skrócić za pomocą:

>>> c = (a < 3).astype(int)
Steve Barnes
źródło
2
jak to zrobić z określonymi kolumnami bez wycinania niektórych kolumn i ponownego przypisywania? na przykład tylko elementy w kolumnach [2, 3] powinny zmieniać wartość po spełnieniu warunków, podczas gdy inne kolumny nie zmienią się bez względu na to, czy warunki są spełnione, czy nie.
kuixiong
Prawda, ale tylko w przypadku zer i jedynek. Zobacz bardziej ogólną odpowiedź poniżej (kosztem wydajności)
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]])
>>>

Zobacz np. Indeksowanie za pomocą tablic logicznych .

ev-br
źródło
4
świetne rzeczy, dzięki! Jeśli chcesz odnieść się do wartości, którą zmieniasz, możesz użyć czegoś takiego jak a[a > 3] = -101+a[a > 3].
pexmar
2
@pexmar Chociaż jeśli to zrobisz a[a > 3] = -101+a[a > 3]zamiast a[a > 3] += -101ciebie, najprawdopodobniej napotkasz wyciek pamięci.
Samuel Prevost
2
jak odnosisz się do zmienianej wartości, o co pytał pexmar?
Juan
34

Najszybciej (i elastycznych) sposobem jest użycie np.where , które wybiera pomiędzy dwoma macierzy zgodnie z maską (tablica wartości rzeczywistych i fałszywych)

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)

który wyprodukuje:

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
źródło
2
co będzie najlepszym sposobem, jeśli nie chcę niczego zastępować, jeśli warunek nie jest spełniony? tzn. zamień na wartość dostarczoną tylko wtedy, gdy warunek jest spełniony, jeśli nie, pozostaw oryginalny numer taki, jaki jest ....
Abhishek Sengupta
2
aby zamienić wszystkie wartości w a, które są mniejsze od 3 i pozostawić resztę tak, jak jest, użyja[a<3] = 0
Markus Dutschke
3

Możesz stworzyć swoją tablicę masek w jednym kroku

mask_data = input_mask_data < 3

Tworzy to tablicę logiczną, która może być następnie używana jako maska ​​pikseli. Zauważ, że nie zmieniliśmy tablicy wejściowej (tak jak w twoim kodzie), ale utworzyliśmy nową tablicę do przechowywania danych maski - zalecałbym zrobienie tego w ten sposób.

>>> 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
źródło
1
Tak. Jeśli OP naprawdę chce 0 i 1, mógłby użyć .astype(int)lub *1, ale tablica Truei Falsejest tak samo dobra, jak jest.
DSM
-4

Nie jestem pewien, czy zrozumiałem Twoje pytanie, ale jeśli napiszesz:

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

Spowoduje to, że wszystkie wartości danych maski, których indeksy x i y są mniejsze niż 3, będą równe 1, a wszystkie pozostałe będą równe 0

mamalos
źródło