Muszę utworzyć wielomian Lagrange'a w Pythonie dla projektu, który robię. Robię styl barycentryczny, aby uniknąć używania jawnej pętli for w przeciwieństwie do stylu podzielonej różnicy Newtona. Problem, który mam, polega na tym, że muszę złapać dzielenie przez zero, ale Python (lub może numpy) po prostu sprawia, że jest to ostrzeżenie zamiast normalnego wyjątku.
Tak więc muszę wiedzieć, jak to zrobić, to złapać to ostrzeżenie, jakby to był wyjątek. Odpowiedzi na pytania związane z tym, które znalazłem na tej stronie, nie były potrzebne. 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.
Po wykonaniu tego kodu otrzymuję następujące dane:
Warning: divide by zero encountered in int_scalars
To ostrzeżenie, które chcę złapać. Powinien pojawić się wewnątrz rozumienia listy.
Warning: ...
? Próbuję rzeczy takich jaknp.array([1])/0
otrzymujęRuntimeWarning: ...
jako wyjście.Odpowiedzi:
Wygląda na to, że Twoja konfiguracja używa
print
opcji dlanumpy.seterr
:Oznacza to, że ostrzeżenie, które widzisz, nie jest prawdziwym ostrzeżeniem, ale jest to tylko kilka znaków drukowanych
stdout
(zobacz dokumentacjęseterr
). Jeśli chcesz go złapać, możesz:numpy.seterr(all='raise')
które bezpośrednio spowoduje zgłoszenie wyjątku. To jednak zmienia zachowanie wszystkich operacji, więc jest to dość duża zmiana w zachowaniu.numpy.seterr(all='warn')
, który przekształci wydrukowane ostrzeżenie w prawdziwe ostrzeżenie, a będziesz mógł użyć powyższego rozwiązania, aby zlokalizować tę zmianę w zachowaniu.Gdy otrzymasz ostrzeżenie, możesz użyć
warnings
modułu, aby kontrolować, jak ostrzeżenia powinny być traktowane:Przeczytaj uważnie dokumentację,
filterwarnings
ponieważ pozwala filtrować tylko żądane ostrzeżenie i ma inne opcje. Zastanowiłbym się również nad tym,catch_warnings
który jest menedżerem kontekstu, który automatycznie resetuje oryginalnąfilterwarnings
funkcję:źródło
RuntimeWarning
. Zaktualizowałem odpowiedź.RuntimeWarning
podniesiono a. Problem może polegać na tym, że twoja konfiguracja numpy używaprint
opcji, która po prostu drukuje ostrzeżenie, ale nie jest to prawdziwe ostrzeżenie obsługiwane przezwarnings
moduł ... Jeśli tak jest, możesz spróbować użyćnumpy.seterr(all='warn')
i spróbować ponownie.numpy
nie możesz używaćnumpy.seterr(all='error')
,error
musi byćraise
.Aby dodać trochę do odpowiedzi @ Bakuriu:
Jeśli już wiesz, gdzie prawdopodobnie wystąpi ostrzeżenie, często lepiej jest użyć
numpy.errstate
menedżera kontekstu, zamiastnumpy.seterr
traktować wszystkie kolejne ostrzeżenia tego samego typu tak samo niezależnie od tego, gdzie występują w kodzie:Edytować:
W moim oryginalnym przykładzie miałem
a = np.r_[0]
, ale najwyraźniej nastąpiła zmiana w zachowaniu numpy'ego, tak że dzielenie przez zero jest traktowane inaczej w przypadkach, gdy licznik jest zerowy. Na przykład w numpy 1.16.4:Odpowiednie komunikaty ostrzegawcze są również różne:
1. / 0.
jest rejestrowany jakoRuntimeWarning: divide by zero encountered in true_divide
, a0. / 0.
jakoRuntimeWarning: invalid value encountered in true_divide
. Nie jestem pewien, dlaczego dokładnie ta zmiana została wprowadzona, ale podejrzewam, że ma to związek z faktem, że wynik0. / 0.
nie jest reprezentowalny jako liczba (numpy zwraca w tym przypadku NaN), podczas gdy1. / 0.
i-1. / 0.
zwracają odpowiednio + Inf i -Inf , zgodnie ze standardem IEE 754.Jeśli chcesz wyłapać oba typy błędów, zawsze możesz przejść
np.errstate(divide='raise', invalid='raise')
, luball='raise'
jeśli chcesz zgłosić wyjątek dla dowolnego rodzaju błędu zmiennoprzecinkowego.źródło
FloatingPointError
, nieZeroDivisionError
.Python 3.6.3
znumpy==1.16.3
. Czy mógłbyś to zaktualizować?Aby rozwinąć powyższą odpowiedź @ Bakuriu, stwierdziłem, że pozwala mi to złapać ostrzeżenie w czasie wykonywania w podobny sposób, w jaki złapałbym ostrzeżenie o błędzie, ładnie drukując ostrzeżenie:
Prawdopodobnie będziesz mógł pobawić się rozmieszczeniem ostrzeżeń.catch_warnings () w zależności od tego, jak duży parasol chcesz zarzucić z wyłapywaniem błędów w ten sposób.
źródło
Usuń ostrzeżenia.filterwarnings i dodaj:
źródło