Ersetzen Sie alle Elemente des Python NumPy-Arrays, die größer als ein Wert sind

187

Ich habe ein 2D-NumPy-Array und möchte alle darin enthaltenen Werte, die größer oder gleich einem Schwellenwert T sind, durch 255,0 ersetzen. Meines Wissens wäre der grundlegendste Weg:

shape = arr.shape
result = np.zeros(shape)
for x in range(0, shape[0]):
    for y in range(0, shape[1]):
        if arr[x, y] >= T:
            result[x, y] = 255
  1. Was ist der prägnanteste und pythonischste Weg, dies zu tun?

  2. Gibt es eine schnellere (möglicherweise weniger prägnante und / oder weniger pythonische) Möglichkeit, dies zu tun?

Dies ist Teil einer Unterroutine zur Fenster- / Pegelanpassung für MRT-Scans des menschlichen Kopfes. Das 2D-Numpy-Array sind die Bildpixeldaten.

NLi10Me
quelle
Weitere Informationen finden Sie in dieser Einführung zur Indizierung .
Askewchan

Antworten:

329

Ich denke, der schnellste und prägnanteste Weg, dies zu tun, ist die Verwendung der integrierten Fancy-Indizierung von NumPy. Wenn Sie einen ndarrayNamen haben arr, können Sie alle Elemente wie folgt >255durch einen Wert ersetzen x:

arr[arr > 255] = x

Ich habe dies auf meinem Computer mit einer Zufallsmatrix von 500 x 500 ausgeführt, wobei alle Werte> 0,5 durch 5 ersetzt wurden, und es dauerte durchschnittlich 7,59 ms.

In [1]: import numpy as np
In [2]: A = np.random.rand(500, 500)
In [3]: timeit A[A > 0.5] = 5
100 loops, best of 3: 7.59 ms per loop
mdml
quelle
3
Beachten Sie, dass dadurch das vorhandene Array geändert wird arr, anstatt resultwie im OP ein Array zu erstellen .
Askewchan
1
Gibt es eine Möglichkeit, dies zu tun, indem Sie nicht ändern, Asondern ein neues Array erstellen?
Natriumnitrat
Was würden wir tun, wenn wir Werte an Indizes ändern möchten, die ein Vielfaches von n sind, wie a [2], a [4], a [6], a [8] ..... für n = 2?
lavee_singh
100 Schleifen, am besten 3: 2,22 ms pro Schleife
Dreab
5
HINWEIS: Dies funktioniert nicht, wenn sich die Daten in einer Python-Liste befinden. Sie müssen sich in einem numpy-Array ( np.array([1,2,3]) befinden
mjp
46

Da Sie tatsächlich ein anderes Array wünschen, arrwo arr < 255und 255ansonsten, kann dies einfach durchgeführt werden:

result = np.minimum(arr, 255)

Allgemeiner für eine Unter- und / oder Obergrenze:

result = np.clip(arr, 0, 255)

Wenn Sie nur auf Werte über 255 oder etwas Komplizierteres zugreifen möchten, ist die Antwort von @ mtitan8 allgemeiner, aber np.clipund np.minimum(oder np.maximum) sind für Ihren Fall schöner und viel schneller:

In [292]: timeit np.minimum(a, 255)
100000 loops, best of 3: 19.6 µs per loop

In [293]: %%timeit
   .....: c = np.copy(a)
   .....: c[a>255] = 255
   .....: 
10000 loops, best of 3: 86.6 µs per loop

Wenn Sie dies direkt tun möchten (dh ändern arrstatt erstellen result), können Sie den folgenden outParameter verwenden np.minimum:

np.minimum(arr, 255, out=arr)

oder

np.clip(arr, 0, 255, arr)

(Der out=Name ist optional, da die Argumente in derselben Reihenfolge wie die Definition der Funktion vorliegen.)

Bei direkten Änderungen beschleunigt sich die boolesche Indizierung erheblich (ohne dass die Kopie separat erstellt und anschließend geändert werden muss), ist jedoch immer noch nicht so schnell wie minimum:

In [328]: %%timeit
   .....: a = np.random.randint(0, 300, (100,100))
   .....: np.minimum(a, 255, a)
   .....: 
100000 loops, best of 3: 303 µs per loop

In [329]: %%timeit
   .....: a = np.random.randint(0, 300, (100,100))
   .....: a[a>255] = 255
   .....: 
100000 loops, best of 3: 356 µs per loop

Zum Vergleich, wenn Sie Ihre Werte sowohl auf ein Minimum als auch auf ein Maximum beschränken clipmöchten , ohne dass Sie dies zweimal tun müssten, mit so etwas wie

np.minimum(a, 255, a)
np.maximum(a, 0, a)

oder,

a[a>255] = 255
a[a<0] = 0
askewchan
quelle
1
Vielen Dank für Ihren vollständigen Kommentar, jedoch scheinen np.clip und np.minimum in diesem Fall nicht das zu sein, was ich brauche. Im OP sehen Sie, dass der Schwellenwert T und der Wiederbeschaffungswert (255) nicht unbedingt gleich sind Nummer. Trotzdem habe ich Ihnen immer noch eine Stimme für Gründlichkeit gegeben. Danke noch einmal.
NLi10Me
Was würden wir tun, wenn wir Werte an Indizes ändern möchten, die ein Vielfaches von n sind, wie a [2], a [4], a [6], a [8] ..... für n = 2?
lavee_singh
@lavee_singh, um dies zu tun, können Sie den dritten Teil des Slice verwenden, der normalerweise vernachlässigt wird: a[start:stop:step]Gibt Ihnen die Elemente des Arrays von startbis an stop, aber anstelle jedes Elements werden nur alle Elemente verwendet step(wenn dies vernachlässigt wird, ist dies 1standardmäßig der Fall) ). Also, um alle Ereignisse auf Null zu setzen, könnten Sie tuna[::2] = 0
Askewchan
Danke, ich brauchte so etwas, obwohl ich es für einfache Listen wusste, aber ich wusste nicht, ob oder wie es für numpy.array funktioniert.
lavee_singh
14

