Sostituisci tutti gli elementi dell'array Python NumPy che sono maggiori di un valore


191

Ho un array NumPy 2D 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
  1. Qual è il modo più conciso e pitonico per farlo?

  2. Esiste un modo più rapido (forse meno conciso e / o meno pitonico) per farlo?

Questo farà parte di una subroutine di regolazione finestra / livello per scansioni MRI della testa umana. L'array numpy 2D sono i dati pixel dell'immagine.


Per ulteriori informazioni, dai un'occhiata a questa introduzione all'indicizzazione .
askewchan,

Risposte:


334

Penso che sia il modo più veloce che più conciso per farlo sia usare l'indicizzazione Fancy integrata di NumPy. Se hai un ndarraynome arr, puoi sostituire tutti gli elementi >255con un valore xcome 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,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

3
Si noti che ciò modifica l'array esistente arr, invece di creare un resultarray come nell'OP.
askewchan,

1
C'è un modo per farlo non modificando Ama creando un nuovo array?
Sodio nitrato

Cosa faremmo se volessimo cambiare valori in corrispondenza di indici multipli di un dato n, come un [2], un [4], un [6], un [8] ..... per n = 2?
lavee_singh

100 loop, meglio di 3: 2,22 ms per loop
dreab,

5
NOTA: questo non funziona se i dati sono in un elenco Python, DEVE essere in un array numpy ( np.array([1,2,3])
mjp

46

Dal momento che in realtà vuoi un array diverso che è arrdove arr < 255, e 255altrimenti, 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 a valori superiori a 255 o qualcosa di più complicato, la risposta di @ mtitan8 è più generale, ma np.clipe np.minimum(o np.maximum) sono più belli 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 sul posto (es. Modifica arrinvece di creare result) puoi usare il outparametro di np.minimum:

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

o

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

(il out=nome è facoltativo poiché gli argomenti sono nello stesso ordine della definizione della funzione.)

Per la modifica sul posto, l'indicizzazione booleana accelera molto (senza dover effettuare e quindi 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 fare un confronto, se vuoi limitare i tuoi valori con un minimo e un massimo, senza che clipdovresti farlo due volte, con qualcosa di simile

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

o,

a[a>255] = 255
a[a<0] = 0

1
Grazie mille per il tuo commento completo, tuttavia np.clip e np.minimum non sembrano essere ciò di cui ho bisogno in questo caso, nell'OP vedi che la soglia T e il valore di sostituzione (255) non sono necessariamente gli stessi numero. Tuttavia ti ho ancora dato un voto positivo per completezza. Grazie ancora.
NLi10Me

Cosa faremmo se volessimo cambiare valori in corrispondenza di indici multipli di un dato n, come un [2], un [4], un [6], un [8] ..... per n = 2?
lavee_singh

@lavee_singh, per farlo, puoi usare la terza parte della sezione, che di solito viene trascurata: a[start:stop:step]ti dà gli elementi dell'array da starta stop, ma invece di ogni elemento, prende solo ogni step(se trascurato, è 1di default ). Quindi, per impostare tutti i pari a zero, potresti farloa[::2] = 0
askewchan

Grazie, avevo bisogno di qualcosa del genere, anche se lo sapevo per semplici elenchi, ma non sapevo se o come funzionasse per numpy.array.
lavee_singh

14

Penso che tu possa ottenere questo il più rapidamente usando la wherefunzione:

Ad esempio, cercando elementi maggiori di 0,2 in un array intorpidito e sostituendo quelli con 0:

import numpy as np

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

print np.where(nums > 0.2, 0, nums)

10

Puoi prendere in considerazione l'uso di numpy.putmask :

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

Ecco un confronto delle prestazioni con l'indicizzazione integrata 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

8

Un altro modo è quello di utilizzare la np.placesostituzione sul posto e funziona con array multidimensionali:

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)

Questa è la soluzione che ho usato perché è stata la prima che ho incontrato. Mi chiedo se ci sia una grande differenza tra questa e la risposta selezionata sopra. Cosa ne pensi?
jonathanking

Nei miei test molto limitati, il mio codice sopra con np.place esegue 2 volte più lentamente del metodo di indicizzazione diretta della risposta accettata. È sorprendente perché avrei pensato che np.place sarebbe stato più ottimizzato, ma credo che probabilmente abbiano dedicato più lavoro all'indicizzazione diretta.
Shital Shah,

Nel mio caso è np.placestato anche più lento rispetto al metodo integrato, sebbene in questo commento sia affermato il contrario .
riyansh.legend,

3

Puoi anche usare &, |(e / o) per una maggiore flessibilità:

valori tra 5 e 10: A[(A>5)&(A<10)]

valori maggiori di 10 o inferiori a 5: A[(A<5)|(A>10)]

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.