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
Quelle est la manière la plus concise et la plus pythique de le faire ?
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.
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
Puisque vous voulez en fait un tableau différent qui est arr
où arr < 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<0] = 0
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