python list + empty numpy array = empty numpy array?
Asked Answered
O

1

6

Today I noticed something odd in my code, and discovered that in certain situations it run down to the execution of the following:

my_list = [0] + np.array([])

which results in my_list being the following:

array([], dtype=float64)

At the beginning I was quite confused, than I understood that the interpreter is first converting the list to a numpy array, and then trying a broadcasting operation:

>>> np.array([0]) + np.array([])
array([], dtype=float64)

I have some questions about this behaviour:

  • Why is it broadcasting?
  • Wouldn't it be better if python threw an error, at least for this particular case where a list is converted and made disappear?

Thank you for your clarifications!

Oleaceous answered 14/11, 2019 at 10:33 Comment(4)
This seems to be a specific choice of numpy for shapes (1,) + (0,). For example, np.array([0, 3]) + np.array([]) is an error.Tyus
Can you clarify your question? It is broadcasting because that is what + means for numpy. It is not an error because the result is defined as array([]).Tyus
Broadcasting to 0 is consistent with the documented rules. We just don't see it often, and are surprised.Aciculum
If it's fully answered by the documentation then I think that is enough to constitute an answer by pointing to the relevant parts. Or, there may be a dupe. I'm not sure why this is voted to be closed, though, because I am certainly surprised by seeing numpy rules play out like this. If it's the result of documented behaviour then I fail to see how it's opinion-basedChoke
P
3

First of all:

Wouldn't it be better if python threw an error, at least for this particular case where a list is converted and made disappear?

I don't think that test is possible. According to this comment:

For reversed operations like b.__radd__(a) we call the corresponding ufunc.

This means that using [0] + np.array([]) will actually call the ufunc np.add([0], np.array([])), which converts array-like lists to arrays without having a chance to decide about the size of the operands.

So broadcasting is a given. The question is then whether it's sane to have shapes (1,) and (0,) broadcast to (0,). You can think about it this way: scalars always broadcast, and 1-element 1d arrays are as good as scalars in most situations:

>>> np.add([0], [])
array([], dtype=float64)

>>> np.add(0, [])
array([], dtype=float64)

If you look at it this way it makes sense as a rule, even though I agree it's surprising, especially that non-one-length arrays won't broadcast like this. But it's not a bug (just an interesting situation for a feature).


To be more precise, what is happening with broadcasting is always that "dimensions with size 1 will broadcast". The array-like [0] has shape (1,), and the np.array([]) has shape (0,) (as opposed to a scalar np.int64() which would have shape ()!). So broadcasting happens on the singleton, and the result has shape (0,).

It gets clearer if we inject more singleton dimensions:

>>> ([0] + np.array([])).shape
(0,)

>>> ([[0]] + np.array([])).shape
(1, 0)

>>> ([[[0]]] + np.array([])).shape
(1, 1, 0)

>>> np.shape([[[0]]])
(1, 1, 1)

So for instance in the last case shapes (1, 1, 1) will nicely broadcast with a 1d array along its last dimension, and the result should indeed be (1, 1, 0) in this case.

Postconsonantal answered 14/11, 2019 at 11:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.