Eu tenho que fazer um polinômio Lagrange em Python para um projeto que estou fazendo. Eu estou fazendo um estilo baricêntrico para evitar o uso de um loop for explícito, em oposição ao estilo de diferença dividida de Newton. O problema que tenho é que preciso capturar uma divisão por zero, mas o Python (ou talvez o numpy) apenas faz disso um aviso em vez de uma exceção normal.
Então, o que eu preciso saber como fazer é capturar esse aviso como se fosse uma exceção. As perguntas relacionadas a este que encontrei neste site foram respondidas da maneira que eu precisava. Aqui está o meu código:
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.
Quando esse código é executado, a saída que recebo é:
Warning: divide by zero encountered in int_scalars
Esse é o aviso que eu quero pegar. Deve ocorrer dentro da compreensão da lista.
Warning: ...
? Tentando coisas comonp.array([1])/0
eu receboRuntimeWarning: ...
como saída.Respostas:
Parece que sua configuração está usando a
print
opção paranumpy.seterr
:Isso significa que o aviso que você vê não é um aviso real, mas são apenas alguns caracteres impressos
stdout
(consulte a documentação paraseterr
). Se você quiser pegá-lo, você pode:numpy.seterr(all='raise')
que gerará diretamente a exceção. No entanto, isso altera o comportamento de todas as operações, portanto, é uma grande mudança de comportamento.numpy.seterr(all='warn')
, que transformará o aviso impresso em um aviso real e você poderá usar a solução acima para localizar essa alteração no comportamento.Depois de receber um aviso, você pode usar o
warnings
módulo para controlar como os avisos devem ser tratados:Leia atentamente a documentação,
filterwarnings
pois ela permite filtrar apenas o aviso desejado e tem outras opções. Também consideraria analisarcatch_warnings
qual é um gerenciador de contexto que redefine automaticamente afilterwarnings
função original :fonte
RuntimeWarning
. Atualizado a resposta.RuntimeWarning
é gerado. O problema pode ser que sua configuração numpy esteja usando aprint
opção, que simplesmente imprime o aviso, mas não é um aviso real tratado pelowarnings
módulo ... Se esse for o caso, você pode tentar usarnumpy.seterr(all='warn')
e tentar novamente.numpy
, você não pode usarnumpy.seterr(all='error')
,error
precisa serraise
.Para adicionar um pouco à resposta de @ Bakuriu:
Se você já sabe onde o aviso provavelmente ocorrerá, geralmente é mais fácil usar o
numpy.errstate
gerenciador de contexto, em vez denumpy.seterr
tratar todos os avisos subsequentes do mesmo tipo da mesma forma, independentemente de onde eles ocorram no seu código:Editar:
No meu exemplo original, eu tinha
a = np.r_[0]
, mas aparentemente houve uma mudança no comportamento de numpy, de modo que a divisão por zero é tratada de maneira diferente nos casos em que o numerador é todo-zero. Por exemplo, no numpy 1.16.4:As mensagens de aviso correspondentes também são diferentes:
1. / 0.
é registrada comoRuntimeWarning: divide by zero encountered in true_divide
, enquanto0. / 0.
é registrada comoRuntimeWarning: invalid value encountered in true_divide
. Não sei por que exatamente essa alteração foi feita, mas desconfio que isso tenha a ver com o fato de o resultado de0. / 0.
não ser representável como um número (numpy retorna um NaN nesse caso), enquanto1. / 0.
e-1. / 0.
retornar + Inf e -Inf, respectivamente , de acordo com o padrão IEE 754.Se você deseja capturar os dois tipos de erro, sempre pode passar
np.errstate(divide='raise', invalid='raise')
ouall='raise'
se deseja gerar uma exceção para qualquer tipo de erro de ponto flutuante.fonte
FloatingPointError
, nãoZeroDivisionError
.Python 3.6.3
comnumpy==1.16.3
. Você poderia atualizá-lo, por favor?Para elaborar a resposta de @ Bakuriu acima, descobri que isso me permite capturar um aviso de tempo de execução de maneira semelhante a como capturaria um aviso de erro, imprimindo bem o aviso:
Você provavelmente poderá brincar com a colocação do posicionamento warnings.catch_warnings (), dependendo do tamanho de um guarda-chuva que deseja lançar com erros de captura dessa maneira.
fonte
Remova warnings.filterwarnings e adicione:
fonte