运行时警告:如何避免除零错误?PYTHON, NUMPY

13 投票
5 回答
53520 浏览
提问于 2025-04-18 15:41

我遇到了一个运行时警告:在除法中遇到了无效值。

 import numpy
 a = numpy.random.rand((1000000, 100))
 b = numpy.random.rand((1,100))
 dots = numpy.dot(b,a.T)/numpy.dot(b,b)
 norms = numpy.linalg.norm(a, axis =1)
 angles = dots/norms ### Basically I am calculating angle between 2 vectors 

我的一些向量的长度是0,所以在计算角度时就出现了这个运行时警告。

有没有一种简单的一行代码的方式,可以在计算角度时考虑到长度为0的情况呢?

angles =[i/j if j!=0 else -2 for i,j in zip(dots, norms)] # takes 10.6 seconds

不过这样做耗时很长。因为所有的角度值都在1到-1之间,而我只需要最大的10个值,这样可以帮到我。这个过程大约需要10.6秒,真是太慢了。

5 个回答

0

你可以使用 np.where( condition ) 来选择那些不等于0的数值,然后再进行除法运算:

norms = np.where(norms != 0 ) 
angles = dots/norms
2

你应该使用 np.where 这个函数。具体的用法可以参考 这个文档

angles = np.where(norms != 0, dots/norms, -2)

在计算角度的时候,当 norms 不等于 0 时,结果会是 downs/norms,如果 norms 等于 0,则结果会是 -2。虽然你会看到一个运行时警告(RuntimeWarning),因为 np.where 还是会内部计算整个向量 dots/norms,但这个警告可以安全地忽略。

3

你可以用 angles[~np.isfinite(angles)] = ... 这个方法,把 nan 值替换成其他的值。

比如说:

In [103]: angles = dots/norms

In [104]: angles
Out[104]: array([[ nan,  nan,  nan, ...,  nan,  nan,  nan]])

In [105]: angles[~np.isfinite(angles)] = -2

In [106]: angles
Out[106]: array([[-2., -2., -2., ..., -2., -2., -2.]])

要注意的是,除以零可能会得到 inf,而不是 nan

In [140]: np.array([1, 2, 3, 4, 0])/np.array([1, 2, 0, -0., 0])
Out[140]: array([  1.,   1.,  inf, -inf,  nan])

所以最好用 np.isfinite 来找出那些因为除以零而出现的问题,而不是用 np.isnan

In [141]: np.isfinite(np.array([1, 2, 3, 4, 0])/np.array([1, 2, 0, -0., 0]))
Out[141]: array([ True,  True, False, False, False], dtype=bool)

另外,如果你只想从一个 NumPy 数组中获取前十个值,使用 np.argpartition 函数可能会比把整个数组完全排序要快,尤其是对于很大的数组:

In [110]: N = 3

In [111]: x = np.array([50, 40, 30, 20, 10, 0, 100, 90, 80, 70, 60])

In [112]: idx = np.argpartition(-x, N)

In [113]: idx
Out[113]: array([ 6,  7,  8,  9, 10,  0,  1,  4,  3,  2,  5])

In [114]: x[idx[:N]]
Out[114]: array([100,  90,  80])

这表明即使是对于中等大小的数组,np.argpartition 也会更快:

In [123]: x = np.array([50, 40, 30, 20, 10, 0, 100, 90, 80, 70, 60]*1000)

In [124]: %timeit np.sort(x)[-N:]
1000 loops, best of 3: 233 µs per loop

In [125]: %timeit idx = np.argpartition(-x, N); x[idx[:N]]
10000 loops, best of 3: 53.3 µs per loop
5

在较新版本的numpy中,有一个第三种选择,可以避免使用errstate上下文管理器。

所有的Numpy ufuncs都接受一个可选的“where”参数。这个参数的作用和np.where函数有点不同,它只在条件为真的地方计算函数。当条件为假时,它不会改变值,因此使用“out”参数可以让我们提前设置任何我们想要的默认值。

import numpy as np

angle = np.arange(-5., 5.)
norm = np.arange(10.)

# version 1
with np.errstate(divide='ignore'):
    res1 = np.where(norm != 0., angle / norm, -2)

# version 2
with np.errstate(divide='ignore'):
    res2 = angle/norm
res2[np.isinf(res2)] = -2

# version 3
res3 = -2. * np.ones(angle.shape)
np.divide(angle, norm, out=res3, where=norm != 0)

print(res1)
print(res2)
print(res3)

np.testing.assert_array_almost_equal(res1, res2)
np.testing.assert_array_almost_equal(res1, res3)
17

你可以使用 np.errstate 这个上下文管理器来忽略警告,之后再把那些“不是数字”(nans)替换成你想要的值:

import numpy as np
angle = np.arange(-5., 5.) 
norm = np.arange(10.)
with np.errstate(divide='ignore'):
    print np.where(norm != 0., angle / norm, -2)
# or:
with np.errstate(divide='ignore'):
    res = angle/norm
res[np.isnan(res)] = -2

撰写回答