Remplacez tous les éléments de Python NumPy Array qui sont supérieurs à une certaine valeur

191

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, le moyen le plus fondamental serait:

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 pythonique de faire cela?

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

Cela fera partie d'un sous-programme d'ajustement de la fenêtre / du niveau pour les examens IRM de la tête humaine. Le tableau numpy 2D correspond aux données de pixels de l'image.

NLi10Me
la source
Pour plus d'informations, jetez un œil à cette introduction à l'indexation .
askewchan

Réponses:

334

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

arr[arr > 255] = x

J'ai exécuté ceci 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,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
mdml
la source
3
Notez que cela modifie le tableau existant arr, au lieu de créer un resulttableau comme dans l'OP.
askewchan
1
Existe-t-il un moyen de le faire en ne modifiant pas Amais en créant un nouveau tableau?
sodiumnitrate
Que ferions-nous, si nous voulions changer les valeurs à des index qui sont multiples de n donné, comme a [2], a [4], a [6], a [8] ..... pour n = 2?
lavee_singh
100 boucles, meilleur de 3: 2,22 ms par boucle
dreab
5
REMARQUE: cela ne fonctionne pas si les données sont dans une liste python, elles DOIVENT être dans un tableau numpy ( np.array([1,2,3])
mjp
46

Puisque vous voulez en fait un tableau différent qui soit arrarr < 255, et 255sinon, 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 souhaitez simplement 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.clipet np.minimum(ou np.maximum) sont plus agréables 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 sur place (c'est-à-dire modifier arrau lieu de créer result), vous pouvez utiliser le outparamètre de np.minimum:

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

ou

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

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

Pour la modification sur place, l'indexation booléenne accélère beaucoup (sans avoir à faire puis 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

À titre de comparaison, si vous vouliez restreindre vos valeurs avec un minimum ainsi qu'un maximum, sans clipvous auriez à 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
Askewchan
la source
1
Merci beaucoup pour votre commentaire complet, cependant np.clip et np.minimum ne semblent pas être ce dont j'ai besoin dans ce cas, dans l'OP vous voyez que le seuil T et la valeur de remplacement (255) ne sont pas forcément les mêmes nombre. Cependant, je vous ai quand même donné un vote pour la rigueur. Merci encore.
NLi10Me
Que ferions-nous, si nous voulions changer les valeurs à des index qui sont multiples de n donné, comme a [2], a [4], a [6], a [8] ..... pour n = 2?
lavee_singh
@lavee_singh, pour ce faire, vous pouvez utiliser la troisième partie de la tranche, qui est généralement négligée: a[start:stop:step]vous donne les éléments du tableau de startà stop, mais au lieu de chaque élément, il ne prend que tous step(si négligé, c'est 1par défaut ). Donc, pour mettre tous les égales à zéro, vous pouvez le fairea[::2] = 0
askewchan
Merci j'avais besoin de quelque chose, comme ça, même si je le savais pour des listes simples, mais je ne savais pas si ou comment cela fonctionnait pour numpy.array.
lavee_singh
14

Je pense que vous pouvez y parvenir le plus rapidement en utilisant la wherefonction:

Par exemple, rechercher des éléments supérieurs à 0,2 dans un tableau numpy et remplacer ceux par 0:

import numpy as np

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

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

Vous pouvez envisager d'utiliser numpy.putmask :

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
lev
la source
8

Une autre façon consiste à utiliser np.placequi effectue un remplacement sur place et fonctionne avec des tableaux multidimensionnels:

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
la source
C'est la solution que j'ai utilisée car c'est la première que j'ai rencontrée. Je me demande s'il y a une grande différence entre cela et la réponse choisie ci-dessus. Qu'est-ce que tu penses?
jonathanking
Dans mes tests très limités, mon code ci-dessus avec np.place s'exécute 2X plus lentement que la méthode d'indexation directe de la réponse acceptée. C'est surprenant car j'aurais pensé que np.place serait plus optimisé mais je suppose qu'ils ont probablement mis plus de travail sur l'indexation directe.
Shital Shah
Dans mon cas, np.placec'était également plus lent par rapport à la méthode intégrée, bien que le contraire soit affirmé dans ce commentaire.
riyansh.legend
3

Vous pouvez également utiliser &, |(et / ou) pour plus de flexibilité:

valeurs entre 5 et 10: A[(A>5)&(A<10)]

valeurs supérieures à 10 ou inférieures à 5: A[(A<5)|(A>10)]

Mahdi Shahbaba
la source