我如何捕获 numpy 警告,就像它是一个异常一样(不仅仅是为了测试)?

2025-02-28 08:22:00
admin
原创
75
摘要:问题描述:我必须用 Python 为我正在做的一个项目制作一个拉格朗日多项式。我制作了一个重心式的拉格朗日多项式,以避免使用显式 for 循环,而不是牛顿的差分式。我遇到的问题是我需要捕获除以零的结果,但 Python(或者可能是 numpy)只是将其作为警告而不是正常异常。因此,我需要知道如何捕获此警告,就...

问题描述:

我必须用 Python 为我正在做的一个项目制作一个拉格朗日多项式。我制作了一个重心式的拉格朗日多项式,以避免使用显式 for 循环,而不是牛顿的差分式。我遇到的问题是我需要捕获除以零的结果,但 Python(或者可能是 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

这就是我想要捕获的警告。它应该出现在列表推导中。


解决方案 1:

您的配置似乎使用了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')which 将直接引发异常。然而,这会改变所有操作的行为,因此这是一个相当大的行为变化。

  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: 

解决方案 2:

对@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],但显然 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),而根据 IEE 754 标准,1. / 0.-1. / 0.分别返回 +Inf 和 -Inf。

如果您想捕获这两种类型的错误,您可以随时传递np.errstate(divide='raise', invalid='raise'),或者如果您想对任何类型的浮点错误all='raise'引发异常。

解决方案 3:

为了详细说明上述@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() 的位置,具体取决于您想通过这种方式捕获错误时要投射多大的伞。

解决方案 4:

删除 warnings.filterwarnings 并添加:

numpy.seterr(all='raise')

解决方案 5:

如果我可以建议这个选项,那么一个不错的 Pythonic 方法是使用上下文管理器。之前的另一个答案提出了这个想法,但我花了一些时间才弄清楚如何使用它,所以我补充了这个答案。

  • 以前Python 3.11,您必须在不同的行上写下所需的操作(这是执行此操作的方法,我检查了文档,见下文)。

  • 之后Python 3.11,您可以以一种非常好的方式和简洁的方式将操作传递给上下文管理器的构造函数。

参见下面的示例。

# works in python 3.8: https://docs.python.org/3.8/library/warnings.html
import warnings
with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    fn_triggering_warnings()

# works in python 3.11: https://docs.python.org/3.11/library/warnings.html
import warnings
with warnings.catch_warnings(action="ignore"):
    fn_triggering_warnings()

解决方案 6:

from statsmodels.stats.weightstats import ztest

with warnings.catch_warnings():
    warnings.filterwarnings('error')
    try:
        zstat, pvalue = ztest([0,0], [0], alternative='two-sided')
    except Warning as e:
        print('error found:', e)

这可能是一个更好的例子。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2941  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1803  
  PLM(产品生命周期管理)系统在企业的产品研发、生产与管理过程中扮演着至关重要的角色。然而,在实际运行中,资源冲突是经常会遇到的难题。资源冲突可能导致项目进度延迟、成本增加以及产品质量下降等一系列问题,严重影响企业的效益与竞争力。因此,如何有效应对PLM系统中的资源冲突,成为众多企业关注的焦点。接下来,我们将详细探讨5...
plm项目管理系统   31  
  敏捷项目管理与产品生命周期管理(PLM)的融合,正成为企业在复杂多变的市场环境中提升研发效率、增强竞争力的关键举措。随着技术的飞速发展和市场需求的快速更迭,传统的研发流程面临着诸多挑战,而将敏捷项目管理理念融入PLM,有望在2025年实现研发流程的深度优化,为企业创造更大的价值。理解敏捷项目管理与PLM的核心概念敏捷项...
plm项目   31  
  模块化设计在现代产品开发中扮演着至关重要的角色,它能够提升产品开发效率、降低成本、增强产品的可维护性与可扩展性。而产品生命周期管理(PLM)系统作为整合产品全生命周期信息的关键平台,对模块化设计有着强大的支持能力。随着技术的不断发展,到 2025 年,PLM 系统在支持模块化设计方面将有一系列令人瞩目的技术实践。数字化...
plm软件   28  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用