Tengo que hacer un polinomio de Lagrange en Python para un proyecto que estoy haciendo. Estoy haciendo un estilo baricéntrico para evitar el uso de un bucle for explícito en lugar del estilo de diferencia dividida de Newton. El problema que tengo es que necesito atrapar una división por cero, pero Python (o quizás numpy) solo lo convierte en una advertencia en lugar de una excepción normal.
Entonces, lo que necesito saber hacer es captar esta advertencia como si fuera una excepción. Las preguntas relacionadas con esto que encontré en este sitio no fueron respondidas de la manera que necesitaba. Aquí está mi 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.
Cuando se ejecuta este código, el resultado que obtengo es:
Warning: divide by zero encountered in int_scalars
Esa es la advertencia que quiero atrapar. Debe ocurrir dentro de la lista de comprensión.
Warning: ...
? Probar cosas comonp.array([1])/0
me saleRuntimeWarning: ...
como salida.Respuestas:
Parece que su configuración está utilizando la
print
opción paranumpy.seterr
:Esto significa que la advertencia que ve no es una advertencia real, sino solo algunos caracteres impresos
stdout
(consulte la documentación paraseterr
). Si quieres atraparlo puedes:numpy.seterr(all='raise')
que aumentará directamente la excepción. Sin embargo, esto cambia el comportamiento de todas las operaciones, por lo que es un cambio bastante grande en el comportamiento.numpy.seterr(all='warn')
, que transformará la advertencia impresa en una advertencia real y podrá utilizar la solución anterior para localizar este cambio de comportamiento.Una vez que tenga una advertencia, puede usar el
warnings
módulo para controlar cómo se deben tratar las advertencias:Lea atentamente la documentación
filterwarnings
ya que le permite filtrar solo la advertencia que desea y tiene otras opciones. También consideraría mirarcatch_warnings
cuál es un administrador de contexto que restablece automáticamente lafilterwarnings
función original :fuente
RuntimeWarning
. Se actualizó la respuesta.RuntimeWarning
se plantea a. El problema podría ser que su configuración numpy está utilizando laprint
opción, que simplemente imprime la advertencia pero no es una advertencia real manejada por elwarnings
módulo ... Si este es el caso, puede intentar usarlonumpy.seterr(all='warn')
e intentarlo nuevamente.numpy
, no se puede usarnumpy.seterr(all='error')
,error
debe serraise
.Para agregar un poco a la respuesta de @ Bakuriu:
Si ya sabe dónde es probable que ocurra la advertencia, a menudo es más limpio usar el
numpy.errstate
administrador de contexto, en lugar denumpy.seterr
tratar todas las advertencias posteriores del mismo tipo, independientemente de dónde ocurran dentro de su código:Editar:
En mi ejemplo original
a = np.r_[0]
, sí, pero aparentemente hubo un cambio en el comportamiento de numpy de tal manera que la división por cero se maneja de manera diferente en los casos en que el numerador es todo ceros. Por ejemplo, en numpy 1.16.4:Los mensajes de advertencia correspondientes también son diferentes:
1. / 0.
se registra comoRuntimeWarning: divide by zero encountered in true_divide
, mientras que0. / 0.
se registra comoRuntimeWarning: invalid value encountered in true_divide
. No estoy seguro de por qué se realizó exactamente este cambio, pero sospecho que tiene que ver con el hecho de que el resultado de0. / 0.
no es representable como un número (numpy devuelve un NaN en este caso) mientras que1. / 0.
y-1. / 0.
devuelve + Inf y -Inf respectivamente , según el estándar IEE 754.Si desea detectar ambos tipos de error, siempre puede pasar
np.errstate(divide='raise', invalid='raise')
, oall='raise'
si desea generar una excepción en cualquier tipo de error de coma flotante.fuente
FloatingPointError
, noZeroDivisionError
.Python 3.6.3
connumpy==1.16.3
. ¿Podrías actualizarlo por favor?Para profundizar en la respuesta de @ Bakuriu anterior, descubrí que esto me permite captar una advertencia de tiempo de ejecución de una manera similar a la que detectaría una advertencia de error, imprimiendo la advertencia muy bien:
Probablemente podrás jugar con la colocación de la colocación warnings.catch_warnings () dependiendo de qué tan grande de paraguas quieras lanzar con errores de captura de esta manera.
fuente
Elimine warnings.filterwarnings y agregue:
fuente