【问题标题】:Numpy where and division by zeroNumpy where 和除以零
【发布时间】:2018-02-08 23:14:50
【问题描述】:

我需要按以下方式计算x(遗留代码):

x = numpy.where(b == 0, a, 1/b) 

我想它在 中有效(就像在 代码中一样),但在 中无效(if b = 0 它返回错误)。

如何让它在 python-3.x 中工作?

编辑:错误消息(Python 3.6.3):

ZeroDivisionError: division by zero

【问题讨论】:

  • 为什么它在 python 2 中工作?您正在划分 1/0,这是非法的。你在 python 2 中使用过 b=0 吗?
  • 你得到什么错误?
  • 我猜(我可能错了)在 Python 2x 中,如果第一个表达式为真,则不会评估第三个表达式。
  • 我刚刚在 2.7 和 3.6 中都对其进行了测试。同:RuntimeWarning: divide by zero encountered in divide
  • @henry - 我们需要知道ab 在这里是什么。将它们都作为数组,这不会在任何一个版本中对我造成ZeroDivisionError,但都会给出RuntimeWarning(如果 b 的任何元素为零)。使用b 作为int,我得到ZeroDivisionError

标签: python-2.x python-2.7 python-3.x python numpy


【解决方案1】:

numpy.where不是条件执行;这是有条件的选择。 Python 函数参数总是在函数调用之前完全计算,因此函数无法有条件地或部分地计算其参数。

您的代码:

x = numpy.where(b == 0, a, 1/b)

告诉 Python 反转b每个 元素,然后根据b == 0 的元素从a1/b 中选择元素。 Python 甚至没有达到选择元素的地步,因为计算 1/b 失败。

您可以通过仅反转b 的非零部分来避免此问题。假设ab 具有相同的形状,它可能看起来像这样:

x = numpy.empty_like(b)
mask = (b == 0)
x[mask] = a[mask]
x[~mask] = 1/b[~mask]

【讨论】:

  • 如果我理解正确的话,我得到的RuntimeWarning: divide by zero encountered in true_divide实际上来自条件选择,它只是告诉我某些条件是不可能的?
  • @roganjosh 它来自并不懒惰的部门本身。但是 nans/infs 会被选择丢弃。
  • @AndrasDeak 这是有道理的,但我想知道为什么在这种情况下它会告诉我任何事情,因为重点是要尽量避免这种情况。我不确定这样的警告在哪里可能有用。
  • where 已经获得了一个分割数组。除法发生在通话之前。 where 无法阻止这种情况。尝试只计算除法。
  • 在我看来,问题是为什么np.where(b == 0, a, 1/b) [b=0 失败] 的评估方式与(a if b==0 else 1/b) [适用于b=0] 的评估方式相同。我认为这是一个有效的问题,对于不知道np.where 是如何实现的人来说是不明显的。
【解决方案2】:

在数组除法中处理 0 元素的一个老技巧是添加一个条件值:

In [63]: 1/(b+(b==0))
Out[63]: array([1.        , 1.        , 0.5       , 0.33333333])

(我几年前在apl使用过这个)。


x = numpy.where(b == 0, a, 1/b) 的评估方式与任何其他 Python 函数相同。评估每个函数参数,并将值传递给where 函数。没有“短路”或其他绕过1/b 错误值的方法。

因此,如果 1/b 返回错误,您需要更改 b 以便它不这样做,在陷阱陷阱 ZeroDivisionError 或跳过 1/b 的上下文中计算它。

In [53]: 1/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-53-9e1622b385b6> in <module>()
----> 1 1/0

ZeroDivisionError: division by zero
In [54]: 1.0/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-54-99b9b9983fe8> in <module>()
----> 1 1.0/0

ZeroDivisionError: float division by zero
In [55]: 1/np.array(0)
/usr/local/bin/ipython3:1: RuntimeWarning: divide by zero encountered in true_divide
  #!/usr/bin/python3
