예외적 인 것처럼 numpy 경고를 잡는 방법은 무엇입니까?


173

내가하고있는 프로젝트를 위해 파이썬에서 Lagrange 다항식을 만들어야합니다. 나는 뉴턴의 분리 된 차이 스타일과 달리 명시 적 for 루프를 사용하지 않기 위해 barycentric 스타일을하고 있습니다. 내가 가진 문제는 0으로 나누기를 잡아야하지만 파이썬 (또는 아마도 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: ...것과 같은 것을 시도 합니다.
Bakuriu

1
@MadPhysicist 복제본이 아님; NumPy에는 Python을 기반으로 자체적으로 내부 경고 아키텍처가 있으며이를 구체적으로 제어 할 수 있습니다 (Bakuríu의 답변 참조).
gerrit

@ 거릿. 나는 정정되어 새로운 것을 배웠다. 배지 수집 열풍을 유발하지 않기 위해 원래 의견을 삭제했습니다.
Mad Physicist

사용할 수있는 또 다른 접근법은 분열 전에 분모가 0인지 간단히 확인하는 것입니다. 이는 numpy의 경고 시스템으로 인한 오버 헤드를 피합니다. (이것은 아마도 분모가 0인지 확인하는 깔끔한 목록 이해를 루프로 확장해야 함을 의미하지만)
Oliver

답변:


197

구성에서 다음 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 ()))을 추가하면 경고가 나타납니다. 어떤 이유로 든 0으로 나누기 경고를 포착하지 못합니다. 정확한 경고 메시지는 다음과 같습니다. 경고 : int_scalars에서 발생한 0으로 나누기
John K.

@JohnK. 질문을 편집하고 정확한 결과를 추가해야합니다. 그렇지 않으면 무엇이 잘못되었는지 알 수 없습니다. NumPy와이 경고 수준의 어딘가를 정의하는 것이 가능하고 당신이 그것을 잡을 수있는 서브 패키지에 discovere해야합니다. 걱정하지 마십시오 RuntimeWarning. 답변을 업데이트했습니다.
Bakuriu

확실합니까? RuntimeWarning :을 제외하고 사용하도록 코드를 변경했습니다. 여전히 작동하지 않습니다 = /
John K.

@JohnK. 문서에서 a RuntimeWarning가 발생 했음을 나타냅니다 . 문제는 numpy 구성이 print단순히 경고를 인쇄 하는 옵션을 사용하고 있지만 warnings모듈에서 처리하는 실제 경고는 아닙니다 ...이 경우 사용 numpy.seterr(all='warn')하고 다시 시도 할 수 있습니다 .
Bakuriu

3
내 버전에서는 numpy사용할 수 없습니다 numpy.seterr(all='error'), error될 필요가있다 raise.
detly

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]있었지만 분자가 모두 0 인 경우 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'대해 예외를 발생시킬 수 있습니다.


특히이 제기 FloatingPointError하지 ZeroDivisionError.
gerrit

이 작동하지 않습니다 Python 3.6.3와 함께 numpy==1.16.3. 업데이트 하시겠습니까?
anilbey 2016 년

1
@anilbey 분명히 numpy의 동작이 변경되어 분자가 0인지 여부에 따라 0으로 나누기가 다르게 처리됩니다.
ali_m 2016 년

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
answer = 1 / 0은 오류를 독립적으로 발생시킵니다 ... 정확한 예를들 수 있습니다 ...
Amirkhm

8

warnings.filterwarnings를 제거하고 다음을 추가하십시오.

numpy.seterr(all='raise')
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.