web-dev-qa-db-fra.com

Comment transformer des éléments négatifs à zéro sans boucle?

Si j'ai un tableau comme

a = np.array([2, 3, -1, -4, 3])

Je veux mettre tous les éléments négatifs à zéro: [2, 3, 0, 0, 3]. Comment le faire avec numpy sans explicite pour? J'ai besoin d'utiliser le a modifié dans un calcul, par exemple

c = a * b

b est un autre tableau de la même longueur que l'original a

Conclusion

import numpy as np
from time import time

a = np.random.uniform(-1, 1, 20000000)
t = time(); b = np.where(a>0, a, 0); print ("1. ", time() - t)
a = np.random.uniform(-1, 1, 20000000)
t = time(); b = a.clip(min=0); print ("2. ", time() - t)
a = np.random.uniform(-1, 1, 20000000)
t = time(); a[a < 0] = 0; print ("3. ", time() - t)
a = np.random.uniform(-1, 1, 20000000)
t = time(); a[np.where(a<0)] = 0; print ("4. ", time() - t)
a = np.random.uniform(-1, 1, 20000000)
t = time(); b = [max(x, 0) for x in a]; print ("5. ", time() - t)
  1. 1.38629984856
  2. 0,516846179962 <- un a.clip plus rapide (min = 0);
  3. 0,615426063538
  4. 0,944557905197
  5. 51,7364809513
42
Ruggero Turra
a = a.clip(min=0)
80
Ruggero Turra

Je ferais ceci:

a[a < 0] = 0

Si vous souhaitez conserver le a d'origine et ne définir que les éléments négatifs à zéro dans une copie, vous pouvez d'abord copier le tableau:

c = a.copy()
c[c < 0] = 0
12
David Z

Une autre astuce consiste à utiliser la multiplication. Cela semble en fait être beaucoup plus rapide que toutes les autres méthodes ici. Par exemple

b = a*(a>0) # copies data

ou

a *= (a>0) # in-place zero-ing

J'ai exécuté des tests avec timeit, en pré-calculant le <et> car certains d'entre eux se modifient sur place et cela affecterait grandement les résultats. Dans tous les cas, a était np.random.uniform(-1, 1, 20000000) mais avec des négatifs déjà définis sur 0 mais L = a < 0 Et G = a > 0 Avant que a ne soit modifié. Le clip est relativement négativement impacté car il ne peut pas utiliser L ou G (cependant le calcul de ceux sur la même machine n'a pris que 17 ms chacun, donc ce n'est pas le cause principale de différence de vitesse).

%timeit b = np.where(G, a, 0)  # 132ms  copies
%timeit b = a.clip(min=0)      # 165ms  copies
%timeit a[L] = 0               # 158ms  in-place
%timeit a[np.where(L)] = 0     # 122ms  in-place
%timeit b = a*G                # 87.4ms copies
%timeit np.multiply(a,G,a)     # 40.1ms in-place (normal code would use `a*=G`)

Lorsque vous choisissez de pénaliser les méthodes sur place au lieu de clip, les délais suivants apparaissent:

%timeit b = np.where(a>0, a, 0)             # 152ms
%timeit b = a.clip(min=0)                   # 165ms
%timeit b = a.copy(); b[a<0] = 0            # 231ms
%timeit b = a.copy(); b[np.where(a<0)] = 0  # 205ms
%timeit b = a*(a>0)                         # 108ms
%timeit b = a.copy(); b*=a>0                # 121ms

Les méthodes non en place sont pénalisées de 20 ms (le temps nécessaire pour calculer a>0 Ou a<0) Et les méthodes en place pénalisent 73 à 83 ms (il faut donc environ 53 à 63 ms pour faire b.copy()).

Globalement, les méthodes de multiplication sont beaucoup plus rapides que clip. S'il n'est pas en place, c'est 1.5x plus rapide. Si vous pouvez le faire sur place, c'est 2.75x plus rapide.

9
coderforlife

Utilisez

a[numpy.where(a<0)] = 0
5
Adam Schmideg

Et juste pour des raisons d'exhaustivité, je voudrais ajouter l'utilisation de la fonction Heaviside (ou une fonction de pas) pour obtenir un résultat similaire comme suit:

Disons pour la continuité que nous avons

a = np.array([2, 3, -1, -4, 3])

Ensuite, en utilisant une fonction pas à pas np.heaviside() on peut essayer

b = a * np.heaviside(a, 0)

Notez quelque chose d'intéressant dans cette opération car les signes négatifs sont préservés! Pas très idéal pour la plupart des situations, je dirais.

Cela peut ensuite être corrigé par

b = abs(b)

C'est donc probablement un assez long chemin à parcourir sans invoquer de boucle.

0
Kris Stern