Out[55]: inf

ab 是什么?标量,某种大小的数组?


如果b(也可能是a)是一个数组,where 最有意义:

In [59]: b = np.array([0,1,2,3])

裸除给我一个警告,以及一个inf 元素:

In [60]: 1/b
/usr/local/bin/ipython3:1: RuntimeWarning: divide by zero encountered in true_divide
  #!/usr/bin/python3
Out[60]: array([       inf, 1.        , 0.5       , 0.33333333])

我可以使用whereinf 替换为其他内容,例如nan

In [61]: np.where(b==0, np.nan, 1/b)
/usr/local/bin/ipython3:1: RuntimeWarning: divide by zero encountered in true_divide
  #!/usr/bin/python3
Out[61]: array([       nan, 1.        , 0.5       , 0.33333333])

警告可以像@donkopotamus 显示的那样被静音。

seterr 的替代方案是 errstatewith 上下文中:

In [64]: with np.errstate(divide='ignore'):
    ...:     x = np.where(b==0, np.nan, 1/b)
    ...:     
In [65]: x
Out[65]: array([       nan, 1.        , 0.5       , 0.33333333])

How to suppress the error message when dividing 0 by 0 using np.divide (alongside other floats)?

【讨论】:

  • 问题是一些天真的人(我)可能期望它评估为:(1 if c==0 else 1/c)。这段代码对c==0 工作得很好。似乎有一个实现细节使它与众不同。我试图在我的回答中强调这一点。
  • 坦率地说,@jp_data_analysis,我相信你的回答没有抓住重点。
  • @jp_data_analysis,这不是实现细节,它是基本的 Python 语法。 Python if(和 andor)会“短路”,但这是由 Python 解释器完成的。 numpy 不会搞砸的。
  • 但是a if b==0 else 1/b是一个特殊的句法表达式,而不是一个函数。
【解决方案3】:

我用这个解决了:

x = (1/(np.where(b == 0, np.nan, b))).fillna(a) 

【讨论】:

    【解决方案4】:

    如果您希望在除以零时禁用 numpy 中的警告,请执行以下操作:

    >>> existing = numpy.seterr(divide="ignore")
    >>> # now divide by zero in numpy raises no sort of exception
    >>> 1 / numpy.zeros( (2, 2) )
    array([[ inf,  inf],
           [ inf,  inf]])
    >>> numpy.seterr(*existing)
    

    当然,这只适用于数组中的除零。执行简单的1 / 0 时不会阻止错误。

    在您的特定情况下,如果我们希望确保无论 b 是标量还是 numpy 类型都能正常工作,请执行以下操作:

    # ignore division by zero in numpy
    existing = numpy.seterr(divide="ignore")
    
    # upcast `1.0` to be a numpy type so that numpy division will always occur
    x = numpy.where(b == 0, a, numpy.float64(1.0) / b) 
    
    # restore old error settings for numpy
    numpy.seterr(*existing) 
    

    【讨论】:

      【解决方案5】:

      numpy.where 文档指出:

      如果给定 xy 并且输入数组是一维的,则 where 是 相当于::

          [xv if c else yv for (c,xv,yv) in zip(condition,x,y)]
      

      那么为什么你会看到错误?举个简单的例子:

      c = 0
      result = (1 if c==0 else 1/c)
      # 1
      

      到目前为止一切顺利。首先检查if c==0,结果为1。该代码不会尝试评估1/c。这是因为 Python 解释器处理 lazy 三元运算符,因此只计算适当的表达式。

      现在让我们把它翻译成numpy.where 方法:

      c = 0
      result = (xv if c else yv for (c, xv, yv) in zip([c==0], [1], [1/c]))
      # ZeroDivisionError
      

      在应用逻辑之前评估zip([c==0], [1], [1/c]) 时会发生错误。无法评估生成器表达式本身。作为一个函数,numpy.where 不会,实际上也不能复制 Python 三元表达式的惰性计算。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多