标签归档:divide-by-zero

如何捕获像异常一样的numpy警告(不仅用于测试)?

问题:如何捕获像异常一样的numpy警告(不仅用于测试)?

我必须在Python中为正在执行的项目制作Lagrange多项式。我正在做一个重心样式,以避免使用显式的for循环,而不是牛顿的分差样式。我的问题是我需要用零除,但是Python(或者也许是numpy)只是将其警告而不是正常异常。

因此,我需要知道的是如何捕获此警告,就像它是一个exceptions一样。我在本网站上发现的与此相关的问题并未按照我需要的方式回答。这是我的代码:

import numpy as np
import matplotlib.pyplot as plt
import warnings

class Lagrange:
    def __init__(self, xPts, yPts):
        self.xPts = np.array(xPts)
        self.yPts = np.array(yPts)
        self.degree = len(xPts)-1 
        self.weights = np.array([np.product([x_j - x_i for x_j in xPts if x_j != x_i]) for x_i in xPts])

    def __call__(self, x):
        warnings.filterwarnings("error")
        try:
            bigNumerator = np.product(x - self.xPts)
            numerators = np.array([bigNumerator/(x - x_j) for x_j in self.xPts])
            return sum(numerators/self.weights*self.yPts) 
        except Exception, e: # Catch division by 0. Only possible in 'numerators' array
            return yPts[np.where(xPts == x)[0][0]]

L = Lagrange([-1,0,1],[1,0,1]) # Creates quadratic poly L(x) = x^2

L(1) # This should catch an error, then return 1. 

执行此代码后,我得到的输出是:

Warning: divide by zero encountered in int_scalars

那是我要抓住的警告。它应该出现在列表理解中。

I have to make a Lagrange polynomial in Python for a project I’m doing. I’m doing a barycentric style one to avoid using an explicit for-loop as opposed to a Newton’s divided difference style one. The problem I have is that I need to catch a division by zero, but Python (or maybe numpy) just makes it a warning instead of a normal exception.

So, what I need to know how to do is to catch this warning as if it were an exception. The related questions to this I found on this site were answered not in the way I needed. Here’s my code:

import numpy as np
import matplotlib.pyplot as plt
import warnings

class Lagrange:
    def __init__(self, xPts, yPts):
        self.xPts = np.array(xPts)
        self.yPts = np.array(yPts)
        self.degree = len(xPts)-1 
        self.weights = np.array([np.product([x_j - x_i for x_j in xPts if x_j != x_i]) for x_i in xPts])

    def __call__(self, x):
        warnings.filterwarnings("error")
        try:
            bigNumerator = np.product(x - self.xPts)
            numerators = np.array([bigNumerator/(x - x_j) for x_j in self.xPts])
            return sum(numerators/self.weights*self.yPts) 
        except Exception, e: # Catch division by 0. Only possible in 'numerators' array
            return yPts[np.where(xPts == x)[0][0]]

L = Lagrange([-1,0,1],[1,0,1]) # Creates quadratic poly L(x) = x^2

L(1) # This should catch an error, then return 1. 

When this code is executed, the output I get is:

Warning: divide by zero encountered in int_scalars

That’s the warning I want to catch. It should occur inside the list comprehension.


回答 0

看来您的配置正在使用print选项numpy.seterr

>>> import numpy as np
>>> np.array([1])/0   #'warn' mode
__main__:1: RuntimeWarning: divide by zero encountered in divide
array([0])
>>> np.seterr(all='print')
{'over': 'warn', 'divide': 'warn', 'invalid': 'warn', 'under': 'ignore'}
>>> np.array([1])/0   #'print' mode
Warning: divide by zero encountered in divide
array([0])

这意味着您看到的警告不是真正的警告,而只是打印了一些字符stdout(请参阅文档以获取信息seterr)。如果您想抓住它,可以:

  1. 使用numpy.seterr(all='raise')它将直接引发异常。但是,这会更改所有操作的行为,因此,这是行为上的很大变化。
  2. 使用numpy.seterr(all='warn'),可以将打印的警告转换为真实的警告,您将可以使用上述解决方案来本地化此行为更改。

