使用RuntimeWarning进行numpy除法:在double_scalars中遇到无效值


77

我写了以下脚本:

import numpy

d = numpy.array([[1089, 1093]])
e = numpy.array([[1000, 4443]])
answer = numpy.exp(-3 * d)
answer1 = numpy.exp(-3 * e)
res = answer.sum()/answer1.sum()
print res

但是我得到了这个结果,并发生了错误:

nan
C:\Users\Desktop\test.py:16: RuntimeWarning: invalid value encountered in double_scalars
  res = answer.sum()/answer1.sum()

看来输入元素太小,以至于python将它们设为零,但是除法确实有其结果。

如何解决这种问题?

Answers:


82

您无法解决。简单地说answer1.sum()==0,您不能执行零除。

发生这种情况是因为answer12的指数非常大,为负数,因此结果四舍五入为零。

nan 在这种情况下,由于除以零而返回。

现在,要解决您的问题,您可以:

  • 去像mpmath这样的高精度数学图书馆。但这没那么有趣。
  • 作为更大武器的替代品,请进行一些数学操作,如下所示。
  • 选择一款scipy/numpy完全可以满足您需求的定制功能!查看@Warren Weckesser的答案。

在这里,我解释了如何进行一些数学运算以帮助解决此问题。我们有分子的:

exp(-x)+exp(-y) = exp(log(exp(-x)+exp(-y)))
                = exp(log(exp(-x)*[1+exp(-y+x)]))
                = exp(log(exp(-x) + log(1+exp(-y+x)))
                = exp(-x + log(1+exp(-y+x)))

上面x=3* 1089和哪里y=3* 1093。现在,这个指数的参数是

-x + log(1+exp(-y+x)) = -x + 6.1441934777474324e-06

对于分母,您可以类似地进行处理,但获得的log(1+exp(-z+k))值已经四舍五入0,因此分母处的指数函数的参数仅四舍五入为-z=-3000。然后,您得到的结果是

exp(-x + log(1+exp(-y+x)))/exp(-z) = exp(-x+z+log(1+exp(-y+x)) 
                                   = exp(-266.99999385580668)

如果只保留2个前导项(即1089分子中的第一个数字和分母中的第一个数字)1000,则该结果已经非常接近您得到的结果:

exp(3*(1089-1000))=exp(-267)

为此,让我们看看我们与Wolfram alpha(link)解决方案有多近:

Log[(exp[-3*1089]+exp[-3*1093])/([exp[-3*1000]+exp[-3*4443])] -> -266.999993855806522267194565420933791813296828742310997510523

该数字与上面的指数之差为+1.7053025658242404e-13,因此我们在分母处的近似值很好。

最终结果是

'exp(-266.99999385580668) = 1.1050349147204485e-116

来自Wolfram Alpha是(link

1.105034914720621496.. × 10^-116 # Wolfram alpha.

同样,在这里也可以使用numpy。


但是在这种情况下,我需要得到除以2的非常小的值的值。
亨氏2015年

@Heinz我认为您的意思是将小数除以小数的情况。在这种情况下,更改算法以按比例放大两个数字要比查找机械扭曲好得多。例如,采用您的代码试图模拟的解析方程的对数。当涉及少量数字时,计算稳定性存在许多问题。如果可能的话,最好避免使用其中的任何一个。

20

您可以使用np.logaddexp(实现@ gg349的答案中的想法):

In [33]: d = np.array([[1089, 1093]])

In [34]: e = np.array([[1000, 4443]])

In [35]: log_res = np.logaddexp(-3*d[0,0], -3*d[0,1]) - np.logaddexp(-3*e[0,0], -3*e[0,1])

In [36]: log_res
Out[36]: -266.99999385580668

In [37]: res = exp(log_res)

In [38]: res
Out[38]: 1.1050349147204485e-116

或者您可以使用scipy.special.logsumexp

In [52]: from scipy.special import logsumexp

In [53]: res = np.exp(logsumexp(-3*d) - logsumexp(-3*e))

In [54]: res
Out[54]: 1.1050349147204485e-116
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.