If I have an array like
a = np.array([2, 3, -1, -4, 3])
I want to set all the negative elements to zero: [2, 3, 0, 0, 3]
. How to do it with numpy without an explicit for? I need to use the modified a
in a computation, for example
c = a * b
where b
is another array with the same length of the original a
Conclusion
import num开发者_开发技巧py 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.38629984856
- 0.516846179962 <- faster a.clip(min=0);
- 0.615426063538
- 0.944557905197
- 51.7364809513
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
Based on my answer here, using np.maximum is the fastest possible way.
a = np.random.random(1000) - 0.5
%%timeit
a_ = a.copy()
a_ = np.maximum(a_,0)
# 15.6 µs ± 2.14 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit
a_ = a.copy()
a_ = a_.clip(min=0)
# 54.2 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
And just for the sake of comprehensiveness, I would like to add the use of the Heaviside function (or a step function) to achieve a similar outcome as follows:
Let say for continuity we have
a = np.array([2, 3, -1, -4, 3])
Then using a step function np.heaviside()
one can try
b = a * np.heaviside(a, 0)
Note something interesting in this operation because the negative signs are preserved! Not very ideal for most situations I would say.
This can then be corrected for by
b = abs(b)
So this is probably a rather long way to do it without invoking some loop.
精彩评论