2013-04-10 23 views
96

Muszę utworzyć wielomian Lagrange w Pythonie dla projektu, który robię. Robię styl barycentryczny, aby uniknąć użycia wyraźnej pętli for, w odróżnieniu od dzielonego stylu różnicowego Newtona. Problem w tym, że muszę złapać dzielenie przez zero, ale Python (lub może numpy) sprawia, że ​​jest to ostrzeżenie zamiast normalnego wyjątku.Jak złapać numpy ostrzeżenie, jak to jest wyjątek (nie tylko do testowania)?

Muszę wiedzieć, jak to zrobić, aby uchwycić to ostrzeżenie tak, jakby był wyjątkiem. Odpowiedzi na te pytania, które znalazłem na tej stronie, zostały udzielone nie w sposób, w jaki potrzebowałem. Oto mój kod:

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. 

Gdy ten kod jest wykonywany na wyjściu pojawia się:

Warning: divide by zero encountered in int_scalars 

To ostrzeżenie chcę złapać. Powinien pojawić się w spisie zrozumiałym.

+2

Czy jesteś pewien, że to 'Ostrzeżenie: ...'? Próbując takich rzeczy jak 'np.array ([1])/0' otrzymuję' RuntimeWarning: ... 'jako wynik. – Bakuriu

+0

@MadPhysicist Nie duplikat; NumPy ma swoją własną wewnętrzną architekturę ostrzegawczą na szczycie Pythonów, którą można kontrolować (patrz odpowiedź Bakuríu). – gerrit

+0

@gerrit. Poprawiłem się i nauczyłem czegoś nowego. Usunąłem swój oryginalny komentarz, aby uniknąć wyzwalania szału kolekcjonerskiego. –

Odpowiedz

117

Wydaje się, że konfiguracja jest za pomocą opcji print dla 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]) 

Oznacza to, że ostrzeżenie widzisz to nie realne ostrzeżenie, ale jest to tylko niektóre znaki drukowane na stdout (patrz dokumentacja dla seterr). Jeśli chcesz go złapać, możesz:

  1. Użyj numpy.seterr(all='raise'), który bezpośrednio podniesie wyjątek. To jednak zmienia zachowanie wszystkich operacji, więc jest to dość duża zmiana w zachowaniu.
  2. Użyj numpy.seterr(all='warn'), która przekształci wydrukowane ostrzeżenie w prawdziwe ostrzeżenie i będziesz mógł użyć powyższego rozwiązania, aby zlokalizować tę zmianę w zachowaniu.

Gdy mamy ostrzeżenie, można użyć modułu warnings kontrolować sposób ostrzeżenia należy traktować:

>>> import warnings 
>>> 
>>> warnings.filterwarnings('error') 
>>> 
>>> try: 
...  warnings.warn(Warning()) 
... except Warning: 
...  print 'Warning was raised as an exception!' 
... 
Warning was raised as an exception! 

uważnie przeczytać dokumentację dla filterwarnings ponieważ pozwala na filtrowanie tylko ostrzeżenie chcesz i ma inne opcje. Chciałbym również rozważyć patrząc na catch_warnings który jest menedżerem kontekst, który automatycznie kasuje oryginalne filterwarnings funkcję:

>>> 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: 
+0

Myślę, że to jest początek. Ale to nie naprawia mojego problemu. Jeśli dodaję warnings.warn (Warning())) w moim kodzie w bloku try, to dostrzeże to ostrzeżenie. Z jakiegoś powodu nie przechwytuje ostrzeżenia dzielenia przez zero. Oto dokładna wiadomość ostrzegawcza: Ostrzeżenie: dziel się przez zero w int_scalars –

+0

@ John. Powinieneś edytować swoje pytanie i dodać dokładne wyniki, inaczej nie będziemy w stanie powiedzieć, co jest nie tak. Może * być * możliwe, że numpy definiuje gdzieś tę klasę ostrzegawczą i musisz odkryć w której podpakiecie jest w stanie ją złapać. Nieważne, odkryłem, że powinieneś używać 'RuntimeWarning'. Zaktualizowano odpowiedź. – Bakuriu

+0

Czy jesteś pewien? Zmieniłem kod do użycia z wyjątkiem RuntimeWarning :. Nadal nie działa =/ –

20

dodać trochę do @ Bakuriu za odpowiedź:

Jeśli już wiesz gdzie ostrzeżenie jest prawdopodobne występuje wtedy często czystsze użyć menedżera numpy.errstate kontekstowe, zamiast numpy.seterr który traktuje wszystkie kolejne ostrzeżenia tego samego typu, taki sam, niezależnie od tego, gdzie występują one w kodzie:

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 
+0

W szczególności podnosi 'FloatingPointError', a nie' ZeroDivisionError'. – gerrit

12

Aby rozwinąć @ odpowiedź Bakuriu za wyżej, znalazłem, że to pozwala mi złapać ostrzeżenie wykonawczego w sposób podobny do tego, jak chciałbym złapać ostrzeżenie o błędzie, drukując ostrzeżenie ładnie:

import warnings 

with warnings.catch_warnings(): 
    warnings.filterwarnings('error') 
    try: 
     answer = 1/0 
    except Warning as e: 
     print('error found:', e) 

You prawdopodobnie będzie w stanie bawić się z umieszczeniem miejsca wyświetlania ostrzeżeń.catch_warnings() w zależności od tego, jak dużą część parasola chcesz rzucić z błędami łowienia w ten sposób.

1

Usuń warnings.filterwarnings i dodać:

numpy.seterr(all='raise') 
Powiązane problemy