Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Numpy where and division by zero

Tags:

python

numpy

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 it was in a python-2.7 code), but it does not work in python-3.x (if b = 0 it returns an error).

How do I make it work in python-3.x?

EDIT: error message (Python 3.6.3):

ZeroDivisionError: division by zero

2 Answers

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]
like image 172
user2357112 supports Monica Avatar answered Mar 13 '26 18:03

user2357112 supports Monica


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

like image 39
hpaulj Avatar answered Mar 13 '26 16:03

hpaulj



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!