Ersetzen von Numpy-Elementen, wenn die Bedingung erfüllt ist

90

Ich habe ein großes Numpy-Array, das ich bearbeiten muss, damit jedes Element entweder auf 1 oder 0 geändert wird, wenn eine Bedingung erfüllt ist (wird später als Pixelmaske verwendet). Das Array enthält ungefähr 8 Millionen Elemente, und meine derzeitige Methode dauert zu lange für die Reduktionspipeline:

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

Gibt es eine Numpy-Funktion, die dies beschleunigen würde?

ChrisFro
quelle
1
Was möchten Sie tun, wenn mask_data[y,x]==3?
DSM
Guter Punkt, das wäre immer noch ein schlechtes Pixel. Ich werde die Bedingung ändern zuif mask_data[y,x]>=3:
ChrisFro

Antworten:

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

Sie können dies verkürzen mit:

>>> c = (a < 3).astype(int)
Steve Barnes
quelle
2
Wie kann dies mit bestimmten Spalten erreicht werden, ohne jemals einige Spalten herauszuschneiden und dann wieder zuzuweisen? Beispielsweise sollten nur Elemente in Spalten [2, 3] den Wert ändern, wenn Bedingungen erfüllt sind, während andere Spalten sich nicht ändern, unabhängig davon, ob Bedingungen erfüllt sind oder nicht.
Kuixiong
Richtig, aber nur für den Fall von Nullen und Einsen. Siehe allgemeinere Antwort unten (zu Effizienzkosten)
borgr
86
>>> 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]])
>>>

Siehe z. B. Indizieren mit booleschen Arrays .

ev-br
quelle
3
tolles Zeug, danke! Wenn Sie sich auf den Wert beziehen möchten, den Sie ändern, können Sie so etwas wie verwenden a[a > 3] = -101+a[a > 3].
Pexmar
1
@pexmar Wenn Sie dies tun, a[a > 3] = -101+a[a > 3]werden a[a > 3] += -101Sie höchstwahrscheinlich mit einem Speicherverlust konfrontiert sein.
Samuel Prevost
1
Wie verweisen Sie auf den Wert, den Sie ändern, als Pexmar gefragt hat?
Juan
33

Der schnellste (und flexibelste) Weg ist die Verwendung von np.where , bei dem zwischen zwei Arrays gemäß einer Maske (Array von wahren und falschen Werten) gewählt wird:

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)

was produzieren wird:

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
quelle
1
Was ist der beste Weg, wenn ich nichts ersetzen möchte, wenn die Bedingung nicht erfüllt ist? dh nur durch den angegebenen Wert ersetzen, wenn die Bedingung erfüllt ist, wenn die ursprüngliche Nummer nicht unverändert bleibt ...
Abhishek Sengupta
1
Verwenden Siea[a<3] = 0
Markus Dutschke
3

Sie können Ihr Maskenarray in einem Schritt wie folgt erstellen

mask_data = input_mask_data < 3

Dadurch wird ein boolesches Array erstellt, das dann als Pixelmaske verwendet werden kann. Beachten Sie, dass wir das Eingabearray nicht geändert haben (wie in Ihrem Code), sondern ein neues Array erstellt haben, das die Maskendaten enthält. Ich würde empfehlen, dies auf diese Weise zu tun.

>>> 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
quelle
1
Ja. Wenn der OP wirklich Nullen und Einsen will, könnte er .astype(int)oder verwenden *1, aber ein Array von Trueund Falseist genauso gut wie es ist.
DSM
-4

Ich bin nicht sicher, ob ich Ihre Frage verstanden habe, aber wenn Sie schreiben:

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

Dadurch werden alle Werte von Maskendaten, deren x- und y-Index kleiner als 3 ist, gleich 1 und alle übrigen gleich 0

Mamalos
quelle