(テストだけでなく)例外であるように派手な警告をどのようにキャッチしますか?


174

私がやっているプロジェクトのために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

それは私がキャッチしたい警告です。リスト内包表記の内部で発生する必要があります。


2
それは確かWarning: ...ですか?np.array([1])/0私がRuntimeWarning: ...出力として得るようなことをしようとしています。
バクリウ2013

1
@MadPhysicist重複ではありません。NumPyには、Pythonの上に独自の内部警告アーキテクチャがあり、具体的に制御できます(Bakuríuによる回答を参照)。
gerrit

@gerrit。私は直立して新しいことを学びました。バッジコレクションの狂乱を引き起こさないように、元のコメントを削除しました。
Mad Physicist 2016

使用できる別の方法は、除算の前に分母が0であるかどうかを単にチェックすることです。これにより、numpyの警告システムをいじるオーバーヘッドを回避できます。(これはおそらく、分母のいずれかがゼロであるかどうかをチェックするループチェックにきちんとしたリストの理解を拡張する必要があることを意味します。)
Oliver

回答:


198

あなたの構成は次の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: 

これが出発点だと思います。しかし、それは実際には私の問題を解決しません。tryブロックのコードにwarnings.warn(Warning()))を追加すると、警告がキャッチされます。何らかの理由で、ゼロによる除算の警告をキャッチしません。正確な警告メッセージは次のとおりです。警告:int_scalarsで発生したゼロで除算
John K.

@JohnK。質問を編集して正確な出力を追加する必要があります。そうしないと、何が悪いのかわかりません。可能性がある numpyのは、この警告クラスのどこかを定義することを可能にし、あなたはそれをキャッチすることができるようにどのサブパッケージにdiscovereしなければなりません。気にしないで、を使用する必要があることを発見しましたRuntimeWarning。回答を更新しました。
バクリウ2013

本気ですか?RuntimeWarning:以外のコードを使用するように変更しました。まだ機能していません= /
John K.

@JohnK。ドキュメントではa RuntimeWarningが発生することを述べています。問題は、あなたの派手な設定がprint警告を出力するだけのオプションを使用していることかもしれませんが、それはwarningsモジュールによって処理される実際の警告ではありません...これが当てはまる場合は、使用numpy.seterr(all='warn')して再試行できます。
Bakuriu 2013

3
私のバージョンではnumpy使用できません、numpy.seterr(all='error')errorである必要がありますraise
2014年

41

@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を返す)ことと、それぞれ+ Infおよび-Inf 1. / 0.-1. / 0.返すことに関係しているのではないかと思います。 、IEE 754標準に従って。

両方のタイプのエラーをキャッチしたい場合は、いつでも渡すことができますnp.errstate(divide='raise', invalid='raise')。またはall='raise'あらゆる種類の浮動小数点エラーで例外を発生させたい場合。


特にそれは発生FloatingPointErrorしませんZeroDivisionError
gerrit 16

これは上で動作しませんPython 3.6.3numpy==1.16.3。更新して頂けますか?
anilbey

1
@anilbeyどうやら、numpyの動作に変更があったため、ゼロによる除算は、分子も(すべて)ゼロであるかどうかに応じて異なる方法で処理されるようになりました。
ali_m

27

上記の@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()の配置を試すことができます。


3
回答=
1/0

8

warnings.filterwarningsを削除して、以下を追加します。

numpy.seterr(all='raise')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.