Zastąp wszystkie elementy tablicy Python NumPy, które są większe niż pewna wartość

190

Mam tablicę 2D NumPy i chciałbym zastąpić w niej wszystkie wartości większe lub równe progowi T o wartości 255.0. Według mojej wiedzy najbardziej fundamentalnym sposobem byłoby:

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. Jaki jest najbardziej zwięzły i pytonowy sposób na zrobienie tego?

  2. Czy istnieje szybszy (być może mniej zwięzły i / lub mniej pytoniczny) sposób na zrobienie tego?

Będzie to część podprogramu regulacji okna / poziomu dla skanów MRI ludzkiej głowy. Tablica numpy 2D to dane pikseli obrazu.

NLi10Me
źródło
Aby uzyskać więcej informacji, zapoznaj się z tym wprowadzeniem do indeksowania .
askewchan

Odpowiedzi:

332

Myślę, że najszybszym i najbardziej zwięzłym sposobem na zrobienie tego jest użycie wbudowanego indeksowania NumPy Fancy. Jeśli masz ndarraynazwę arr, możesz zastąpić wszystkie elementy >255wartością xw następujący sposób:

arr[arr > 255] = x

Uruchomiłem to na mojej maszynie z losową matrycą 500 x 500, zastępując wszystkie wartości> 0,5 wartością 5, i zajęło to średnio 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
źródło
3
Zauważ, że modyfikuje to istniejącą tablicę arr, zamiast tworzyć resulttablicę jak w OP.
askewchan
1
Czy istnieje sposób, aby to zrobić, nie modyfikując, Aale tworząc nową tablicę?
azotan sodu
Co byśmy zrobili, gdybyśmy chcieli zmienić wartości przy indeksach, które są wielokrotnościami danego n, np. [2], a [4], a [6], a [8] ..... dla n = 2?
lavee_singh
100 pętli, najlepiej 3: 2,22 ms na pętlę
dreab
5
UWAGA: to nie działa, jeśli dane znajdują się na liście python, MUSI znajdować się w tablicy numpy ( np.array([1,2,3])
mjp
46

Ponieważ tak naprawdę potrzebujesz innej tablicy, która jest arrtam arr < 255, gdzie 255indziej, można to zrobić po prostu:

result = np.minimum(arr, 255)

Mówiąc bardziej ogólnie, dla dolnej i / lub górnej granicy:

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

Jeśli chcesz uzyskać dostęp do wartości powyżej 255 lub coś bardziej skomplikowanego, odpowiedź @ mtitan8 jest bardziej ogólna, ale np.clipi np.minimum(lub np.maximum) jest ładniejsza i znacznie szybsza w twoim przypadku:

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

Jeśli chcesz to zrobić w miejscu (tj. Zmodyfikować arrzamiast tworzyć result), możesz użyć outparametru np.minimum:

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

lub

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

( out=nazwa jest opcjonalna, ponieważ argumenty w tej samej kolejności, co definicja funkcji).

W przypadku modyfikacji na miejscu indeksowanie logiczne znacznie przyspiesza (bez konieczności wykonywania, a następnie modyfikowania kopii osobno), ale nadal nie jest tak szybkie, jak 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

Dla porównania, jeśli chcesz ograniczyć swoje wartości z minimum, jak i maksimum, bez clipkonieczności robienia tego dwa razy, z czymś takim jak

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

lub,

a[a>255] = 255
a[a<0] = 0
askewchan
źródło
1
Bardzo dziękuję za cały komentarz, jednak np. Klip i np. Minimum nie wydają się być tym, czego potrzebuję w tym przypadku, w PO widać, że próg T i wartość zastępcza (255) niekoniecznie są takie same numer. Nadal jednak oddałem głos za dokładnością. Dzięki jeszcze raz.
NLi10Me
Co byśmy zrobili, gdybyśmy chcieli zmienić wartości przy indeksach, które są wielokrotnościami danego n, np. [2], a [4], a [6], a [8] ..... dla n = 2?
lavee_singh
@lavee_singh, aby to zrobić, możesz użyć trzeciej części plastra, która jest zwykle zaniedbywana: a[start:stop:step]daje elementy tablicy od startdo stop, ale zamiast każdego elementu, zajmuje tylko każdy step(jeśli zostanie pominięty, 1domyślnie jest ). Aby więc wyzerować wszystkie a[::2] = 0
znaki
Dzięki, potrzebowałem czegoś takiego, chociaż znałem to dla prostych list, ale nie wiedziałem, czy i jak to działa dla numpy.array.
lavee_singh
14

Myślę, że możesz to zrobić najszybciej, używając wherefunkcji:

Na przykład szukanie elementów większych niż 0,2 w tablicy numpy i zamiana tych na 0:

import numpy as np

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

print np.where(nums > 0.2, 0, nums)
Amir F.
źródło
10

Możesz rozważyć użycie numpy.putmask :

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

Oto porównanie wydajności z wbudowanym indeksowaniem 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
lew
źródło
8

Innym sposobem jest użycie, np.placektóre zastępuje w miejscu i działa z tablicami wielowymiarowymi:

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
źródło
To jest rozwiązanie, którego użyłem, ponieważ było to pierwsze, z którym się spotkałem. Zastanawiam się, czy istnieje duża różnica między tym a wybraną odpowiedzią powyżej. Co myślisz?
jonathanking
W moich bardzo ograniczonych testach mój powyższy kod z np.place działa 2 razy wolniej niż metoda bezpośredniego indeksowania przyjętej odpowiedzi. To zaskakujące, ponieważ myślałem, że np. Miejsce byłoby bardziej zoptymalizowane, ale myślę, że prawdopodobnie włożyli więcej pracy w bezpośrednie indeksowanie.
Shital Shah
W moim przypadku np.placebył również wolniejszy w porównaniu do metody wbudowanej, chociaż w tym komentarzu twierdzi się coś przeciwnego.
riyansh.legend
3

Możesz także użyć &, |(i / lub) dla większej elastyczności:

wartości od 5 do 10: A[(A>5)&(A<10)]

wartości większe niż 10 lub mniejsze niż 5: A[(A<5)|(A>10)]

Mahdi Shahbaba
źródło