实际warnings收到警告后,您可以使用该模块来控制警告的处理方式:

>>> import warnings
>>> 
>>> warnings.filterwarnings('error')
>>> 
>>> try:
...     warnings.warn(Warning())
... except Warning:
...     print 'Warning was raised as an exception!'
... 
Warning was raised as an exception!

请仔细阅读文档,filterwarnings因为它可以使您仅过滤所需的警告并具有其他选项。我还要考虑看看catch_warnings哪个是上下文管理器,它会自动重置原始filterwarnings功能:

>>> import warnings
>>> with warnings.catch_warnings():
...     warnings.filterwarnings('error')
...     try:
...         warnings.warn(Warning())
...     except Warning: print 'Raised!'
... 
Raised!
>>> try:
...     warnings.warn(Warning())
... except Warning: print 'Not raised!'
... 
__main__:2: Warning: 

It seems that your configuration is using the print option for numpy.seterr:

>>> import numpy as np
>>> np.array([1])/0   #'warn' mode
__main__:1: RuntimeWarning: divide by zero encountered in divide
array([0])
>>> np.seterr(all='print')
{'over': 'warn', 'divide': 'warn', 'invalid': 'warn', 'under': 'ignore'}
>>> np.array([1])/0   #'print' mode
Warning: divide by zero encountered in divide
array([0])

This means that the warning you see is not a real warning, but it’s just some characters printed to stdout(see the documentation for seterr). If you want to catch it you can:

  1. Use numpy.seterr(all='raise') which will directly raise the exception. This however changes the behaviour of all the operations, so it’s a pretty big change in behaviour.
  2. Use numpy.seterr(all='warn'), which will transform the printed warning in a real warning and you’ll be able to use the above solution to localize this change in behaviour.

Once you actually have a warning, you can use the warnings module to control how the warnings should be treated:

>>> import warnings
>>> 
>>> warnings.filterwarnings('error')
>>> 
>>> try:
...     warnings.warn(Warning())
... except Warning:
...     print 'Warning was raised as an exception!'
... 
Warning was raised as an exception!

Read carefully the documentation for filterwarnings since it allows you to filter only the warning you want and has other options. I’d also consider looking at catch_warnings which is a context manager which automatically resets the original filterwarnings function:

>>> import warnings
>>> with warnings.catch_warnings():
...     warnings.filterwarnings('error')
...     try:
...         warnings.warn(Warning())
...     except Warning: print 'Raised!'
... 
Raised!
>>> try:
...     warnings.warn(Warning())
... except Warning: print 'Not raised!'
... 
__main__:2: Warning: 

回答 1

在@Bakuriu的答案中添加一些内容:

如果您已经知道警告可能在何处发生,那么使用numpy.errstate上下文管理器通常会更干净一些,而不是 numpy.seterr将所有相同类型的后续警告视为相同,而不管它们在代码中的位置如何:

import numpy as np

a = np.r_[1.]
with np.errstate(divide='raise'):
    try:
        a / 0   # this gets caught and handled as an exception
    except FloatingPointError:
        print('oh no!')
a / 0           # this prints a RuntimeWarning as usual

编辑:

在我最初的示例中,我有a = np.r_[0],但是显然numpy的行为发生了变化,使得在分子为全零的情况下对零除的处理方式有所不同。例如,在numpy 1.16.4中:

all_zeros = np.array([0., 0.])
not_all_zeros = np.array([1., 0.])

with np.errstate(divide='raise'):
    not_all_zeros / 0.  # Raises FloatingPointError

with np.errstate(divide='raise'):
    all_zeros / 0.  # No exception raised

with np.errstate(invalid='raise'):
    all_zeros / 0.  # Raises FloatingPointError

