如何在不使用循环的情况下将负元素变为零?

72 投票
6 回答
89922 浏览
提问于 2025-04-16 02:12

如果我有一个数组,比如说:

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

我想把所有负数的元素都变成零,变成这样:[2, 3, 0, 0, 3]。我想知道怎么用numpy做到这一点,而不使用显式的循环。因为我需要用修改后的a进行计算,比如说:

c = a * b

这里b是另一个和原始a长度相同的数组。

结论

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 <- 用a.clip(min=0)会更快;
  3. 0.615426063538
  4. 0.944557905197
  5. 51.7364809513

6 个回答

16

还有一个小技巧就是使用乘法。实际上,这种方法比这里的其他方法都要快很多。例如:

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

或者:

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

我用timeit进行了测试,事先计算了<和>,因为有些方法是直接在原地修改的,这会大大影响结果。在所有情况下,a都是通过np.random.uniform(-1, 1, 20000000)生成的,但负数已经被设置为0,之后才计算L = a < 0G = a > 0。由于clip没有使用LG,所以它的性能受到了一定影响(不过在同一台机器上计算这些只花了17毫秒,所以这并不是速度差异的主要原因)。

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

在选择惩罚原地修改的方法而不是clip时,得到的时间如下:

%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

非原地的方法会多花20毫秒(这是计算a>0a<0所需的时间),而原地的方法则会多花73到83毫秒(所以执行b.copy()大约需要53到63毫秒)。

总体来说,乘法的方法比clip快很多。如果不是原地修改,速度快1.5倍;如果可以原地修改,速度快2.75倍

26

我会这样做:

a[a < 0] = 0

如果你想保留原来的 a 数组,并且只把负数的元素在一个副本中设置为零,你可以先复制这个数组:

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

在编程中,有时候我们需要处理一些数据,比如从一个地方获取数据,然后在另一个地方使用这些数据。这个过程就像是把水从一个水桶倒到另一个水桶里。

有些时候,我们会遇到一些问题,比如数据的格式不对,或者数据不完整。这就像是你在倒水的时候,发现水桶有个洞,水流出来了,导致你倒不满。

为了避免这些问题,我们可以使用一些工具和方法来确保数据的质量。就像在倒水之前,先检查水桶有没有破损,确保水能顺利倒入。

总之,处理数据就像是一个小实验,我们需要仔细观察,确保每一步都能顺利进行,这样才能得到我们想要的结果。

a = a.clip(min=0)

撰写回答