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.