Ich denke, Sie können dies am schnellsten erreichen, indem Sie die whereFunktion verwenden:

Suchen Sie beispielsweise nach Elementen mit mehr als 0,2 in einem numpy-Array und ersetzen Sie diese durch 0:

import numpy as np

nums = np.random.rand(4,3)

print np.where(nums > 0.2, 0, nums)
Amir F.
quelle
10

Sie können die Verwendung von numpy.putmask in Betracht ziehen :

np.putmask(arr, arr>=T, 255.0)

Hier ist ein Leistungsvergleich mit der integrierten Indexierung des Numpy:

In [1]: import numpy as np
In [2]: A = np.random.rand(500, 500)

In [3]: timeit np.putmask(A, A>0.5, 5)
1000 loops, best of 3: 1.34 ms per loop

In [4]: timeit A[A > 0.5] = 5
1000 loops, best of 3: 1.82 ms per loop
lev
quelle
8

Eine andere Möglichkeit ist die Verwendung, np.placedie direkt vor Ort ersetzt wird und mit multidimentionalen Arrays funktioniert:

import numpy as np

# create 2x3 array with numbers 0..5
arr = np.arange(6).reshape(2, 3)

# replace 0 with -10
np.place(arr, arr == 0, -10)
Shital Shah
quelle
Dies ist die Lösung, die ich verwendet habe, weil es die erste war, auf die ich gestoßen bin. Ich frage mich, ob es einen großen Unterschied zwischen dieser und der oben ausgewählten Antwort gibt. Was denken Sie?
Jonathanking
In meinen sehr begrenzten Tests läuft mein obiger Code mit np.place 2X langsamer als die Methode der direkten Indizierung der akzeptierten Antwort. Es ist überraschend, weil ich gedacht hätte, np.place wäre optimierter, aber ich denke, sie haben wahrscheinlich mehr Arbeit in die direkte Indizierung gesteckt.
Shital Shah
In meinem Fall np.placewar es auch langsamer als bei der eingebauten Methode, obwohl in diesem Kommentar das Gegenteil behauptet wird .
riyansh.legend
3

Sie können auch verwendet werden &, |(und / oder) für mehr Flexibilität:

Werte zwischen 5 und 10: A[(A>5)&(A<10)]

Werte größer als 10 oder kleiner als 5: A[(A<5)|(A>10)]

Mahdi Shahbaba
quelle