How do I catch a numpy warning like it's an exception (not just for testing)?

匿名 (未验证) 提交于 2019-12-03 09:05:37

问题:

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.

回答1:

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:  


回答2:

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_[0] 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  


回答3:

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.



回答4:

Remove warnings.filterwarnings and add:

numpy.seterr(all='raise') 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!