167

I'm trying to perform an element wise divide in python, but if a zero is encountered, I need the quotient to just be zero.

For example:

array1 = np.array([0, 1, 2])
array2 = np.array([0, 1, 1])

array1 / array2 # should be np.array([0, 1, 2])

I could always just use a for-loop through my data, but to really utilize numpy's optimizations, I need the divide function to return 0 upon divide by zero errors instead of ignoring the error.

Unless I'm missing something, it doesn't seem numpy.seterr() can return values upon errors. Does anyone have any other suggestions on how I could get the best out of numpy while setting my own divide by zero error handling?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
hlin117
  • 20,764
  • 31
  • 72
  • 93

9 Answers9

327

In numpy v1.7+, you can take advantage of the "where" option for ufuncs. You can do things in one line and you don't have to deal with the errstate context manager.

>>> a = np.array([-1, 0, 1, 2, 3], dtype=float)
>>> b = np.array([ 0, 0, 0, 2, 2], dtype=float)

# If you don't pass `out` the indices where (b == 0) will be uninitialized!
>>> c = np.divide(a, b, out=np.zeros_like(a), where=b!=0)
>>> print(c)
[ 0.   0.   0.   1.   1.5]

In this case, it does the divide calculation anywhere 'where' b does not equal zero. When b does equal zero, then it remains unchanged from whatever value you originally gave it in the 'out' argument.

Andy Jones
  • 4,723
  • 2
  • 19
  • 24
DStauffman
  • 3,960
  • 2
  • 21
  • 30
  • 6
    If `a` and/or `b` might be integer arrays, then it's the same concept, you just need to explicitly set the correct output type: `c = np.divide(a, b, out=np.zeros(a.shape, dtype=float), where=b!=0)` – DStauffman Dec 10 '19 at 20:04
  • 1
    `out=np.zeros_like(a)` is critical, as stated in the commented line. – Jonatan Öström Jan 31 '20 at 22:45
  • 1
    If I use ```np.divide(a, b, out=np.zeros_like(a), where=b!=0)```, I get the error ```Assigning to function call which doesn't return```. The weird thing is, I use it twice and the error only pops up once. – Jelmer Mulder May 06 '20 at 18:59
  • 2
    In case anyone is interested in which is fastest, this method is faster than @denis's/@Franck Dernoncourt's answer, running one million cycles I'm getting 8 seconds for this versus 11 seconds for theirs. – kory Oct 30 '20 at 10:55
  • 2
    In my case it wasn't only float values precisely equal to zero that need to be 'zeroed', but those values 'close' to zero. So the following is useful `np.divide(a, b, out=np.zeros_like(a), where=~np.isclose(b,np.zeros_like(b)))` and you can set the `atol` and `rtol` of `isclose` as suits your use case. – Colin Feb 01 '22 at 05:06
57

Building on @Franck Dernoncourt's answer, fixing -1 / 0 and my bug on scalars:

def div0( a, b, fill=np.nan ):
    """ a / b, divide by 0 -> `fill`
        div0( [-1, 0, 1], 0, fill=np.nan) -> [nan nan nan]
        div0( 1, 0, fill=np.inf ) -> inf
    """
    with np.errstate(divide='ignore', invalid='ignore'):
        c = np.true_divide( a, b )
    if np.isscalar( c ):
        return c if np.isfinite( c ) \
            else fill
    else:
        c[ ~ np.isfinite( c )] = fill
        return c
denis
  • 21,378
  • 10
  • 65
  • 88
  • Thanks, I didn't even catch that bug with @Frank Dernoncourt's code. – hlin117 Feb 29 '16 at 17:38
  • Hi, I'm trying to do array math and I want 0/0 to result in 0 but I also want to ignore np.NaN in my calculations as well. Will this work for that? Also, I am trying to understand. What does c[ ~ np.isfinite( c )] = 0 do? I've never used ~ in python. What is it for? Thank you – user20408 May 17 '16 at 14:41
  • @user20408, `~` inverts `True` and `False` in numpy arrays: `print ~ np.array([ True, False, False ])`. `c[ ~ np.isfinite( c )] = 0` means: find the positions where `c` is finite, invert those to NOT finite with `~`, and set the not-finite values to 0. See also http://stackoverflow.com/search?q=[numpy]+"boolean+indexing" – denis May 20 '16 at 09:07
53

Building on the other answers, and improving on:

Code:

import numpy as np

