Numpy where and division by zero

后端 未结 5 1105
盖世英雄少女心
盖世英雄少女心 2020-12-04 01:00

I need to compute x in the following way (legacy code):

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

I suppose it worked in python-2.x (as

相关标签:
5条回答
  • 2020-12-04 01:31

    If you wish to disable warnings in numpy while you divide by zero, then do something like:

    >>> 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)
    

    Of course this only governs division by zero in an array. It will not prevent an error when doing a simple 1 / 0.

    In your particular case, if we wish to ensure that we work whether b is a scalar or a numpy type, do as follows:

    # 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) 
    
    0 讨论(0)
  • 2020-12-04 01:46

    The numpy.where documentation states:

    If x and y are given and input arrays are 1-D, where is equivalent to::

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

    So why do you see the error? Take this trivial example:

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

    So far so good. if c==0 is checked first and the result is 1. The code does not attempt to evaluate 1/c. This is because the Python interpreter processes a lazy ternary operator and so only evaluates the appropriate expression.

    Now let's translate this into numpy.where approach:

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

    The error occurs in evaluating zip([c==0], [1], [1/c]) before even the logic is applied. The generator expression itself can't be evaluated. As a function, numpy.where does not, and indeed cannot, replicate the lazy computation of Python's ternary expression.

    0 讨论(0)
  • 2020-12-04 01:49

    I solved it using this:

    x = (1/(np.where(b == 0, np.nan, b))).fillna(a) 
    
    0 讨论(0)
  • 2020-12-04 01:51

    numpy.where is not conditional execution; it is conditional selection. Python function parameters are always completely evaluated before a function call, so there is no way for a function to conditionally or partially evaluate its parameters.

    Your code:

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

    tells Python to invert every element of b and then select elements from a or 1/b based on elements of b == 0. Python never even reaches the point of selecting elements, because computing 1/b fails.

    You can avoid this problem by only inverting the nonzero parts of b. Assuming a and b have the same shape, it could look like this:

    x = numpy.empty_like(b)
    mask = (b == 0)
    x[mask] = a[mask]
    x[~mask] = 1/b[~mask]
    
    0 讨论(0)
  • 2020-12-04 01:55

    A old trick for handling 0 elements in an array division is to add a conditional value:

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

    (I used this years ago in apl).


    x = numpy.where(b == 0, a, 1/b) is evaluated in the same way as any other Python function. Each function argument is evaluated, and the value passed to the where function. There's no 'short-circuiting' or other method of bypassing bad values of 1/b.

    So if 1/b returns a error you need to either change b so it doesn't do that, calculate it in context that traps traps the ZeroDivisionError, or skips the 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
    

    What are a and b? Scalars, arrays of some size?


    where makes most sense if b (and maybe a) is an array:

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

    The bare division gives me a warning, and an inf element:

    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])
    

    I could use where to replace that inf with something else, for example a 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])
    

    The warning can be silenced as @donkopotamus shows.

    An alternative to seterr is errstate in a with context:

    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)?

    0 讨论(0)
提交回复
热议问题