Remplacez tous les éléments d'un tableau Python NumPy qui sont supérieurs à une certaine valeur.

J'ai un tableau NumPy 2D et je voudrais remplacer toutes les valeurs supérieures ou égales à un seuil T par 255.0. À ma connaissance, la méthode la plus fondamentale serait la suivante :

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. Quelle est la manière la plus concise et la plus pythique de le faire ?

  2. Existe-t-il un moyen plus rapide (peut-être moins concis et/ou moins pythonique) de faire cela ?

Ceci fera partie d'une sous-routine d'ajustement de fenêtre/niveau pour les scans IRM de la tête humaine. Le tableau numpy 2D est le pixel de l'image.

Solution

Je pense que le moyen le plus rapide et le plus concis de le faire est d'utiliser l'indexation fantaisiste intégrée de NumPy. Si vous avez un ndarray nommé arr, vous pouvez remplacer tous les éléments >255 par une valeur x comme suit :

arr[arr > 255] = x

J'ai exécuté cette opération sur ma machine avec une matrice aléatoire de 500 x 500, en remplaçant toutes les valeurs >0.5 par 5, et cela a pris en moyenne 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
Commentaires (9)

Puisque vous voulez en fait un tableau différent qui est arrarr < 255, et 255 sinon, cela peut être fait simplement :

result = np.minimum(arr, 255)

Plus généralement, pour une borne inférieure et/ou supérieure :

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

Si vous voulez juste accéder aux valeurs supérieures à 255, ou quelque chose de plus compliqué, la réponse de @mtitan8'est plus générale, mais np.clip et np.minimum (ou np.maximum) sont plus jolis et beaucoup plus rapides pour votre cas :

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 vous voulez le faire in-place (c'est-à-dire modifier arr au lieu de créer result), vous pouvez utiliser le paramètre out de np.minimum :

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

ou

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

(le nom out= est facultatif puisque les arguments sont dans le même ordre que la définition de la fonction).

Pour la modification en place, l'indexation booléenne accélère beaucoup (sans avoir à faire et à modifier la copie séparément), mais n'est toujours pas aussi rapide que 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 titre de comparaison, si vous vouliez restreindre vos valeurs avec un minimum ainsi qu'un maximum, sans clip vous devriez le faire deux fois, avec quelque chose comme

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

ou,


a[a>255] = 255
a[a
Commentaires (4)

Vous pouvez envisager d'utiliser [numpy.putmask][1] :

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

Voici une comparaison des performances avec l'indexation intégrée 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

[1] : http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.putmask.html

Commentaires (1)