Hi njs,

Well, this opens a whole can of worms and exposes some extra bugs.

I was thinking primarily about the 'empty condlist' situation here, but I remember when I wrote that check in my code I was worrying about the many situations that need to be considered when empty ndarrays start to appear, if the output must be consistent for all of them.

So there's the two cases you mention plus more I'll describe below. In terms of empty arrays/lists, the obvious  problem case in the old code is this one:

 e.g. np.select([], []). Let's call it case A.

My test code (https://github.com/gbb/numpy-simple-select/blob/master/test_ss.py) was for this type of case and I was assuming that the output for ([np.array([], dtype=bool)], [np.array([])]) was the same (it isn't, as you note). 

It seems you and I are both in agreement that generating an integer as output in the case np.select([], []) seems wrong.

The behaviour for np.select([], []) is also inconsistent with the behaviour in the case np.select([np.array([], dtype=bool)], [np.array([])])   (which produces the 'right' output, if you must produce some output - though I still think it should produce a warning that you're 'selecting on nothing', if not a full error).

So, the first issue is that np.select([], []) returns '0' is just plain awful. It's not an error and it's not consistent with  np.select([np.array([], dtype=bool)], [np.array([])]).

The second thing is that while "(empty ndarray) x (empty ndarray) must produce (empty ndarray)" looks cute and natural, the human concept of selection according to rules - when there aren't any rules or choices - isn't as well defined as say, the intellectual concept of merging two empty lists. 

I'm arguing for having a think here about the meaning and error/warning output of this function from first principles rather than simply 'definition by existing implementation'. Is this really a case of 0+0, or a case of 0/0, or something inbetween? One situation is naturally silent in maths, the other requires an error and warning message  which comes from the semantics of 'dividing'. Here, I think we need at least some warning output, which comes from the semantics of selection.

The third thing, which the highlighted code in the pull request was intended to address in a consistent way, was these situations, which I think need to be defined (as meaningful or erroneous) and tested.

C - condlist contains 'normal' items to select upon along with an ndarray with no items/dimension.
D - choicelist contains 'normal' items to select upon along with an ndarray with no items (in some position).
E - condlist contains multiple ndarrays that contain no items/dimensions (similar to C, but test separately). 
F - choicelist contains multiple ndarrays that contain no items/dimensions (similar to D, but test separately). 
G - combinations of the above.

Case C1 + output (sorry, numpy 1.6.2 is all I have to hand here, and it's almost 3 in the morning)

>>> np.select([np.array([1], dtype=bool),np.array([], dtype=bool)], [np.array([1]), np.array([2])]);
ValueError: non-broadcastable output operand with shape (1) doesn't match the broadcast shape (0)

OK.. that makes sense! You gave weird input, you get an error. Now let's simply swap the arguments around... we'll still get the same error/output since the underlying problem is still the same?

Nope. 

Case C2 + output
>>> np.select([np.array([], dtype=bool),np.array([1], dtype=bool)], [np.array([2]), np.array([1])]);
 array([], dtype=float64)

Completely different program behaviour when we swap the arguments around. (This is an example of why it's a really good idea to have a little bit of logic that warns the user when there are peculiar empty arguments to a function that really needs arguments or generates an error - it picks up on strange implementation artefacts that would otherwise cause silent bugs in your code/data).

Case C3 + output - multiple empty condlists
>>> np.select([np.array([], dtype=bool),np.array([], dtype=bool)], [np.array([1]), np.array([2])]);
array([], dtype=float64)

(Looks like an error to me. The datatype & content of the output does not correspond to the available options presented as choices in the choicelist.)

To make this post shorter, I'll jump to some of the trickier ones to show how nasty the problem can get. 

Case G1 - 2 empty condlists/choicelists and 1 'meaningful' condlist with corresponding 'meaningful' choicelist at list position 1.

>>> np.select([np.array([], dtype=bool),np.array([1], dtype=bool), np.array([], dtype=bool)], [np.array([]), np.array([1]), np.array([])]);

After reading the documented specification for np.select, what is the correct output here, and how well does it correspond to your human intuitions about selecting things?

This is an example of why you really ought to be warning the user when zero item ndarrays are present or refusing to process them. The items in condlist[1] and choicelist[1] are meaningful data and enable a meaningful selection. I had expected they would match and that the other cases wouldn't be selected, since that is the first matching condition. In fact... the output from numpy 1.6.2 was:

array([], dtype=float64)

Case G2:

np.select([np.array([1], dtype=bool),np.array([1], dtype=bool), np.array([1], dtype=bool)], [np.array([1]), np.array([]), np.array([])]);

So, what will the output be this time? Check the specs before you answer. http://docs.scipy.org/doc/numpy/reference/generated/numpy.select.html .

We have a meaningful 'matching' ndarray in every position in the condlist, so we can expect the first item of the choicelist to be returned, nd.array[1]. In numpy 1.6.2, the output was actually: array([], dtype=float64). Yikes!

So...to sum up - *if* it's meaningful to make selections upon empty condlists/choicelists or upon lists that can contain one or more empty ndarrays, the behaviour should be well specified, consistent and behaviour *should correspond to a natural human understanding about selecting things as well as possible* so that user aren't shocked or misled. Datatypes and selected values should be consistent and well defined by specification, not implementation.

*If* it's meaningful to allow empty ndarrays in choicelists or condlists, I think we should warn the user that they're doing quirky things so that bugs like this don't sit unnoticed for years and because warning people in this situation corresponds to what we'd do in real life too. 

But personally I think it *isn't meaningful* for select to allow empty ndarrays, and I think that it opens the gates of hell in terms of specifying correct and natural behaviour - hence the check.

Seberg:
"This function is well defined, nothing special about it."

As I have said *several times* - the question has never been about whether how well the function is defined, the question is: does it perform a semantically meaningful (i.e. corresponding to people's intuitions about this thing) and useful operation? 

A merge_lists function which is well defined and returns a new list containing the arbitrary number '0' when given two empty parameters would be considered broken by many programmers, because that behaviour doesn't correspond with what we'd naturally expect from something called 'merge_lists'. 

The division_by_self function below is also well defined and returns consistent output (1) across inputs. It is also *wrong* in terms of the human concepts that go with the idea of dividing a number by itself. In mathematics it was decided that it's better to generate an error in the case of division by zero rather than make something up. I think the same is true of selection by zero.

function division_by_self(num=3):
  if num==0 return 1 else return num/num;