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
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)
The numpy.where documentation states:
If
x
andy
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.
I solved it using this:
x = (1/(np.where(b == 0, np.nan, b))).fillna(a)
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]
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)?