web-dev-qa-db-ja.com

numpyの警告を(テストだけでなく)例外のようにキャッチするにはどうすればよいですか?

私がやっているプロジェクトのために、Pythonでラグランジュ多項式を作成する必要があります。ニュートンの分割差分スタイルとは対照的に、明示的なforループの使用を避けるために、重心スタイルを実行しています。私が抱えている問題は、ゼロによる除算をキャッチする必要があることですが、Python(または多分numpy)は、通常の例外の代わりに警告を出すだけです。

そのため、この警告を例外であるかのようにキャッチする方法を知る必要があります。このサイトで見つけたこれに関連する質問は、私が必要とする方法で答えられませんでした。これが私のコードです:

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

それは私がキャッチしたい警告です。リスト内包表記内で発生するはずです。

143
John K.

あなたの設定は numpy.seterrprintオプションを使用しているようです:

>>> 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])

これは、表示される警告がnot実際の警告であることを意味しますが、それは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: 
168
Bakuriu

@Bakuriuの回答に少し追加するには:

警告が発生する可能性のある場所が既にわかっている場合は、多くの場合、同じタイプの後続のすべての警告を同じように処理する numpy.errstate ではなく numpy.seterr コンテキストマネージャーを使用しますコード内のどこで発生するかに関係なく:

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 
33
ali_m

上記の@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()配置の配置をいじることができるでしょう。

23
ntk4

Warnings.filterwarningsを削除して、次を追加します。

numpy.seterr(all='raise')
4
Shital Shah