0

I have two NumPy arrays, say num and denom. I need to return specific values, based on if the respective elements are zero or not, in num and denom,

        r2 = []
        for denind, denel in enumerate(denom):
            numel = num[denind]
            if denel:  # Denominator is not zero
                r2.append(1 - numel/denom)
            elif numel:  # Denominator is zero, but numerator is not zero
                r2.append(0.0)
            else:  # Both denominator and numerator are zero.
                r2.append(1.0)
        return np.array(r2)

Is there a "NumPy" way of doing such an iteration.

Manoj
  • 961
  • 4
  • 11
  • 37
  • Thanks for pointing that out. I've edited it, by the way, numel is used in the `if numel` condition. – Manoj Oct 09 '13 at 14:33

3 Answers3

2

Yes, you can use a boolean array as an index to fix up an array after your division. And you can use isinf(a) and isnan(a) to get the desired boolean arrays for indexing a.

with numpy.errstate(divide='ignore'):
    res = 1.0 - num/double(den);
res[isinf(res)] = 0.0;   # zero denominator will give +- infinity
res[isnan(res)] = 1.0;   # both zero will give nan

The magic here is that the boolean-array index returns a view into res which only selects those elements for which the boolean was True. If you read from that selection, you see a flat array, but if you write to it (as above) then it modifies the original array.

Also, I explicitly cast den to double because if both operands were integers there would by horror.

Update: It seems you should cast den, if you cast num it will still work, but you get a warning. Safest thing is to cast both.

Adrian Ratnapala
  • 5,485
  • 2
  • 29
  • 39
  • Thanks, but it does give me a RuntimeWarning, that I have to catch. – Manoj Oct 09 '13 at 14:07
  • In python 2.7, if `num` and `den` are integer it won't work (you get `0` instead of `inf` and `nan`. In python 3 you don't need the cast – Francesco Montesano Oct 09 '13 at 14:07
  • you don't have to catch warnings. They just warn you and don't interrupt the execution. Of course you can catch it if you want. [numpy.seterr](http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html#numpy.seterr) should help you getting rid of the warning – Francesco Montesano Oct 09 '13 at 14:09
  • Umm.. sorry, I had meant that I don't want the user to know there is a warning. – Manoj Oct 09 '13 at 14:11
  • In Python2 I get no warning as long as I make sure that the argument is an array of doubles. @Fransesco how do numpy arrays behave in python3? Do they use the exact representation or does it automatically give you floating point? – Adrian Ratnapala Oct 09 '13 at 14:14
  • num and den are always arrays of floats. So that shouldn't be a problem. – Manoj Oct 09 '13 at 14:15
  • in python3 when doing division the cast is automatic: 3/2=1.5. If you want rounding you do 3//2=1 – Francesco Montesano Oct 09 '13 at 14:15
  • @Manoj: either try to play with numpy.seterr or use [`try/except'](http://docs.python.org/2/tutorial/errors.html#handling-exceptions) to hide the warning – Francesco Montesano Oct 09 '13 at 14:17
  • @Manoj, my (updated) version which casts `den` works for me in both Python 2.7.3 and 3.2.3. as @Fransesco says, there should be some error setting in numpy that you can use. And try the explicit cast anyway - it's always nice to check your assumptions. – Adrian Ratnapala Oct 09 '13 at 14:21
  • OK the fragment now temporarily disable the error message, just while the division is happening (see http://stackoverflow.com/questions/10519237/python-how-to-avoid-runtimewarning-in-function-definition). I hope that works for you @Manoj! – Adrian Ratnapala Oct 09 '13 at 14:29
1

How about doing the minimal amount of division work?

r2 = np.ones_like(numel, dtype=float)
num_mask = (numel != 0)
den_mask = (denel != 0)
mask = num_mask & den_mask
r2[mask] = 1 - numel[mask]/denel[mask]
mask = ~den_mask & num_mask
r2[mask] = 0
Jaime
  • 65,696
  • 17
  • 124
  • 159
  • This seems really elegant to me. Is there any difference if I do, `r2 = np.ones(numel.shape[1], dtype=float)` in the first line? – Manoj Oct 09 '13 at 15:16
  • No, but `ones_like` saves you having to extract the shape of the array manually. – Jaime Oct 10 '13 at 06:33
1

You could use a nested np.where statement

numpy.where(denom !=0, 1 - numel/denom, np.where(numel != 0, 0, 1))
Greg Whittier
  • 3,105
  • 1
  • 19
  • 14