相应的警告消息也不同:1. / 0.记录为RuntimeWarning: divide by zero encountered in true_divide,而0. / 0.记录为RuntimeWarning: invalid value encountered in true_divide。我不确定为什么要进行此更改,但是我怀疑这与以下事实有关:0. / 0.是不能表示为数字(numpy的回报为NaN在这种情况下),而1. / 0.-1. / 0.分别返回+ Inf文件和-Inf ,符合IEE 754标准。

如果您想捕获两种类型的错误,则可以始终通过np.errstate(divide='raise', invalid='raise'),或者all='raise'如果您想对任何类型的浮点错误引发异常。

To add a little to @Bakuriu’s answer:

If you already know where the warning is likely to occur then it’s often cleaner to use the numpy.errstate context manager, rather than numpy.seterr which treats all subsequent warnings of the same type the same regardless of where they occur within your code:

import numpy as np

a = np.r_[1.]
with np.errstate(divide='raise'):
    try:
        a / 0   # this gets caught and handled as an exception
    except FloatingPointError:
        print('oh no!')
a / 0           # this prints a RuntimeWarning as usual

Edit:

In my original example I had a = np.r_[0], but apparently there was a change in numpy’s behaviour such that division-by-zero is handled differently in cases where the numerator is all-zeros. For example, in numpy 1.16.4:

all_zeros = np.array([0., 0.])
not_all_zeros = np.array([1., 0.])

with np.errstate(divide='raise'):
    not_all_zeros / 0.  # Raises FloatingPointError

with np.errstate(divide='raise'):
    all_zeros / 0.  # No exception raised

with np.errstate(invalid='raise'):
    all_zeros / 0.  # Raises FloatingPointError

The corresponding warning messages are also different: 1. / 0. is logged as RuntimeWarning: divide by zero encountered in true_divide, whereas 0. / 0. is logged as RuntimeWarning: invalid value encountered in true_divide. I’m not sure why exactly this change was made, but I suspect it has to do with the fact that the result of 0. / 0. is not representable as a number (numpy returns a NaN in this case) whereas 1. / 0. and -1. / 0. return +Inf and -Inf respectively, per the IEE 754 standard.

If you want to catch both types of error you can always pass np.errstate(divide='raise', invalid='raise'), or all='raise' if you want to raise an exception on any kind of floating point error.


回答 2

为了详细说明上述@Bakuriu的答案,我发现这使我能够以类似于捕获错误警告的方式捕获运行时警告,从而很好地打印警告:

import warnings

with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
        answer = 1 / 0
    except Warning as e:
        print('error found:', e)

您可能可以尝试放置warnings.catch_warnings()的位置,具体取决于您要用这种方式捕获错误的伞的大小。

To elaborate on @Bakuriu’s answer above, I’ve found that this enables me to catch a runtime warning in a similar fashion to how I would catch an error warning, printing out the warning nicely:

import warnings

with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
        answer = 1 / 0
    except Warning as e:
        print('error found:', e)

You will probably be able to play around with placing of the warnings.catch_warnings() placement depending on how big of an umbrella you want to cast with catching errors this way.


回答 3

删除warnings.filterwarnings并添加:

numpy.seterr(all='raise')

Remove warnings.filterwarnings and add:

numpy.seterr(all='raise')

如何用零除返回0

问题:如何用零除返回0

我正在尝试在python中执行元素明智的除法,但是如果遇到零,我需要将商设为零。

例如:

array1 = np.array([0, 1, 2])
array2 = np.array([0, 1, 1])

array1 / array2 # should be np.array([0, 1, 2])

我总是可以在数据中使用for循环,但是要真正利用numpy的优化,我需要除法函数在除以零错误后返回0,而不是忽略错误。

除非我缺少任何东西,否则numpy.seterr()似乎不会在出现错误时返回值。在设置自己的除以零的错误处理方法时,还有人对我如何从numpy中获得最大收益有其他建议吗?