a = np.array([0,0,1,1,2], dtype='float')
b = np.array([0,1,0,1,3], dtype='float')

with np.errstate(divide='ignore', invalid='ignore'):
    c = np.true_divide(a,b)
    c[c == np.inf] = 0
    c = np.nan_to_num(c)

print('c: {0}'.format(c))

Output:

c: [ 0.          0.          0.          1.          0.66666667]
Franck Dernoncourt
  • 77,520
  • 72
  • 342
  • 501
  • 2
    Good job for checking `0/0` as well as `1/0` errors. – hlin117 Aug 20 '15 at 04:31
  • I tried your method with the example arrays given in [DStauffman's answer](https://stackoverflow.com/a/37977222/6126315) and it seems to result in very high numbers instead of np.inf, which remains at the final result – Gal Avineri Sep 30 '19 at 11:01
  • I would discourage this approach. If either `a` or `b` contains `NaN`, your solution suddenly gives `0` as a result. This can easily hide errors in your code and is absolutely unexpected. – DerWeh Jan 13 '20 at 11:12
  • 1
    According to recent [numpy manual](https://numpy.org/doc/stable/reference/generated/numpy.nan_to_num.html?highlight=numpy%20nan_to_num#numpy.nan_to_num) nan_to_num() takes values to substitute for positive inf and negative inf as well. `numpy.nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None)` is the signature. – Craig Hicks May 16 '20 at 06:38
20

DEPRECATED (PYTHON 2 SOLUTION):

One-liner (throws warning)

np.nan_to_num(array1 / array2)
Ulf Aslak
  • 7,876
  • 4
  • 34
  • 56
  • Divide by zero (confusingly, since it's incorrect!) gives `inf` in numpy, not undefined or `nan`. So this will set divide by zero to *[a very large number](https://numpy.org/doc/stable/reference/generated/numpy.nan_to_num.html)*, not `0` as one might expect and the OP asks. You can get around this by setting `posinf=0`. – c z Feb 18 '21 at 12:40
  • Ah, yeah this answer is so old that the code is in Python 2 (where it works). – Ulf Aslak Feb 19 '21 at 13:37
17

Try doing it in two steps. Division first, then replace.

with numpy.errstate(divide='ignore'):
    result = numerator / denominator
    result[denominator == 0] = 0

The numpy.errstate line is optional, and just prevents numpy from telling you about the "error" of dividing by zero, since you're already intending to do so, and handling that case.

Pi Marillion
  • 4,465
  • 1
  • 19
  • 20
3

You can also replace based on inf, only if the array dtypes are floats, as per this answer:

>>> a = np.array([1,2,3], dtype='float')
>>> b = np.array([0,1,3], dtype='float')
>>> c = a / b
>>> c
array([ inf,   2.,   1.])
>>> c[c == np.inf] = 0
>>> c
array([ 0.,  2.,  1.])
Community
  • 1
  • 1
Lee
  • 29,398
  • 28
  • 117
  • 170
0

One answer I found searching a related question was to manipulate the output based upon whether the denominator was zero or not.

Suppose arrayA and arrayB have been initialized, but arrayB has some zeros. We could do the following if we want to compute arrayC = arrayA / arrayB safely.

In this case, whenever I have a divide by zero in one of the cells, I set the cell to be equal to myOwnValue, which in this case would be zero

myOwnValue = 0
arrayC = np.zeros(arrayA.shape())
indNonZeros = np.where(arrayB != 0)
indZeros = np.where(arrayB = 0)

# division in two steps: first with nonzero cells, and then zero cells
arrayC[indNonZeros] = arrayA[indNonZeros] / arrayB[indNonZeros]
arrayC[indZeros] = myOwnValue # Look at footnote

Footnote: In retrospect, this line is unnecessary anyways, since arrayC[i] is instantiated to zero. But if were the case that myOwnValue != 0, this operation would do something.

hlin117
  • 20,764
  • 31
  • 72
  • 93
0

A solution with try/except to manage any kind of those numpy RuntimeWarnings:

with np.errstate(all='raise'):
    try:
        array3 = array1/array2
    except:
        array3 = array1 # Give whatever default value you like
Marcnu
  • 11
  • 2
-1

An other solution worth mentioning :

>>> a = np.array([1,2,3], dtype='float')
>>> b = np.array([0,1,3], dtype='float')
>>> b_inv = np.array([1/i if i!=0 else 0 for i in b])
>>> a*b_inv
array([0., 2., 1.])
T. Gwen
  • 104
  • 5