1. ホーム
  2. パイソン

[解決済み】numpyの警告を例外のようにキャッチするにはどうすればよいですか(テスト用だけではありません)?

2022-04-21 16:23:49

質問

あるプロジェクトでラグランジュ多項式を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

これは私が捕まえたい警告です。リスト内包の中で発生するはずです。

どのように解決するのですか?

を使用しているようです。 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: