How to transform negative elements to zero without a loop?

a = a.clip(min=0)

I would do this:

a[a < 0] = 0

If you want to keep the original a and only set the negative elements to zero in a copy, you can copy the array first:

c = a.copy()
c[c < 0] = 0

Another trick is to use multiplication. This actually seems to be much faster than every other method here. For example

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

or

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

I ran tests with timeit, pre-calculating the the < and > because some of these modify in-place and that would greatly effect results. In all cases a was np.random.uniform(-1, 1, 20000000) but with negatives already set to 0 but L = a < 0 and G = a > 0 before a was changed. The clip is relatively negatively impacted since it doesn't get to use L or G (however calculating those on the same machine took only 17ms each, so it is not the major cause of speed difference).

%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`)

When choosing to penalize the in-place methods instead of clip, the following timings come up:

%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

Non in-place methods are penalized by 20ms (the time required to calculate a>0 or a<0) and the in-place methods are penalize 73-83 ms (so it takes about 53-63ms to do b.copy()).

Overall the multiplication methods are much faster than clip. If not in-place, it is 1.5x faster. If you can do it in-place then it is 2.75x faster.


Use where

a[numpy.where(a<0)] = 0