3

(note: the original question was a bit different, to which the other answer applies; see the revision history for the original question.)

Is there a uniform way to index numpy arrays, when these arrays could be scalar as well?

I'm trying to write a function that deals with a float, a list of floats, or a 0/1D numpy array. To deal with that uniformly, I use numpy.asarray(), which works fine overall (I don't mind returning a numpy.float64 when the input is a standard Python float).

Problems arise when I need to deal with conditional operations and an intermediate array function, something like:

value = np.asarray(5.5)
mask = value > 5
tmpvalue = np.asarray(np.cos(value))
tmpvalue[mask] = value

This will throw an exception:

Traceback (most recent call last):
  File "testscalars.py", line 27, in <module>
    tmpvalue[mask] = value
IndexError: 0-d arrays can't be indexed

Is there any elegant solution to this?

5
  • 1
    "this fails when a is a scalar" - aren't you turning a into a 0D array if it comes in as a scalar? Indexing a 0D array with a boolean mask actually works. Heck, indexing a NumPy scalar with a boolean mask even works, except that scalars are immutable, so you can't do the assignment. Anyway, it might help to see a concrete example of the kind of function you're trying to write, and how you currently write it. Commented Jun 23, 2015 at 8:12
  • @user2357112 Well, I'm confused now; I did plenty of numpy index exceptions when running my code. I'll try and come up with a reproducible example, because it does look indeed that the above actually works without problems. Commented Jun 23, 2015 at 8:16
  • np.cos is a ufunc. It is wrapped in a layer of C code that ensures correct handling of dimensions, in and out. vectorize is supposed to do something similar to user functions. Commented Jun 23, 2015 at 11:07
  • @user2357112 Your comment put me on the right track: 0-d arrays can be properly indexed, and the problem was somewhere else. While not the actual answer, I've provided the solution to my problem as an answer. I'm not sure whether to actually delete the question, because the answer to the actual question is simply: it's already there. Commented Jun 24, 2015 at 3:49
  • @hpaulj I did think about vectorize, but I guess I just don't like the idea that it wraps a loop around the function and essentially gets rid of the numpy benefits (in case my input array would be 100K or more elements). Commented Jun 24, 2015 at 3:50

2 Answers 2

1

It turns out this problem pertains to numpy 1.8 and before; upgrading to numpy 1.9(.2) fixes this.

The numpy 1.9 release notes have this to say:

Boolean indexing into scalar arrays will always return a new 1-d array. This means that array(1)[array(True)] gives array([1]) and not the original array.

which conveniently will turn tmpvalue[mask] temporarily into a 1D array, allowing it to be assigned to value:

tmpvalue[mask] = value
Sign up to request clarification or add additional context in comments.

Comments

0

While not the actual answer to the question asked, the following is essentially what bit me and caused (Type)errors:

value = numpy.asarray(5.5)
mask = value > 5
tmpvalue = numpy.cos(value)
tmpvalue[mask] = value[mask]

The problem here is that value is of type numpy.ndarray, but since it's a 0-d array, numpy.cos returns a numpy.scalar, which can't be indexed.

I think this numpy issue is directly related to this problem.

For now, it appears that the simplest solution is to wrap the numpy ufuncs with numpy.asarray:

value = numpy.asarray(5.5)
mask = value > 5
tmpvalue = numpy.asarray(numpy.cos(value))
tmpvalue[mask] = value[mask]

which I've tested successfully with inputs 5.5, 4.5, [5.5], [4.5] and [4.5, 5.5].

Note that this behaviour also applies to even more common operations, like addition:

>>> x = numpy.asarray(5)
>>> y = numpy.asarray(6)
>>> z = x + y
>>> type(x), type(y), type(z)
(<class 'numpy.ndarray'>, <class 'numpy.ndarray'>, <class 'numpy.int64'>)

3 Comments

Alternatively, you could invert the mask and assign into value instead of tmpvalue.
I could probably turn things around, yes. But typically, the actual code is a bit more complicated, and this is just to show what the actual problem is. As per the first comment in the Github numpy issue tracker: "Ideally ufuncs should return 0d arrays in case of 0d array inputs, and scalars in case of scalar inputs. "
Yeah, I certainly would have expected a 0D array output myself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.