Reemplace todos los elementos de Python NumPy Array que sean mayores que algún valor

191

Tengo una matriz 2D NumPy y me gustaría reemplazar todos los valores en ella mayores o iguales a un umbral T con 255.0. Que yo sepa, la forma más fundamental sería:

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. ¿Cuál es la forma más concisa y pitónica de hacer esto?

  2. ¿Hay una manera más rápida (posiblemente menos concisa y / o menos pitónica) de hacer esto?

Esto formará parte de una subrutina de ajuste de ventana / nivel para imágenes de resonancia magnética de la cabeza humana. La matriz numpy 2D son los datos de píxeles de la imagen.

NLi10Me
fuente
Para obtener más información, eche un vistazo a esta introducción a la indexación .
askewchan

Respuestas:

334

Creo que la forma más rápida y concisa de hacer esto es usar la indexación Fancy incorporada de NumPy. Si tiene un ndarraynombre arr, puede reemplazar todos los elementos >255con un valor de la xsiguiente manera:

arr[arr > 255] = x

Ejecuté esto en mi máquina con una matriz aleatoria de 500 x 500, reemplazando todos los valores> 0.5 con 5, y tomó un promedio de 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
mdml
fuente
3
Tenga en cuenta que esto modifica la matriz existente arr, en lugar de crear una resultmatriz como en el OP.
askewchan
1
¿Hay alguna manera de hacer esto no modificando Asino creando una nueva matriz?
nitrato de sodio
¿Qué haríamos si quisiéramos cambiar los valores en índices que son múltiplos de n, como a [2], a [4], a [6], a [8] ..... para n = 2?
lavee_singh
100 bucles, lo mejor de 3: 2.22 ms por bucle
dreab
55
NOTA: esto no funciona si los datos están en una lista de Python, TIENE que estar en una matriz numpy ( np.array([1,2,3])
mjp
46

Dado que realmente desea una matriz diferente que es arrdonde arr < 255, y de lo 255contrario, esto puede hacerse simplemente:

result = np.minimum(arr, 255)

Más generalmente, para un límite inferior y / o superior:

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

Si solo desea acceder a los valores superiores a 255, o algo más complicado, la respuesta de @ mtitan8 es más general, pero np.clipy np.minimum(o np.maximum) son más agradables y más rápidos para su 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

Si desea hacerlo en el lugar (es decir, modificar en arrlugar de crear result) puede usar el outparámetro de np.minimum:

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

o

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

(el out=nombre es opcional ya que los argumentos están en el mismo orden que la definición de la función).

Para la modificación en el lugar, la indexación booleana se acelera mucho (sin tener que hacer y luego modificar la copia por separado), pero aún no es tan rápido como 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

A modo de comparación, si quisiera restringir sus valores con un mínimo y un máximo, sin cliptener que hacer esto dos veces, con algo como

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

o,

a[a>255] = 255
a[a<0] = 0
askewchan
fuente
1
Muchas gracias por su comentario completo, sin embargo, np.clip y np.minimum no parecen ser lo que necesito en este caso, en el OP puede ver que el umbral T y el valor de reemplazo (255) no son necesariamente los mismos número. Sin embargo, todavía te di un voto positivo por minuciosidad. Gracias de nuevo.
NLi10Me
¿Qué haríamos si quisiéramos cambiar los valores en índices que son múltiplos de n, como a [2], a [4], a [6], a [8] ..... para n = 2?
lavee_singh
@lavee_singh, para hacer eso, puede usar la tercera parte del segmento, que generalmente se descuida: a[start:stop:step]le da los elementos de la matriz desde starthasta stop, pero en lugar de cada elemento, solo toma cada uno step(si se descuida, es 1por defecto ) Por lo tanto, para poner todos los pares a cero, podría hacerloa[::2] = 0
askewchan
Gracias. Necesitaba algo como esto, aunque lo sabía para listas simples, pero no sabía si funciona o para numpy.array.
lavee_singh
14

Creo que puedes lograr esto más rápido usando la wherefunción:

Por ejemplo, buscar elementos mayores que 0.2 en una matriz numpy y reemplazar aquellos con 0:

import numpy as np

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

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

Puede considerar usar numpy.putmask :

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

Aquí hay una comparación de rendimiento con la indexación integrada de 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
fuente
8

Otra forma es usar el np.placeque reemplaza en el lugar y funciona con matrices multidimensionales:

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
fuente
Esta es la solución que utilicé porque fue la primera que encontré. Me pregunto si hay una gran diferencia entre esto y la respuesta seleccionada arriba. ¿Qué piensas?
jonathanking
En mis pruebas muy limitadas, mi código anterior con np.place se ejecuta 2 veces más lento que el método de indexación directa de la respuesta aceptada. Es sorprendente porque hubiera pensado que np.place estaría más optimizado, pero supongo que probablemente hayan puesto más trabajo en la indexación directa.
Shital Shah
En mi caso np.placetambién fue más lento en comparación con el método incorporado, aunque se afirma lo contrario en este comentario.
riyansh.legend
3

También puede usar &, |(y / o) para obtener más flexibilidad:

valores entre 5 y 10: A[(A>5)&(A<10)]

valores mayores que 10 o menores que 5: A[(A<5)|(A>10)]

Mahdi Shahbaba
fuente