I’m trying to perform an element wise divide in python, but if a zero is encountered, I need the quotient to just be zero.

For example:

array1 = np.array([0, 1, 2])
array2 = np.array([0, 1, 1])

array1 / array2 # should be np.array([0, 1, 2])

I could always just use a for-loop through my data, but to really utilize numpy’s optimizations, I need the divide function to return 0 upon divide by zero errors instead of ignoring the error.

Unless I’m missing something, it doesn’t seem numpy.seterr() can return values upon errors. Does anyone have any other suggestions on how I could get the best out of numpy while setting my own divide by zero error handling?


回答 0

在numpy v1.7 +中,您可以利用ufuncs的“ where”选项。您可以一行完成事情,而不必与错误上下文管理器打交道。

>>> a = np.array([-1, 0, 1, 2, 3], dtype=float)
>>> b = np.array([ 0, 0, 0, 2, 2], dtype=float)

# If you don't pass `out` the indices where (b == 0) will be uninitialized!
>>> c = np.divide(a, b, out=np.zeros_like(a), where=b!=0)
>>> print(c)
[ 0.   0.   0.   1.   1.5]

在这种情况下,它将在“其中” b不等于零的任何地方进行除法计算。当b等于零时,它与您在’out’参数中最初给它的任何值保持不变。

In numpy v1.7+, you can take advantage of the “where” option for ufuncs. You can do things in one line and you don’t have to deal with the errstate context manager.

>>> a = np.array([-1, 0, 1, 2, 3], dtype=float)
>>> b = np.array([ 0, 0, 0, 2, 2], dtype=float)

# If you don't pass `out` the indices where (b == 0) will be uninitialized!
>>> c = np.divide(a, b, out=np.zeros_like(a), where=b!=0)
>>> print(c)
[ 0.   0.   0.   1.   1.5]

In this case, it does the divide calculation anywhere ‘where’ b does not equal zero. When b does equal zero, then it remains unchanged from whatever value you originally gave it in the ‘out’ argument.


回答 1

以@Franck Dernoncourt的答案为基础,修正-1 / 0:

def div0( a, b ):
    """ ignore / 0, div0( [-1, 0, 1], 0 ) -> [0, 0, 0] """
    with np.errstate(divide='ignore', invalid='ignore'):
        c = np.true_divide( a, b )
        c[ ~ np.isfinite( c )] = 0  # -inf inf NaN
    return c

div0( [-1, 0, 1], 0 )
array([0, 0, 0])

Building on @Franck Dernoncourt’s answer, fixing -1 / 0:

def div0( a, b ):
    """ ignore / 0, div0( [-1, 0, 1], 0 ) -> [0, 0, 0] """
    with np.errstate(divide='ignore', invalid='ignore'):
        c = np.true_divide( a, b )
        c[ ~ np.isfinite( c )] = 0  # -inf inf NaN
    return c

div0( [-1, 0, 1], 0 )
array([0, 0, 0])

回答 2

以其他答案为基础,并在以下方面进行改进:

码:

import numpy as np

a = np.array([0,0,1,1,2], dtype='float')
b = np.array([0,1,0,1,3], dtype='float')

with np.errstate(divide='ignore', invalid='ignore'):
    c = np.true_divide(a,b)
    c[c == np.inf] = 0
    c = np.nan_to_num(c)

print('c: {0}'.format(c))

输出:

c: [ 0.          0.          0.          1.          0.66666667]

Building on the other answers, and improving on:

Code:

import numpy as np

a = np.array([0,0,1,1,2], dtype='float')
b = np.array([0,1,0,1,3], dtype='float')

with np.errstate(divide='ignore', invalid='ignore'):
    c = np.true_divide(a,b)
    c[c == np.inf] = 0
    c = np.nan_to_num(c)

print('c: {0}'.format(c))

Output:

c: [ 0.          0.          0.          1.          0.66666667]

