Ho un array 2D NumPy e vorrei sostituire tutti i valori in esso maggiori o uguali a una soglia T con 255.0. Per quanto ne so, il modo più fondamentale sarebbe:
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
Qual è il modo più conciso e pitonico per farlo?
C'è un modo più veloce (possibilmente meno conciso e/o meno pitonico) per farlo?
Questo farà parte di una subroutine di regolazione della finestra/livello per le scansioni MRI della testa umana. L'array 2D numpy è i dati dei pixel dell'immagine.
Penso che il modo più veloce e conciso per farlo sia quello di usare l'indicizzazione Fancy integrata in NumPy. Se avete un ndarray
chiamato arr
, potete sostituire tutti gli elementi >255
con un valore x
come segue:
arr[arr > 255] = x
Ho eseguito questo sulla mia macchina con una matrice casuale 500 x 500, sostituendo tutti i valori >0.5 con 5, e ci sono voluti in media 7.59ms.
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
-- language-all: lang-py -->
Poiché in realtà vuoi un array diverso che sia arr
dove arr < 255
, e 255
altrimenti, questo può essere fatto semplicemente:
result = np.minimum(arr, 255)
Più in generale, per un limite inferiore e/o superiore:
result = np.clip(arr, 0, 255)
Se vuoi solo accedere ai valori oltre 255, o qualcosa di più complicato, la risposta di @mtitan8'è più generale, ma np.clip
e np.minimum
(o np.maximum
) sono più piacevoli e molto più veloci per il tuo caso:
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
Se vuoi farlo in-place (cioè modificare arr
invece di creare result
) puoi usare il parametro out
di np.minimum
:
np.minimum(arr, 255, out=arr)
oppure
np.clip(arr, 0, 255, arr)
(il nome out=
è opzionale poiché gli argomenti sono nello stesso ordine della definizione della funzione).
Per la modifica in-place, l'indicizzazione booleana accelera molto (senza dover fare e poi modificare la copia separatamente), ma non è ancora veloce come 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
Per confronto, se voleste limitare i vostri valori con un minimo e un massimo, senza clip
dovreste farlo due volte, con qualcosa come
np.minimum(a, 255, a)
np.maximum(a, 0, a)
oppure,
a[a>255] = 255
a[a<0] = 0
Potete considerare di usare numpy.putmask:
np.putmask(arr, arr>=T, 255.0)
Ecco un confronto delle prestazioni con l'indicizzazione incorporata di 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