回答 3

单线(引发警告)

np.nan_to_num(array1 / array2)

One-liner (throws warning)

np.nan_to_num(array1 / array2)

回答 4

尝试分两个步骤进行。先划分,然后更换。

with numpy.errstate(divide='ignore'):
    result = numerator / denominator
    result[denominator == 0] = 0

numpy.errstate行是可选的,并且仅防止numpy告诉您除零的“错误”,因为您已经打算这样做并处理这种情况。

Try doing it in two steps. Division first, then replace.

with numpy.errstate(divide='ignore'):
    result = numerator / denominator
    result[denominator == 0] = 0

The numpy.errstate line is optional, and just prevents numpy from telling you about the “error” of dividing by zero, since you’re already intending to do so, and handling that case.


回答 5

您也可以inf仅根据数组dtypes为float来基于进行替换,如下所示

>>> a = np.array([1,2,3], dtype='float')
>>> b = np.array([0,1,3], dtype='float')
>>> c = a / b
>>> c
array([ inf,   2.,   1.])
>>> c[c == np.inf] = 0
>>> c
array([ 0.,  2.,  1.])

You can also replace based on inf, only if the array dtypes are floats, as per this answer:

>>> a = np.array([1,2,3], dtype='float')
>>> b = np.array([0,1,3], dtype='float')
>>> c = a / b
>>> c
array([ inf,   2.,   1.])
>>> c[c == np.inf] = 0
>>> c
array([ 0.,  2.,  1.])

回答 6

我发现搜索一个相关问题的一个答案是根据分母是否为零来操纵输出。

假设arrayAarrayB已经初始化,但是arrayB有一些零。如果我们要arrayC = arrayA / arrayB安全地进行计算,可以执行以下操作。

在这种情况下,只要我在其中一个单元格中myOwnValue被零除,就将单元格设置为等于,在这种情况下为零

myOwnValue = 0
arrayC = np.zeros(arrayA.shape())
indNonZeros = np.where(arrayB != 0)
indZeros = np.where(arrayB = 0)

# division in two steps: first with nonzero cells, and then zero cells
arrayC[indNonZeros] = arrayA[indNonZeros] / arrayB[indNonZeros]
arrayC[indZeros] = myOwnValue # Look at footnote

脚注:回想起来,这条线无论如何都是不必要的,因为它arrayC[i]被实例化为零。但是,如果是这种情况myOwnValue != 0,该操作将有所作为。

One answer I found searching a related question was to manipulate the output based upon whether the denominator was zero or not.

Suppose arrayA and arrayB have been initialized, but arrayB has some zeros. We could do the following if we want to compute arrayC = arrayA / arrayB safely.

In this case, whenever I have a divide by zero in one of the cells, I set the cell to be equal to myOwnValue, which in this case would be zero

myOwnValue = 0
arrayC = np.zeros(arrayA.shape())
indNonZeros = np.where(arrayB != 0)
indZeros = np.where(arrayB = 0)

# division in two steps: first with nonzero cells, and then zero cells
arrayC[indNonZeros] = arrayA[indNonZeros] / arrayB[indNonZeros]
arrayC[indZeros] = myOwnValue # Look at footnote

Footnote: In retrospect, this line is unnecessary anyways, since arrayC[i] is instantiated to zero. But if were the case that myOwnValue != 0, this operation would do something.


回答 7

另一个值得一提的解决方案:

>>> a = np.array([1,2,3], dtype='float')
>>> b = np.array([0,1,3], dtype='float')
>>> b_inv = np.array([1/i if i!=0 else 0 for i in b])
>>> a*b_inv
array([0., 2., 1.])

An other solution worth mentioning :

>>> a = np.array([1,2,3], dtype='float')
>>> b = np.array([0,1,3], dtype='float')
>>> b_inv = np.array([1/i if i!=0 else 0 for i in b])
>>> a*b_inv
array([0., 2., 1.])