What is up with raw boolean indices (like a[False])?

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

What is up with raw boolean indices (like a[False])?

Aaron Meurer
I've been trying to figure out this behavior. It doesn't seem to be
documented at https://numpy.org/doc/stable/reference/arrays.indexing.html

>>> a = np.empty((2, 3))
>>> a.shape
(2, 5)
>>> a[True].shape
(1, 2, 5)
>>> a[False].shape
(0, 2, 5)

It seems like indexing with a raw boolean (True or False) adds an axis
with a dimension 1 or 0, resp.

Except it only works once:

>>> a[:,False]
array([], shape=(2, 0, 3), dtype=float64)
>>> a[:,False, False]
array([], shape=(2, 0, 3), dtype=float64)
>>> a[:,False,True].shape
(2, 0, 3)
>>> a[:,True,False].shape
(2, 0, 3)

The docs say "A single boolean index array is practically identical to
x[obj.nonzero()]". I have a hard time seeing this as an extension of
that, since indexing by `np.nonzero(False)` or `np.nonzero(True)`
*replaces* the given axis.

 >>> a[np.nonzero(True)].shape
(1, 3)
>>> a[np.nonzero(False)].shape
(0, 3)

I think at best this behavior should be documented. I'm trying to
understand the motivation for it, or if it's even intentional. And in
particular, why do multiple boolean indices not insert multiple axes?
It would actually be useful to be able to generically add length 0
axes using an index, similar to how `newaxis` adds a length 1 axis.

Aaron Meurer
_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Sebastian Berg
On Mon, 2020-07-06 at 12:39 -0600, Aaron Meurer wrote:

> I've been trying to figure out this behavior. It doesn't seem to be
> documented at
> https://numpy.org/doc/stable/reference/arrays.indexing.html
>
> > > > a = np.empty((2, 3))
> > > > a.shape
> (2, 5)
> > > > a[True].shape
> (1, 2, 5)
> > > > a[False].shape
> (0, 2, 5)
>
> It seems like indexing with a raw boolean (True or False) adds an
> axis
> with a dimension 1 or 0, resp.
>
> Except it only works once:
>
> > > > a[:,False]
> array([], shape=(2, 0, 3), dtype=float64)
> > > > a[:,False, False]
> array([], shape=(2, 0, 3), dtype=float64)
> > > > a[:,False,True].shape
> (2, 0, 3)
> > > > a[:,True,False].shape
> (2, 0, 3)
>
> The docs say "A single boolean index array is practically identical
> to
> x[obj.nonzero()]". I have a hard time seeing this as an extension of
> that, since indexing by `np.nonzero(False)` or `np.nonzero(True)`
> *replaces* the given axis.
>
>  >>> a[np.nonzero(True)].shape
> (1, 3)
> > > > a[np.nonzero(False)].shape
> (0, 3)
>
> I think at best this behavior should be documented. I'm trying to
> understand the motivation for it, or if it's even intentional. And in
> particular, why do multiple boolean indices not insert multiple axes?
> It would actually be useful to be able to generically add length 0
> axes using an index, similar to how `newaxis` adds a length 1 axis.
Its fully intentional as it is the correct generalization from an N-D
boolean index to include a 0-D boolean index.
To be fair, there is a footnote in the "Detailed notes" saying that:
"the nonzero equivalence for Boolean arrays does not hold for zero
dimensional boolean arrays.", this is for technical reasons since
`nonzero` does not do useful things for 0-D input.


In any case, a boolean index always does the following:

1. It will *remove as many dimensions as the index has, because this
   is the number of dimensions effectively indexed by it*
2. It will add a single new dimension at the same place.  The length of
   this new dimension is the number of `True` elements.
3. If you have multiple advanced indexing you get annoying broadcasting
   of all of these. That is *always* confusing for boolean indices.
   0-D should not be too special there...

And this generalizes to 0-D just as well, even if it may be a bit
surprising at first.


I have written much of this more clearly once before in this NEP, which
may be a good read to _really_ understand it:

https://numpy.org/neps/nep-0021-advanced-indexing.html

In general, I wonder if going into much depth about how 0-D arrays are
not actually really handled very special is good.  Yes, its confusing
on its own, but it seems also a bit like overloading the user with
unnecessary knowledge?

Cheers,

Sebastian



>
> Aaron Meurer
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion
>


_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Aaron Meurer
In reply to this post by Aaron Meurer
> Its fully intentional as it is the correct generalization from an N-D
> boolean index to include a 0-D boolean index.
> To be fair, there is a footnote in the "Detailed notes" saying that:
> "the nonzero equivalence for Boolean arrays does not hold for zero
> dimensional boolean arrays.", this is for technical reasons since
> `nonzero` does not do useful things for 0-D input.
>
> In any case, a boolean index always does the following:
> 1. It will *remove as many dimensions as the index has, because this
>    is the number of dimensions effectively indexed by it*
> 2. It will add a single new dimension at the same place.  The length of
>    this new dimension is the number of `True` elements.
> 3. If you have multiple advanced indexing you get annoying broadcasting
>    of all of these. That is *always* confusing for boolean indices.
>    0-D should not be too special there...
> And this generalizes to 0-D just as well, even if it may be a bit
> surprising at first.

I guess if those are the base rules for boolean indices this makes
sense. So that brings up the question then, is there a way to add
arbitrary empty dimensions using an index?

>
> I have written much of this more clearly once before in this NEP, which
> may be a good read to _really_ understand it:
> https://numpy.org/neps/nep-0021-advanced-indexing.html
> In general, I wonder if going into much depth about how 0-D arrays are
> not actually really handled very special is good.  Yes, its confusing
> on its own, but it seems also a bit like overloading the user with
> unnecessary knowledge?

The page I referenced is already written like a very highly technical
document, so I think it should embrace that and fully describe the
spec of NumPy indexing. NumPy could use more user-friendly
documentation for indexing, but that page ain't it. FWIW, I wrote some
documentation on slices of my own here
https://quansight.github.io/ndindex/slices.html. I eventually plan to
extend this to all forms of NumPy indexing. Anyway, the three bullet
points you mentioned above would be helpful to include in the docs.

> Cheers,
> Sebastian
_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Aaron Meurer
> > 3. If you have multiple advanced indexing you get annoying broadcasting
> >    of all of these. That is *always* confusing for boolean indices.
> >    0-D should not be too special there...

OK, now that I am learning more about advanced indexing, this
statement is confusing to me. It seems that scalar boolean indices do
not broadcast. For example:

>>> np.arange(2)[False, np.array([True, False])]
array([], dtype=int64)
>>> np.arange(2)[tuple(np.broadcast_arrays(False, np.array([True, False])))]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: too many indices for array: array is 1-dimensional, but 2
were indexed

And indeed, the docs even say, as you noted, "the nonzero equivalence
for Boolean arrays does not hold for zero dimensional boolean arrays,"
which I guess also applies to the broadcasting.

From what I can tell, the logic is that all integer and boolean arrays
(and scalar ints) are broadcast together, *except* for boolean
scalars. Then the first boolean scalar is replaced with and(all
boolean scalars) and the rest are removed from the index. Then that
index adds a length 1 axis if it is True and 0 if it is False.

So they don't broadcast, but rather "fake broadcast". I still contend
that it would be much more useful, if True were a synonym for newaxis
and False worked like newaxis but instead added a length 0 axis.
Alternately, True and False scalars should behave exactly like all
other boolean arrays with no exceptions (i.e., work like np.nonzero(),
broadcast, etc.). This would be less useful, but more consistent.

Aaron Meurer
_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Sebastian Berg
On Wed, 2020-08-19 at 18:07 -0600, Aaron Meurer wrote:
> > > 3. If you have multiple advanced indexing you get annoying
> > > broadcasting
> > >    of all of these. That is *always* confusing for boolean
> > > indices.
> > >    0-D should not be too special there...
>
> OK, now that I am learning more about advanced indexing, this
> statement is confusing to me. It seems that scalar boolean indices do
> not broadcast. For example:

Well, broadcasting means you broadcast the *nonzero result* unless I am
very confused... There is a reason I dismissed it. We could (and
arguably should) just deprecate it.  And I have doubts anyone would
even notice.

>
> > > > np.arange(2)[False, np.array([True, False])]
> array([], dtype=int64)
> > > > np.arange(2)[tuple(np.broadcast_arrays(False, np.array([True,
> > > > False])))]
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> IndexError: too many indices for array: array is 1-dimensional, but 2
> were indexed
>
> And indeed, the docs even say, as you noted, "the nonzero equivalence
> for Boolean arrays does not hold for zero dimensional boolean
> arrays,"
> which I guess also applies to the broadcasting.
I actually think that probably also holds. Nonzero just behave weird
for 0D because arrays (because it returns a tuple).
But since broadcasting the nonzero result is so weird, and since 0-D
booleans require some additional logic and don't generalize 100% (code
wise), I won't rule out there are differences.

> From what I can tell, the logic is that all integer and boolean
> arrays

Did you try that? Because as I said above, IIRC broadcasting the
boolean array without first calling `nonzero` isn't really whats going
on. And I don't know how it could be whats going on, since adding
dimensions to a boolean index would have much more implications?

- Sebastian


> (and scalar ints) are broadcast together, *except* for boolean
> scalars. Then the first boolean scalar is replaced with and(all
> boolean scalars) and the rest are removed from the index. Then that
> index adds a length 1 axis if it is True and 0 if it is False.
>
> So they don't broadcast, but rather "fake broadcast". I still contend
> that it would be much more useful, if True were a synonym for newaxis
> and False worked like newaxis but instead added a length 0 axis.
> Alternately, True and False scalars should behave exactly like all
> other boolean arrays with no exceptions (i.e., work like
> np.nonzero(),
> broadcast, etc.). This would be less useful, but more consistent.
>
> Aaron Meurer
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion
>

_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Aaron Meurer
You're right. I was confusing the broadcasting logic for boolean arrays.

However, I did find this example

>>> np.arange(10).reshape((2, 5))[np.array([[0, 0, 0, 0, 0]], dtype=np.int64), False]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: shape mismatch: indexing arrays could not be broadcast
together with shapes (1,5) (0,)

That certainly seems to imply there is some broadcasting being done.

Aaron Meurer

On Wed, Aug 19, 2020 at 6:55 PM Sebastian Berg
<[hidden email]> wrote:

>
> On Wed, 2020-08-19 at 18:07 -0600, Aaron Meurer wrote:
> > > > 3. If you have multiple advanced indexing you get annoying
> > > > broadcasting
> > > >    of all of these. That is *always* confusing for boolean
> > > > indices.
> > > >    0-D should not be too special there...
> >
> > OK, now that I am learning more about advanced indexing, this
> > statement is confusing to me. It seems that scalar boolean indices do
> > not broadcast. For example:
>
> Well, broadcasting means you broadcast the *nonzero result* unless I am
> very confused... There is a reason I dismissed it. We could (and
> arguably should) just deprecate it.  And I have doubts anyone would
> even notice.
>
> >
> > > > > np.arange(2)[False, np.array([True, False])]
> > array([], dtype=int64)
> > > > > np.arange(2)[tuple(np.broadcast_arrays(False, np.array([True,
> > > > > False])))]
> > Traceback (most recent call last):
> >   File "<stdin>", line 1, in <module>
> > IndexError: too many indices for array: array is 1-dimensional, but 2
> > were indexed
> >
> > And indeed, the docs even say, as you noted, "the nonzero equivalence
> > for Boolean arrays does not hold for zero dimensional boolean
> > arrays,"
> > which I guess also applies to the broadcasting.
>
> I actually think that probably also holds. Nonzero just behave weird
> for 0D because arrays (because it returns a tuple).
> But since broadcasting the nonzero result is so weird, and since 0-D
> booleans require some additional logic and don't generalize 100% (code
> wise), I won't rule out there are differences.
>
> > From what I can tell, the logic is that all integer and boolean
> > arrays
>
> Did you try that? Because as I said above, IIRC broadcasting the
> boolean array without first calling `nonzero` isn't really whats going
> on. And I don't know how it could be whats going on, since adding
> dimensions to a boolean index would have much more implications?
>
> - Sebastian
>
>
> > (and scalar ints) are broadcast together, *except* for boolean
> > scalars. Then the first boolean scalar is replaced with and(all
> > boolean scalars) and the rest are removed from the index. Then that
> > index adds a length 1 axis if it is True and 0 if it is False.
> >
> > So they don't broadcast, but rather "fake broadcast". I still contend
> > that it would be much more useful, if True were a synonym for newaxis
> > and False worked like newaxis but instead added a length 0 axis.
> > Alternately, True and False scalars should behave exactly like all
> > other boolean arrays with no exceptions (i.e., work like
> > np.nonzero(),
> > broadcast, etc.). This would be less useful, but more consistent.
> >
> > Aaron Meurer
> > _______________________________________________
> > NumPy-Discussion mailing list
> > [hidden email]
> > https://mail.python.org/mailman/listinfo/numpy-discussion
> >
>
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion
_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Sebastian Berg
On Thu, 2020-08-20 at 12:21 -0600, Aaron Meurer wrote:

> You're right. I was confusing the broadcasting logic for boolean
> arrays.
>
> However, I did find this example
>
> > > > np.arange(10).reshape((2, 5))[np.array([[0, 0, 0, 0, 0]],
> > > > dtype=np.int64), False]
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> IndexError: shape mismatch: indexing arrays could not be broadcast
> together with shapes (1,5) (0,)
>
> That certainly seems to imply there is some broadcasting being done.
Yes, it broadcasts the array after converting it with `nonzero`, i.e.
its much the same as:

   indices = [[0, 0, 0, 0, 0]], *np.nonzero(False)
   indices = np.broadcast_arrays(*indices)

will give the same result (see also `np.ix_` which converts booleans as
well for this reason, to give you outer indexing).
I was half way through a mock-up/pseudo code, but thought you likely
wasn't sure it was ending up clear. It sounds like things are probably
falling into place for you (if they are not, let me know what might
help you):

1. Convert all boolean indices into a series of integer indices using
   `np.nonzero(index)`

2. For True/False scalars, that doesn't work, because `np.nonzero()`.
 
 `nonzero` gave us an index array (which is good, we obviously want
   
one), but we need to index into `boolean_index.ndim == 0`
   dimensions!
   So that won't work, the approach using `nonzero` cannot generalize
 
 here, although boolean indices generalize perfectly.

   The solution to the dilemma is simple: If we have to index one
   dimension, but should be indexing zero, then we simply add that
   dimension to the original array (or at least pretend there was
   an additional dimension).

3. Do normal indexing with the result *including broadcasting*,
   we forget it was converted.

The other way to solve it would be to always reshape the original array
to combine all axes being indexed by a single boolean index into one
axis and then index it using `np.flatnonzero`.  (But that would get a
different result if you try to broadcast!)


In any case, I am not sure I would bother with making sense of this,
except for sports!
Its pretty much nonsense and I think the time understanding it is
probably better spend deprecating it.  The only reason I did not
Deprecate itt before, is that I tried to do be minimal in the changes
when I rewrote advanced indexing (and generalized boolean scalars
correctly) long ago.  That was likely the right start/choice at the
time, since there were much bigger fish to catch, but I do not think
anything is holding us back now.

Cheers,

Sebastian


>
> Aaron Meurer
>
> On Wed, Aug 19, 2020 at 6:55 PM Sebastian Berg
> <[hidden email]> wrote:
> > On Wed, 2020-08-19 at 18:07 -0600, Aaron Meurer wrote:
> > > > > 3. If you have multiple advanced indexing you get annoying
> > > > > broadcasting
> > > > >    of all of these. That is *always* confusing for boolean
> > > > > indices.
> > > > >    0-D should not be too special there...
> > >
> > > OK, now that I am learning more about advanced indexing, this
> > > statement is confusing to me. It seems that scalar boolean
> > > indices do
> > > not broadcast. For example:
> >
> > Well, broadcasting means you broadcast the *nonzero result* unless
> > I am
> > very confused... There is a reason I dismissed it. We could (and
> > arguably should) just deprecate it.  And I have doubts anyone would
> > even notice.
> >
> > > > > > np.arange(2)[False, np.array([True, False])]
> > > array([], dtype=int64)
> > > > > > np.arange(2)[tuple(np.broadcast_arrays(False,
> > > > > > np.array([True,
> > > > > > False])))]
> > > Traceback (most recent call last):
> > >   File "<stdin>", line 1, in <module>
> > > IndexError: too many indices for array: array is 1-dimensional,
> > > but 2
> > > were indexed
> > >
> > > And indeed, the docs even say, as you noted, "the nonzero
> > > equivalence
> > > for Boolean arrays does not hold for zero dimensional boolean
> > > arrays,"
> > > which I guess also applies to the broadcasting.
> >
> > I actually think that probably also holds. Nonzero just behave
> > weird
> > for 0D because arrays (because it returns a tuple).
> > But since broadcasting the nonzero result is so weird, and since 0-
> > D
> > booleans require some additional logic and don't generalize 100%
> > (code
> > wise), I won't rule out there are differences.
> >
> > > From what I can tell, the logic is that all integer and boolean
> > > arrays
> >
> > Did you try that? Because as I said above, IIRC broadcasting the
> > boolean array without first calling `nonzero` isn't really whats
> > going
> > on. And I don't know how it could be whats going on, since adding
> > dimensions to a boolean index would have much more implications?
> >
> > - Sebastian
> >
> >
> > > (and scalar ints) are broadcast together, *except* for boolean
> > > scalars. Then the first boolean scalar is replaced with and(all
> > > boolean scalars) and the rest are removed from the index. Then
> > > that
> > > index adds a length 1 axis if it is True and 0 if it is False.
> > >
> > > So they don't broadcast, but rather "fake broadcast". I still
> > > contend
> > > that it would be much more useful, if True were a synonym for
> > > newaxis
> > > and False worked like newaxis but instead added a length 0 axis.
> > > Alternately, True and False scalars should behave exactly like
> > > all
> > > other boolean arrays with no exceptions (i.e., work like
> > > np.nonzero(),
> > > broadcast, etc.). This would be less useful, but more consistent.
> > >
> > > Aaron Meurer
> > > _______________________________________________
> > > NumPy-Discussion mailing list
> > > [hidden email]
> > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > >
> >
> > _______________________________________________
> > NumPy-Discussion mailing list
> > [hidden email]
> > https://mail.python.org/mailman/listinfo/numpy-discussion
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion
>

_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Sebastian Berg
On Thu, 2020-08-20 at 16:50 -0500, Sebastian Berg wrote:

> On Thu, 2020-08-20 at 12:21 -0600, Aaron Meurer wrote:
> > You're right. I was confusing the broadcasting logic for boolean
> > arrays.
> >
> > However, I did find this example
> >
> > > > > np.arange(10).reshape((2, 5))[np.array([[0, 0, 0, 0, 0]],
> > > > > dtype=np.int64), False]
> > Traceback (most recent call last):
> >   File "<stdin>", line 1, in <module>
> > IndexError: shape mismatch: indexing arrays could not be broadcast
> > together with shapes (1,5) (0,)
> >
> > That certainly seems to imply there is some broadcasting being
> > done.
>
> Yes, it broadcasts the array after converting it with `nonzero`, i.e.
> its much the same as:
>
>    indices = [[0, 0, 0, 0, 0]], *np.nonzero(False)
>    indices = np.broadcast_arrays(*indices)
>
> will give the same result (see also `np.ix_` which converts booleans
> as
> well for this reason, to give you outer indexing).
> I was half way through a mock-up/pseudo code, but thought you likely
> wasn't sure it was ending up clear. It sounds like things are
> probably
> falling into place for you (if they are not, let me know what might
> help you):
Sorry editing error up there, in short I hope those steps sense to you,
note that the broadcasting is basically part of a later "integer only"
indexing step, and the `nonzero` part is pre-processing.

>
> 1. Convert all boolean indices into a series of integer indices using
>    `np.nonzero(index)`
>
> 2. For True/False scalars, that doesn't work, because `np.nonzero()`.
>  
>  `nonzero` gave us an index array (which is good, we obviously want
>    
> one), but we need to index into `boolean_index.ndim == 0`
>    dimensions!
>    So that won't work, the approach using `nonzero` cannot generalize
>  
>  here, although boolean indices generalize perfectly.
>
>    The solution to the dilemma is simple: If we have to index one
>    dimension, but should be indexing zero, then we simply add that
>    dimension to the original array (or at least pretend there was
>    an additional dimension).
>
> 3. Do normal indexing with the result *including broadcasting*,
>    we forget it was converted.
>
> The other way to solve it would be to always reshape the original
> array
> to combine all axes being indexed by a single boolean index into one
> axis and then index it using `np.flatnonzero`.  (But that would get a
> different result if you try to broadcast!)
>
>
> In any case, I am not sure I would bother with making sense of this,
> except for sports!
> Its pretty much nonsense and I think the time understanding it is
> probably better spend deprecating it.  The only reason I did not
> Deprecate itt before, is that I tried to do be minimal in the changes
> when I rewrote advanced indexing (and generalized boolean scalars
> correctly) long ago.  That was likely the right start/choice at the
> time, since there were much bigger fish to catch, but I do not think
> anything is holding us back now.
>
> Cheers,
>
> Sebastian
>
>
> > Aaron Meurer
> >
> > On Wed, Aug 19, 2020 at 6:55 PM Sebastian Berg
> > <[hidden email]> wrote:
> > > On Wed, 2020-08-19 at 18:07 -0600, Aaron Meurer wrote:
> > > > > > 3. If you have multiple advanced indexing you get annoying
> > > > > > broadcasting
> > > > > >    of all of these. That is *always* confusing for boolean
> > > > > > indices.
> > > > > >    0-D should not be too special there...
> > > >
> > > > OK, now that I am learning more about advanced indexing, this
> > > > statement is confusing to me. It seems that scalar boolean
> > > > indices do
> > > > not broadcast. For example:
> > >
> > > Well, broadcasting means you broadcast the *nonzero result*
> > > unless
> > > I am
> > > very confused... There is a reason I dismissed it. We could (and
> > > arguably should) just deprecate it.  And I have doubts anyone
> > > would
> > > even notice.
> > >
> > > > > > > np.arange(2)[False, np.array([True, False])]
> > > > array([], dtype=int64)
> > > > > > > np.arange(2)[tuple(np.broadcast_arrays(False,
> > > > > > > np.array([True,
> > > > > > > False])))]
> > > > Traceback (most recent call last):
> > > >   File "<stdin>", line 1, in <module>
> > > > IndexError: too many indices for array: array is 1-dimensional,
> > > > but 2
> > > > were indexed
> > > >
> > > > And indeed, the docs even say, as you noted, "the nonzero
> > > > equivalence
> > > > for Boolean arrays does not hold for zero dimensional boolean
> > > > arrays,"
> > > > which I guess also applies to the broadcasting.
> > >
> > > I actually think that probably also holds. Nonzero just behave
> > > weird
> > > for 0D because arrays (because it returns a tuple).
> > > But since broadcasting the nonzero result is so weird, and since
> > > 0-
> > > D
> > > booleans require some additional logic and don't generalize 100%
> > > (code
> > > wise), I won't rule out there are differences.
> > >
> > > > From what I can tell, the logic is that all integer and boolean
> > > > arrays
> > >
> > > Did you try that? Because as I said above, IIRC broadcasting the
> > > boolean array without first calling `nonzero` isn't really whats
> > > going
> > > on. And I don't know how it could be whats going on, since adding
> > > dimensions to a boolean index would have much more implications?
> > >
> > > - Sebastian
> > >
> > >
> > > > (and scalar ints) are broadcast together, *except* for boolean
> > > > scalars. Then the first boolean scalar is replaced with and(all
> > > > boolean scalars) and the rest are removed from the index. Then
> > > > that
> > > > index adds a length 1 axis if it is True and 0 if it is False.
> > > >
> > > > So they don't broadcast, but rather "fake broadcast". I still
> > > > contend
> > > > that it would be much more useful, if True were a synonym for
> > > > newaxis
> > > > and False worked like newaxis but instead added a length 0
> > > > axis.
> > > > Alternately, True and False scalars should behave exactly like
> > > > all
> > > > other boolean arrays with no exceptions (i.e., work like
> > > > np.nonzero(),
> > > > broadcast, etc.). This would be less useful, but more
> > > > consistent.
> > > >
> > > > Aaron Meurer
> > > > _______________________________________________
> > > > NumPy-Discussion mailing list
> > > > [hidden email]
> > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > >
> > >
> > > _______________________________________________
> > > NumPy-Discussion mailing list
> > > [hidden email]
> > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > _______________________________________________
> > NumPy-Discussion mailing list
> > [hidden email]
> > https://mail.python.org/mailman/listinfo/numpy-discussion
> >
>
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion

_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Aaron Meurer
Just to be clear, what exactly do you think should be deprecated?
Boolean scalar indices in general, or just boolean scalars combined
with other arrays, or something else?

Aaron Meurer

On Thu, Aug 20, 2020 at 3:56 PM Sebastian Berg
<[hidden email]> wrote:

>
> On Thu, 2020-08-20 at 16:50 -0500, Sebastian Berg wrote:
> > On Thu, 2020-08-20 at 12:21 -0600, Aaron Meurer wrote:
> > > You're right. I was confusing the broadcasting logic for boolean
> > > arrays.
> > >
> > > However, I did find this example
> > >
> > > > > > np.arange(10).reshape((2, 5))[np.array([[0, 0, 0, 0, 0]],
> > > > > > dtype=np.int64), False]
> > > Traceback (most recent call last):
> > >   File "<stdin>", line 1, in <module>
> > > IndexError: shape mismatch: indexing arrays could not be broadcast
> > > together with shapes (1,5) (0,)
> > >
> > > That certainly seems to imply there is some broadcasting being
> > > done.
> >
> > Yes, it broadcasts the array after converting it with `nonzero`, i.e.
> > its much the same as:
> >
> >    indices = [[0, 0, 0, 0, 0]], *np.nonzero(False)
> >    indices = np.broadcast_arrays(*indices)
> >
> > will give the same result (see also `np.ix_` which converts booleans
> > as
> > well for this reason, to give you outer indexing).
> > I was half way through a mock-up/pseudo code, but thought you likely
> > wasn't sure it was ending up clear. It sounds like things are
> > probably
> > falling into place for you (if they are not, let me know what might
> > help you):
>
> Sorry editing error up there, in short I hope those steps sense to you,
> note that the broadcasting is basically part of a later "integer only"
> indexing step, and the `nonzero` part is pre-processing.
>
> >
> > 1. Convert all boolean indices into a series of integer indices using
> >    `np.nonzero(index)`
> >
> > 2. For True/False scalars, that doesn't work, because `np.nonzero()`.
> >
> >  `nonzero` gave us an index array (which is good, we obviously want
> >
> > one), but we need to index into `boolean_index.ndim == 0`
> >    dimensions!
> >    So that won't work, the approach using `nonzero` cannot generalize
> >
> >  here, although boolean indices generalize perfectly.
> >
> >    The solution to the dilemma is simple: If we have to index one
> >    dimension, but should be indexing zero, then we simply add that
> >    dimension to the original array (or at least pretend there was
> >    an additional dimension).
> >
> > 3. Do normal indexing with the result *including broadcasting*,
> >    we forget it was converted.
> >
> > The other way to solve it would be to always reshape the original
> > array
> > to combine all axes being indexed by a single boolean index into one
> > axis and then index it using `np.flatnonzero`.  (But that would get a
> > different result if you try to broadcast!)
> >
> >
> > In any case, I am not sure I would bother with making sense of this,
> > except for sports!
> > Its pretty much nonsense and I think the time understanding it is
> > probably better spend deprecating it.  The only reason I did not
> > Deprecate itt before, is that I tried to do be minimal in the changes
> > when I rewrote advanced indexing (and generalized boolean scalars
> > correctly) long ago.  That was likely the right start/choice at the
> > time, since there were much bigger fish to catch, but I do not think
> > anything is holding us back now.
> >
> > Cheers,
> >
> > Sebastian
> >
> >
> > > Aaron Meurer
> > >
> > > On Wed, Aug 19, 2020 at 6:55 PM Sebastian Berg
> > > <[hidden email]> wrote:
> > > > On Wed, 2020-08-19 at 18:07 -0600, Aaron Meurer wrote:
> > > > > > > 3. If you have multiple advanced indexing you get annoying
> > > > > > > broadcasting
> > > > > > >    of all of these. That is *always* confusing for boolean
> > > > > > > indices.
> > > > > > >    0-D should not be too special there...
> > > > >
> > > > > OK, now that I am learning more about advanced indexing, this
> > > > > statement is confusing to me. It seems that scalar boolean
> > > > > indices do
> > > > > not broadcast. For example:
> > > >
> > > > Well, broadcasting means you broadcast the *nonzero result*
> > > > unless
> > > > I am
> > > > very confused... There is a reason I dismissed it. We could (and
> > > > arguably should) just deprecate it.  And I have doubts anyone
> > > > would
> > > > even notice.
> > > >
> > > > > > > > np.arange(2)[False, np.array([True, False])]
> > > > > array([], dtype=int64)
> > > > > > > > np.arange(2)[tuple(np.broadcast_arrays(False,
> > > > > > > > np.array([True,
> > > > > > > > False])))]
> > > > > Traceback (most recent call last):
> > > > >   File "<stdin>", line 1, in <module>
> > > > > IndexError: too many indices for array: array is 1-dimensional,
> > > > > but 2
> > > > > were indexed
> > > > >
> > > > > And indeed, the docs even say, as you noted, "the nonzero
> > > > > equivalence
> > > > > for Boolean arrays does not hold for zero dimensional boolean
> > > > > arrays,"
> > > > > which I guess also applies to the broadcasting.
> > > >
> > > > I actually think that probably also holds. Nonzero just behave
> > > > weird
> > > > for 0D because arrays (because it returns a tuple).
> > > > But since broadcasting the nonzero result is so weird, and since
> > > > 0-
> > > > D
> > > > booleans require some additional logic and don't generalize 100%
> > > > (code
> > > > wise), I won't rule out there are differences.
> > > >
> > > > > From what I can tell, the logic is that all integer and boolean
> > > > > arrays
> > > >
> > > > Did you try that? Because as I said above, IIRC broadcasting the
> > > > boolean array without first calling `nonzero` isn't really whats
> > > > going
> > > > on. And I don't know how it could be whats going on, since adding
> > > > dimensions to a boolean index would have much more implications?
> > > >
> > > > - Sebastian
> > > >
> > > >
> > > > > (and scalar ints) are broadcast together, *except* for boolean
> > > > > scalars. Then the first boolean scalar is replaced with and(all
> > > > > boolean scalars) and the rest are removed from the index. Then
> > > > > that
> > > > > index adds a length 1 axis if it is True and 0 if it is False.
> > > > >
> > > > > So they don't broadcast, but rather "fake broadcast". I still
> > > > > contend
> > > > > that it would be much more useful, if True were a synonym for
> > > > > newaxis
> > > > > and False worked like newaxis but instead added a length 0
> > > > > axis.
> > > > > Alternately, True and False scalars should behave exactly like
> > > > > all
> > > > > other boolean arrays with no exceptions (i.e., work like
> > > > > np.nonzero(),
> > > > > broadcast, etc.). This would be less useful, but more
> > > > > consistent.
> > > > >
> > > > > Aaron Meurer
> > > > > _______________________________________________
> > > > > NumPy-Discussion mailing list
> > > > > [hidden email]
> > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > >
> > > >
> > > > _______________________________________________
> > > > NumPy-Discussion mailing list
> > > > [hidden email]
> > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > _______________________________________________
> > > NumPy-Discussion mailing list
> > > [hidden email]
> > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > >
> >
> > _______________________________________________
> > NumPy-Discussion mailing list
> > [hidden email]
> > https://mail.python.org/mailman/listinfo/numpy-discussion
>
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion
_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Sebastian Berg
On Thu, 2020-08-20 at 16:00 -0600, Aaron Meurer wrote:
> Just to be clear, what exactly do you think should be deprecated?
> Boolean scalar indices in general, or just boolean scalars combined
> with other arrays, or something else?

My angle is that we should allow only:

* Any number of integer array indices (ideally only explicitly
  with `arr.vindex[]`, but we do not have that luxury right now.)

* A single boolean index (array or scalar is identical)

but no mix of the above (including multiple boolean indices).

Because I think they are at least one level more confusing than
multiple advanced indices.

I admit, I forgot that the broadcasting logic is fine in this case:

   arr = np.zeros((2, 3))
   arr[[True], np.array(3)]

where the advanced index is also a scalar index. In that case the
result is straight forward, since broadcasting does not affect
`np.array(3)`.


I am happy to be wrong about that assessment, but I think your opinion
on it could likely push us towards just doing a Deprecation.
The only use case for "multiple boolean indices" that I could think of
was this:

    arr = np.diag([1, 2, 3, 4])  # 2-d square array
    indx = arr.diagonal() > 2  # mask for each row and column
    masked_diagonal = arr[indx, indx]
    print(repr(masked_diagonal))
    # array([3, 4])

and my guess is that the reaction to that code is a: "Wait what?!"

That code might seem reasonable, but it only works if you have the
exact same number of `True` values in the two indices.
And if you have the exact same number but two different arrays, then I
fail to reason about the result without doing the `nonzero` step, which
I think indicates that there just is no logical concept for it.


So, I think we may be better of forcing the few power-user who may have
found a use for this type of nugget to use `np.nonzero()` or find
another solution.

- Sebastian


>
> Aaron Meurer
>
> On Thu, Aug 20, 2020 at 3:56 PM Sebastian Berg
> <[hidden email]> wrote:
> > On Thu, 2020-08-20 at 16:50 -0500, Sebastian Berg wrote:
> > > On Thu, 2020-08-20 at 12:21 -0600, Aaron Meurer wrote:
> > > > You're right. I was confusing the broadcasting logic for
> > > > boolean
> > > > arrays.
> > > >
> > > > However, I did find this example
> > > >
> > > > > > > np.arange(10).reshape((2, 5))[np.array([[0, 0, 0, 0, 0]],
> > > > > > > dtype=np.int64), False]
> > > > Traceback (most recent call last):
> > > >   File "<stdin>", line 1, in <module>
> > > > IndexError: shape mismatch: indexing arrays could not be
> > > > broadcast
> > > > together with shapes (1,5) (0,)
> > > >
> > > > That certainly seems to imply there is some broadcasting being
> > > > done.
> > >
> > > Yes, it broadcasts the array after converting it with `nonzero`,
> > > i.e.
> > > its much the same as:
> > >
> > >    indices = [[0, 0, 0, 0, 0]], *np.nonzero(False)
> > >    indices = np.broadcast_arrays(*indices)
> > >
> > > will give the same result (see also `np.ix_` which converts
> > > booleans
> > > as
> > > well for this reason, to give you outer indexing).
> > > I was half way through a mock-up/pseudo code, but thought you
> > > likely
> > > wasn't sure it was ending up clear. It sounds like things are
> > > probably
> > > falling into place for you (if they are not, let me know what
> > > might
> > > help you):
> >
> > Sorry editing error up there, in short I hope those steps sense to
> > you,
> > note that the broadcasting is basically part of a later "integer
> > only"
> > indexing step, and the `nonzero` part is pre-processing.
> >
> > > 1. Convert all boolean indices into a series of integer indices
> > > using
> > >    `np.nonzero(index)`
> > >
> > > 2. For True/False scalars, that doesn't work, because
> > > `np.nonzero()`.
> > >
> > >  `nonzero` gave us an index array (which is good, we obviously
> > > want
> > >
> > > one), but we need to index into `boolean_index.ndim == 0`
> > >    dimensions!
> > >    So that won't work, the approach using `nonzero` cannot
> > > generalize
> > >
> > >  here, although boolean indices generalize perfectly.
> > >
> > >    The solution to the dilemma is simple: If we have to index one
> > >    dimension, but should be indexing zero, then we simply add
> > > that
> > >    dimension to the original array (or at least pretend there was
> > >    an additional dimension).
> > >
> > > 3. Do normal indexing with the result *including broadcasting*,
> > >    we forget it was converted.
> > >
> > > The other way to solve it would be to always reshape the original
> > > array
> > > to combine all axes being indexed by a single boolean index into
> > > one
> > > axis and then index it using `np.flatnonzero`.  (But that would
> > > get a
> > > different result if you try to broadcast!)
> > >
> > >
> > > In any case, I am not sure I would bother with making sense of
> > > this,
> > > except for sports!
> > > Its pretty much nonsense and I think the time understanding it is
> > > probably better spend deprecating it.  The only reason I did not
> > > Deprecate itt before, is that I tried to do be minimal in the
> > > changes
> > > when I rewrote advanced indexing (and generalized boolean scalars
> > > correctly) long ago.  That was likely the right start/choice at
> > > the
> > > time, since there were much bigger fish to catch, but I do not
> > > think
> > > anything is holding us back now.
> > >
> > > Cheers,
> > >
> > > Sebastian
> > >
> > >
> > > > Aaron Meurer
> > > >
> > > > On Wed, Aug 19, 2020 at 6:55 PM Sebastian Berg
> > > > <[hidden email]> wrote:
> > > > > On Wed, 2020-08-19 at 18:07 -0600, Aaron Meurer wrote:
> > > > > > > > 3. If you have multiple advanced indexing you get
> > > > > > > > annoying
> > > > > > > > broadcasting
> > > > > > > >    of all of these. That is *always* confusing for
> > > > > > > > boolean
> > > > > > > > indices.
> > > > > > > >    0-D should not be too special there...
> > > > > >
> > > > > > OK, now that I am learning more about advanced indexing,
> > > > > > this
> > > > > > statement is confusing to me. It seems that scalar boolean
> > > > > > indices do
> > > > > > not broadcast. For example:
> > > > >
> > > > > Well, broadcasting means you broadcast the *nonzero result*
> > > > > unless
> > > > > I am
> > > > > very confused... There is a reason I dismissed it. We could
> > > > > (and
> > > > > arguably should) just deprecate it.  And I have doubts anyone
> > > > > would
> > > > > even notice.
> > > > >
> > > > > > > > > np.arange(2)[False, np.array([True, False])]
> > > > > > array([], dtype=int64)
> > > > > > > > > np.arange(2)[tuple(np.broadcast_arrays(False,
> > > > > > > > > np.array([True,
> > > > > > > > > False])))]
> > > > > > Traceback (most recent call last):
> > > > > >   File "<stdin>", line 1, in <module>
> > > > > > IndexError: too many indices for array: array is 1-
> > > > > > dimensional,
> > > > > > but 2
> > > > > > were indexed
> > > > > >
> > > > > > And indeed, the docs even say, as you noted, "the nonzero
> > > > > > equivalence
> > > > > > for Boolean arrays does not hold for zero dimensional
> > > > > > boolean
> > > > > > arrays,"
> > > > > > which I guess also applies to the broadcasting.
> > > > >
> > > > > I actually think that probably also holds. Nonzero just
> > > > > behave
> > > > > weird
> > > > > for 0D because arrays (because it returns a tuple).
> > > > > But since broadcasting the nonzero result is so weird, and
> > > > > since
> > > > > 0-
> > > > > D
> > > > > booleans require some additional logic and don't generalize
> > > > > 100%
> > > > > (code
> > > > > wise), I won't rule out there are differences.
> > > > >
> > > > > > From what I can tell, the logic is that all integer and
> > > > > > boolean
> > > > > > arrays
> > > > >
> > > > > Did you try that? Because as I said above, IIRC broadcasting
> > > > > the
> > > > > boolean array without first calling `nonzero` isn't really
> > > > > whats
> > > > > going
> > > > > on. And I don't know how it could be whats going on, since
> > > > > adding
> > > > > dimensions to a boolean index would have much more
> > > > > implications?
> > > > >
> > > > > - Sebastian
> > > > >
> > > > >
> > > > > > (and scalar ints) are broadcast together, *except* for
> > > > > > boolean
> > > > > > scalars. Then the first boolean scalar is replaced with
> > > > > > and(all
> > > > > > boolean scalars) and the rest are removed from the index.
> > > > > > Then
> > > > > > that
> > > > > > index adds a length 1 axis if it is True and 0 if it is
> > > > > > False.
> > > > > >
> > > > > > So they don't broadcast, but rather "fake broadcast". I
> > > > > > still
> > > > > > contend
> > > > > > that it would be much more useful, if True were a synonym
> > > > > > for
> > > > > > newaxis
> > > > > > and False worked like newaxis but instead added a length 0
> > > > > > axis.
> > > > > > Alternately, True and False scalars should behave exactly
> > > > > > like
> > > > > > all
> > > > > > other boolean arrays with no exceptions (i.e., work like
> > > > > > np.nonzero(),
> > > > > > broadcast, etc.). This would be less useful, but more
> > > > > > consistent.
> > > > > >
> > > > > > Aaron Meurer
> > > > > > _______________________________________________
> > > > > > NumPy-Discussion mailing list
> > > > > > [hidden email]
> > > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > > >
> > > > >
> > > > > _______________________________________________
> > > > > NumPy-Discussion mailing list
> > > > > [hidden email]
> > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > _______________________________________________
> > > > NumPy-Discussion mailing list
> > > > [hidden email]
> > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > >
> > >
> > > _______________________________________________
> > > NumPy-Discussion mailing list
> > > [hidden email]
> > > https://mail.python.org/mailman/listinfo/numpy-discussion
> >
> > _______________________________________________
> > NumPy-Discussion mailing list
> > [hidden email]
> > https://mail.python.org/mailman/listinfo/numpy-discussion
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion
>

_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Aaron Meurer
On Thu, Aug 20, 2020 at 4:38 PM Sebastian Berg
<[hidden email]> wrote:

>
> On Thu, 2020-08-20 at 16:00 -0600, Aaron Meurer wrote:
> > Just to be clear, what exactly do you think should be deprecated?
> > Boolean scalar indices in general, or just boolean scalars combined
> > with other arrays, or something else?
>
> My angle is that we should allow only:
>
> * Any number of integer array indices (ideally only explicitly
>   with `arr.vindex[]`, but we do not have that luxury right now.)
>
> * A single boolean index (array or scalar is identical)
>
> but no mix of the above (including multiple boolean indices).
>
> Because I think they are at least one level more confusing than
> multiple advanced indices.
>
> I admit, I forgot that the broadcasting logic is fine in this case:
>
>    arr = np.zeros((2, 3))
>    arr[[True], np.array(3)]
>
> where the advanced index is also a scalar index. In that case the
> result is straight forward, since broadcasting does not affect
> `np.array(3)`.
>
>
> I am happy to be wrong about that assessment, but I think your opinion
> on it could likely push us towards just doing a Deprecation.
> The only use case for "multiple boolean indices" that I could think of
> was this:
>
>     arr = np.diag([1, 2, 3, 4])  # 2-d square array
>     indx = arr.diagonal() > 2  # mask for each row and column
>     masked_diagonal = arr[indx, indx]
>     print(repr(masked_diagonal))
>     # array([3, 4])
>
> and my guess is that the reaction to that code is a: "Wait what?!"
>
> That code might seem reasonable, but it only works if you have the
> exact same number of `True` values in the two indices.
> And if you have the exact same number but two different arrays, then I
> fail to reason about the result without doing the `nonzero` step, which
> I think indicates that there just is no logical concept for it.
>
>
> So, I think we may be better of forcing the few power-user who may have
> found a use for this type of nugget to use `np.nonzero()` or find
> another solution.

Well I'm cautious because despite implementing the logic for all this,
I'm a bit divorced from most use-cases. So I don't have a great
feeling for what is currently being used. For example, is it possible
to have a situation where you build a mask out of an expression, like
a[x > 0] or whatever, where the mask expression could be any number of
dimensions depending on the input values? And if so, does the current
logic for scalar booleans do the right thing when the number of
dimensions happens to be 0.

Mixing nonscalar boolean and integer arrays seems fine, as far as the
logic is concerned. I'm not really sure if it makes sense
semantically. I'll have to think about it more. The thing that has the
most odd corner cases in the indexing logic is boolean scalars. It
would be nice if you could treat them uniformly with the same logic as
other boolean arrays, but they have special cases everywhere. This is
in contrast with integer scalars which perfectly match the logic of
integer arrays with the shape == (). Maybe I'm just not looking at it
from the right angle. I don't know.

In ndindex, I've left the "arrays separated by slices, ellipses, or
newaxes" case unimplemented. Travis Oliphant told me he thinks it was
a mistake and it would be better to not allow it. I've also left
boolean scalars mixed with other arrays unimplemented because I don't
want to waste more time trying to figure out what is going on in the
example I posted earlier (though what you wrote helps). I have
nonscalar boolean arrays mixed with integer arrays working just fine,
and the logic isn't really any different than it would be if I only
supported them separately.

Aaron Meurer

>
> - Sebastian
>
>
> >
> > Aaron Meurer
> >
> > On Thu, Aug 20, 2020 at 3:56 PM Sebastian Berg
> > <[hidden email]> wrote:
> > > On Thu, 2020-08-20 at 16:50 -0500, Sebastian Berg wrote:
> > > > On Thu, 2020-08-20 at 12:21 -0600, Aaron Meurer wrote:
> > > > > You're right. I was confusing the broadcasting logic for
> > > > > boolean
> > > > > arrays.
> > > > >
> > > > > However, I did find this example
> > > > >
> > > > > > > > np.arange(10).reshape((2, 5))[np.array([[0, 0, 0, 0, 0]],
> > > > > > > > dtype=np.int64), False]
> > > > > Traceback (most recent call last):
> > > > >   File "<stdin>", line 1, in <module>
> > > > > IndexError: shape mismatch: indexing arrays could not be
> > > > > broadcast
> > > > > together with shapes (1,5) (0,)
> > > > >
> > > > > That certainly seems to imply there is some broadcasting being
> > > > > done.
> > > >
> > > > Yes, it broadcasts the array after converting it with `nonzero`,
> > > > i.e.
> > > > its much the same as:
> > > >
> > > >    indices = [[0, 0, 0, 0, 0]], *np.nonzero(False)
> > > >    indices = np.broadcast_arrays(*indices)
> > > >
> > > > will give the same result (see also `np.ix_` which converts
> > > > booleans
> > > > as
> > > > well for this reason, to give you outer indexing).
> > > > I was half way through a mock-up/pseudo code, but thought you
> > > > likely
> > > > wasn't sure it was ending up clear. It sounds like things are
> > > > probably
> > > > falling into place for you (if they are not, let me know what
> > > > might
> > > > help you):
> > >
> > > Sorry editing error up there, in short I hope those steps sense to
> > > you,
> > > note that the broadcasting is basically part of a later "integer
> > > only"
> > > indexing step, and the `nonzero` part is pre-processing.
> > >
> > > > 1. Convert all boolean indices into a series of integer indices
> > > > using
> > > >    `np.nonzero(index)`
> > > >
> > > > 2. For True/False scalars, that doesn't work, because
> > > > `np.nonzero()`.
> > > >
> > > >  `nonzero` gave us an index array (which is good, we obviously
> > > > want
> > > >
> > > > one), but we need to index into `boolean_index.ndim == 0`
> > > >    dimensions!
> > > >    So that won't work, the approach using `nonzero` cannot
> > > > generalize
> > > >
> > > >  here, although boolean indices generalize perfectly.
> > > >
> > > >    The solution to the dilemma is simple: If we have to index one
> > > >    dimension, but should be indexing zero, then we simply add
> > > > that
> > > >    dimension to the original array (or at least pretend there was
> > > >    an additional dimension).
> > > >
> > > > 3. Do normal indexing with the result *including broadcasting*,
> > > >    we forget it was converted.
> > > >
> > > > The other way to solve it would be to always reshape the original
> > > > array
> > > > to combine all axes being indexed by a single boolean index into
> > > > one
> > > > axis and then index it using `np.flatnonzero`.  (But that would
> > > > get a
> > > > different result if you try to broadcast!)
> > > >
> > > >
> > > > In any case, I am not sure I would bother with making sense of
> > > > this,
> > > > except for sports!
> > > > Its pretty much nonsense and I think the time understanding it is
> > > > probably better spend deprecating it.  The only reason I did not
> > > > Deprecate itt before, is that I tried to do be minimal in the
> > > > changes
> > > > when I rewrote advanced indexing (and generalized boolean scalars
> > > > correctly) long ago.  That was likely the right start/choice at
> > > > the
> > > > time, since there were much bigger fish to catch, but I do not
> > > > think
> > > > anything is holding us back now.
> > > >
> > > > Cheers,
> > > >
> > > > Sebastian
> > > >
> > > >
> > > > > Aaron Meurer
> > > > >
> > > > > On Wed, Aug 19, 2020 at 6:55 PM Sebastian Berg
> > > > > <[hidden email]> wrote:
> > > > > > On Wed, 2020-08-19 at 18:07 -0600, Aaron Meurer wrote:
> > > > > > > > > 3. If you have multiple advanced indexing you get
> > > > > > > > > annoying
> > > > > > > > > broadcasting
> > > > > > > > >    of all of these. That is *always* confusing for
> > > > > > > > > boolean
> > > > > > > > > indices.
> > > > > > > > >    0-D should not be too special there...
> > > > > > >
> > > > > > > OK, now that I am learning more about advanced indexing,
> > > > > > > this
> > > > > > > statement is confusing to me. It seems that scalar boolean
> > > > > > > indices do
> > > > > > > not broadcast. For example:
> > > > > >
> > > > > > Well, broadcasting means you broadcast the *nonzero result*
> > > > > > unless
> > > > > > I am
> > > > > > very confused... There is a reason I dismissed it. We could
> > > > > > (and
> > > > > > arguably should) just deprecate it.  And I have doubts anyone
> > > > > > would
> > > > > > even notice.
> > > > > >
> > > > > > > > > > np.arange(2)[False, np.array([True, False])]
> > > > > > > array([], dtype=int64)
> > > > > > > > > > np.arange(2)[tuple(np.broadcast_arrays(False,
> > > > > > > > > > np.array([True,
> > > > > > > > > > False])))]
> > > > > > > Traceback (most recent call last):
> > > > > > >   File "<stdin>", line 1, in <module>
> > > > > > > IndexError: too many indices for array: array is 1-
> > > > > > > dimensional,
> > > > > > > but 2
> > > > > > > were indexed
> > > > > > >
> > > > > > > And indeed, the docs even say, as you noted, "the nonzero
> > > > > > > equivalence
> > > > > > > for Boolean arrays does not hold for zero dimensional
> > > > > > > boolean
> > > > > > > arrays,"
> > > > > > > which I guess also applies to the broadcasting.
> > > > > >
> > > > > > I actually think that probably also holds. Nonzero just
> > > > > > behave
> > > > > > weird
> > > > > > for 0D because arrays (because it returns a tuple).
> > > > > > But since broadcasting the nonzero result is so weird, and
> > > > > > since
> > > > > > 0-
> > > > > > D
> > > > > > booleans require some additional logic and don't generalize
> > > > > > 100%
> > > > > > (code
> > > > > > wise), I won't rule out there are differences.
> > > > > >
> > > > > > > From what I can tell, the logic is that all integer and
> > > > > > > boolean
> > > > > > > arrays
> > > > > >
> > > > > > Did you try that? Because as I said above, IIRC broadcasting
> > > > > > the
> > > > > > boolean array without first calling `nonzero` isn't really
> > > > > > whats
> > > > > > going
> > > > > > on. And I don't know how it could be whats going on, since
> > > > > > adding
> > > > > > dimensions to a boolean index would have much more
> > > > > > implications?
> > > > > >
> > > > > > - Sebastian
> > > > > >
> > > > > >
> > > > > > > (and scalar ints) are broadcast together, *except* for
> > > > > > > boolean
> > > > > > > scalars. Then the first boolean scalar is replaced with
> > > > > > > and(all
> > > > > > > boolean scalars) and the rest are removed from the index.
> > > > > > > Then
> > > > > > > that
> > > > > > > index adds a length 1 axis if it is True and 0 if it is
> > > > > > > False.
> > > > > > >
> > > > > > > So they don't broadcast, but rather "fake broadcast". I
> > > > > > > still
> > > > > > > contend
> > > > > > > that it would be much more useful, if True were a synonym
> > > > > > > for
> > > > > > > newaxis
> > > > > > > and False worked like newaxis but instead added a length 0
> > > > > > > axis.
> > > > > > > Alternately, True and False scalars should behave exactly
> > > > > > > like
> > > > > > > all
> > > > > > > other boolean arrays with no exceptions (i.e., work like
> > > > > > > np.nonzero(),
> > > > > > > broadcast, etc.). This would be less useful, but more
> > > > > > > consistent.
> > > > > > >
> > > > > > > Aaron Meurer
> > > > > > > _______________________________________________
> > > > > > > NumPy-Discussion mailing list
> > > > > > > [hidden email]
> > > > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > > > >
> > > > > >
> > > > > > _______________________________________________
> > > > > > NumPy-Discussion mailing list
> > > > > > [hidden email]
> > > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > > _______________________________________________
> > > > > NumPy-Discussion mailing list
> > > > > [hidden email]
> > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > >
> > > >
> > > > _______________________________________________
> > > > NumPy-Discussion mailing list
> > > > [hidden email]
> > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > >
> > > _______________________________________________
> > > NumPy-Discussion mailing list
> > > [hidden email]
> > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > _______________________________________________
> > NumPy-Discussion mailing list
> > [hidden email]
> > https://mail.python.org/mailman/listinfo/numpy-discussion
> >
>
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion
_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion
Reply | Threaded
Open this post in threaded view
|

Re: What is up with raw boolean indices (like a[False])?

Sebastian Berg
On Thu, 2020-08-20 at 17:08 -0600, Aaron Meurer wrote:

> On Thu, Aug 20, 2020 at 4:38 PM Sebastian Berg
> <[hidden email]> wrote:
> > On Thu, 2020-08-20 at 16:00 -0600, Aaron Meurer wrote:
> > > Just to be clear, what exactly do you think should be deprecated?
> > > Boolean scalar indices in general, or just boolean scalars
> > > combined
> > > with other arrays, or something else?
> >
> > My angle is that we should allow only:
> >
> > * Any number of integer array indices (ideally only explicitly
> >   with `arr.vindex[]`, but we do not have that luxury right now.)
> >
> > * A single boolean index (array or scalar is identical)
> >
> > but no mix of the above (including multiple boolean indices).
> >
> > Because I think they are at least one level more confusing than
> > multiple advanced indices.
> >
> > I admit, I forgot that the broadcasting logic is fine in this case:
> >
> >    arr = np.zeros((2, 3))
> >    arr[[True], np.array(3)]
> >
> > where the advanced index is also a scalar index. In that case the
> > result is straight forward, since broadcasting does not affect
> > `np.array(3)`.
> >
> >
> > I am happy to be wrong about that assessment, but I think your
> > opinion
> > on it could likely push us towards just doing a Deprecation.
> > The only use case for "multiple boolean indices" that I could think
> > of
> > was this:
> >
> >     arr = np.diag([1, 2, 3, 4])  # 2-d square array
> >     indx = arr.diagonal() > 2  # mask for each row and column
> >     masked_diagonal = arr[indx, indx]
> >     print(repr(masked_diagonal))
> >     # array([3, 4])
> >
> > and my guess is that the reaction to that code is a: "Wait what?!"
> >
> > That code might seem reasonable, but it only works if you have the
> > exact same number of `True` values in the two indices.
> > And if you have the exact same number but two different arrays,
> > then I
> > fail to reason about the result without doing the `nonzero` step,
> > which
> > I think indicates that there just is no logical concept for it.
> >
> >
> > So, I think we may be better of forcing the few power-user who may
> > have
> > found a use for this type of nugget to use `np.nonzero()` or find
> > another solution.
>
> Well I'm cautious because despite implementing the logic for all
> this,
> I'm a bit divorced from most use-cases. So I don't have a great
> feeling for what is currently being used. For example, is it possible
> to have a situation where you build a mask out of an expression, like
> a[x > 0] or whatever, where the mask expression could be any number
> of
I am not sure anyone does it, but I certainly can think of ways to use
this functionality:

```
   def good_images(image_or_stack):
       """Filter dark images

       image_or_stack : ndarray (..., N, M, 3)

       Returns
       -------
       good_images : ndarray (K, N, M, 3)
           Returns all good images as a one dimensional stack for
           further processing, where `K` is the number of good
           images.
       """
       assert image_or_stack.ndim >= 3
       assert image_or_stack.shape[-1] == 3  # 3 colors, fixed.

       average_brightness = image_or_stack.mean((-3, -2, -1))
       
       return image_or_stack[average_brigthness, ...]
```

Note that the above uses a single True/False if you pass in a single
image.


> dimensions depending on the input values? And if so, does the current
> logic for scalar booleans do the right thing when the number of
> dimensions happens to be 0.
>
> Mixing nonscalar boolean and integer arrays seems fine, as far as the
> logic is concerned. I'm not really sure if it makes sense
> semantically. I'll have to think about it more. The thing that has
> the
> most odd corner cases in the indexing logic is boolean scalars. It

I think they are perfectly fine semantically, but they definitely do
require special handling.
Although the reason for that special handling is that we have to
implement boolean indices using integer array indices and that is not
possible without additional logic.

If you browse the NumPy code, you will see there is a `HAS_0D_BOOL`
macro (basically enum), to distinguish:

    internal_indx = np.nonzero(False)

and:

    internal_indx = np.nonzero([False])

because the first effectively inserts a new dimension and then indices
it, while the former just indices an existing dimension.

> would be nice if you could treat them uniformly with the same logic
> as
> other boolean arrays, but they have special cases everywhere. This is
> in contrast with integer scalars which perfectly match the logic of
> integer arrays with the shape == (). Maybe I'm just not looking at it
> from the right angle. I don't know.

I hope the example above helps you, I think you should always remember
the two rules of boolean indexing mentioned somewhere in the docs:

  * A boolean array indexes into `arr.ndim` dimensions, and effectively
    removes them.
  * A boolean array index adds a single input array.

I guess, I should have written that mock-up code (maybe you can help
improve the NumPy docs, although I guess this might be too technical):

```
def preprocess_boolean_indices(arr, indices):
    """Take an array and indices and returns a new
    array and new indices without any boolean ones.

    NOTE: Code will not handle None or Ellipsis
    """
    new_indices = []
    for axis, index in enumerate(indices):
        if not is_boolean_index(index):
            new_indices.append(index)

        # Check whether dimensions match here!
        new_indices.extend(np.nonzero(indices))
        if index.ndim == 0:
            # nonzero result added an index, but we
            # should index into 0-dimensions, so add one.
            # (Ellipsis or None would mean `axis` is incorrect)
            arr = np.expand_dims(arr, axis)

    return arr, indices


prep_arr, prep_indices = preprocess_boolean_indices(arr, indices)
arr[indices] == prep_arr[prep_indices]
```

That is ugly, but the issue is not in the semantics of 0-D booleans,
but rather in the translating boolean indices to integer indices.

> In ndindex, I've left the "arrays separated by slices, ellipses, or
> newaxes" case unimplemented. Travis Oliphant told me he thinks it was
> a mistake and it would be better to not allow it. I've also left

Yeah, either always transpose or just refuse the "separated by" cases.
It is an interesting angle to only support the cases where axis
insertion can be done as "expected", I remember mainly the discussion
to just always transpose.

> boolean scalars mixed with other arrays unimplemented because I don't
> want to waste more time trying to figure out what is going on in the
> example I posted earlier (though what you wrote helps). I have

Absolutely agree with that step (I don't know if you are careful with
scalars and 0D arrays, it would be the only issue I can think of).

> nonscalar boolean arrays mixed with integer arrays working just fine,
> and the logic isn't really any different than it would be if I only
> supported them separately.

Right, the implementation is likely straight forward. But the semantics
of it is pretty weird (or impossible), almost any trial will show that,
I think:

    arr = np.arange(12).reshape(3, 4)
    arr
    # array([[ 0,  1,  2,  3],
    #        [ 4,  5,  6,  7],
    #        [ 8,  9, 10, 11]])
    arr[[True, False, True], [True, False, False, False]]
    # array([0, 8])

OK, you can reason about that, but only because there is a single
boolean True in the second array (and then gets broadcast.

    arr[[True, False, True], [True, False, True, False]]
    # array([ 0, 10])

Ok, we can reason about this, but at that point we have to align the
True values from the first index with those from the second
(effectively convert the two indices to integer ones in our heads).

But what is the meaning of aligning true values? I am sure there is
none, except in very special cases.  To proof this, lets try:

    arr[[True, True, True], [True, False, True, False]]

which gives a broadcasting error :).

So yeah, I guess you can find "meaning" for it but it seems just too
strange, and even if you do using two integer indices will make things
much clearer and less error prone.

- Sebastian



> Aaron Meurer
>
> > - Sebastian
> >
> >
> > > Aaron Meurer
> > >
> > > On Thu, Aug 20, 2020 at 3:56 PM Sebastian Berg
> > > <[hidden email]> wrote:
> > > > On Thu, 2020-08-20 at 16:50 -0500, Sebastian Berg wrote:
> > > > > On Thu, 2020-08-20 at 12:21 -0600, Aaron Meurer wrote:
> > > > > > You're right. I was confusing the broadcasting logic for
> > > > > > boolean
> > > > > > arrays.
> > > > > >
> > > > > > However, I did find this example
> > > > > >
> > > > > > > > > np.arange(10).reshape((2, 5))[np.array([[0, 0, 0, 0,
> > > > > > > > > 0]],
> > > > > > > > > dtype=np.int64), False]
> > > > > > Traceback (most recent call last):
> > > > > >   File "<stdin>", line 1, in <module>
> > > > > > IndexError: shape mismatch: indexing arrays could not be
> > > > > > broadcast
> > > > > > together with shapes (1,5) (0,)
> > > > > >
> > > > > > That certainly seems to imply there is some broadcasting
> > > > > > being
> > > > > > done.
> > > > >
> > > > > Yes, it broadcasts the array after converting it with
> > > > > `nonzero`,
> > > > > i.e.
> > > > > its much the same as:
> > > > >
> > > > >    indices = [[0, 0, 0, 0, 0]], *np.nonzero(False)
> > > > >    indices = np.broadcast_arrays(*indices)
> > > > >
> > > > > will give the same result (see also `np.ix_` which converts
> > > > > booleans
> > > > > as
> > > > > well for this reason, to give you outer indexing).
> > > > > I was half way through a mock-up/pseudo code, but thought you
> > > > > likely
> > > > > wasn't sure it was ending up clear. It sounds like things are
> > > > > probably
> > > > > falling into place for you (if they are not, let me know what
> > > > > might
> > > > > help you):
> > > >
> > > > Sorry editing error up there, in short I hope those steps sense
> > > > to
> > > > you,
> > > > note that the broadcasting is basically part of a later
> > > > "integer
> > > > only"
> > > > indexing step, and the `nonzero` part is pre-processing.
> > > >
> > > > > 1. Convert all boolean indices into a series of integer
> > > > > indices
> > > > > using
> > > > >    `np.nonzero(index)`
> > > > >
> > > > > 2. For True/False scalars, that doesn't work, because
> > > > > `np.nonzero()`.
> > > > >
> > > > >  `nonzero` gave us an index array (which is good, we
> > > > > obviously
> > > > > want
> > > > >
> > > > > one), but we need to index into `boolean_index.ndim == 0`
> > > > >    dimensions!
> > > > >    So that won't work, the approach using `nonzero` cannot
> > > > > generalize
> > > > >
> > > > >  here, although boolean indices generalize perfectly.
> > > > >
> > > > >    The solution to the dilemma is simple: If we have to index
> > > > > one
> > > > >    dimension, but should be indexing zero, then we simply add
> > > > > that
> > > > >    dimension to the original array (or at least pretend there
> > > > > was
> > > > >    an additional dimension).
> > > > >
> > > > > 3. Do normal indexing with the result *including
> > > > > broadcasting*,
> > > > >    we forget it was converted.
> > > > >
> > > > > The other way to solve it would be to always reshape the
> > > > > original
> > > > > array
> > > > > to combine all axes being indexed by a single boolean index
> > > > > into
> > > > > one
> > > > > axis and then index it using `np.flatnonzero`.  (But that
> > > > > would
> > > > > get a
> > > > > different result if you try to broadcast!)
> > > > >
> > > > >
> > > > > In any case, I am not sure I would bother with making sense
> > > > > of
> > > > > this,
> > > > > except for sports!
> > > > > Its pretty much nonsense and I think the time understanding
> > > > > it is
> > > > > probably better spend deprecating it.  The only reason I did
> > > > > not
> > > > > Deprecate itt before, is that I tried to do be minimal in the
> > > > > changes
> > > > > when I rewrote advanced indexing (and generalized boolean
> > > > > scalars
> > > > > correctly) long ago.  That was likely the right start/choice
> > > > > at
> > > > > the
> > > > > time, since there were much bigger fish to catch, but I do
> > > > > not
> > > > > think
> > > > > anything is holding us back now.
> > > > >
> > > > > Cheers,
> > > > >
> > > > > Sebastian
> > > > >
> > > > >
> > > > > > Aaron Meurer
> > > > > >
> > > > > > On Wed, Aug 19, 2020 at 6:55 PM Sebastian Berg
> > > > > > <[hidden email]> wrote:
> > > > > > > On Wed, 2020-08-19 at 18:07 -0600, Aaron Meurer wrote:
> > > > > > > > > > 3. If you have multiple advanced indexing you get
> > > > > > > > > > annoying
> > > > > > > > > > broadcasting
> > > > > > > > > >    of all of these. That is *always* confusing for
> > > > > > > > > > boolean
> > > > > > > > > > indices.
> > > > > > > > > >    0-D should not be too special there...
> > > > > > > >
> > > > > > > > OK, now that I am learning more about advanced
> > > > > > > > indexing,
> > > > > > > > this
> > > > > > > > statement is confusing to me. It seems that scalar
> > > > > > > > boolean
> > > > > > > > indices do
> > > > > > > > not broadcast. For example:
> > > > > > >
> > > > > > > Well, broadcasting means you broadcast the *nonzero
> > > > > > > result*
> > > > > > > unless
> > > > > > > I am
> > > > > > > very confused... There is a reason I dismissed it. We
> > > > > > > could
> > > > > > > (and
> > > > > > > arguably should) just deprecate it.  And I have doubts
> > > > > > > anyone
> > > > > > > would
> > > > > > > even notice.
> > > > > > >
> > > > > > > > > > > np.arange(2)[False, np.array([True, False])]
> > > > > > > > array([], dtype=int64)
> > > > > > > > > > > np.arange(2)[tuple(np.broadcast_arrays(False,
> > > > > > > > > > > np.array([True,
> > > > > > > > > > > False])))]
> > > > > > > > Traceback (most recent call last):
> > > > > > > >   File "<stdin>", line 1, in <module>
> > > > > > > > IndexError: too many indices for array: array is 1-
> > > > > > > > dimensional,
> > > > > > > > but 2
> > > > > > > > were indexed
> > > > > > > >
> > > > > > > > And indeed, the docs even say, as you noted, "the
> > > > > > > > nonzero
> > > > > > > > equivalence
> > > > > > > > for Boolean arrays does not hold for zero dimensional
> > > > > > > > boolean
> > > > > > > > arrays,"
> > > > > > > > which I guess also applies to the broadcasting.
> > > > > > >
> > > > > > > I actually think that probably also holds. Nonzero just
> > > > > > > behave
> > > > > > > weird
> > > > > > > for 0D because arrays (because it returns a tuple).
> > > > > > > But since broadcasting the nonzero result is so weird,
> > > > > > > and
> > > > > > > since
> > > > > > > 0-
> > > > > > > D
> > > > > > > booleans require some additional logic and don't
> > > > > > > generalize
> > > > > > > 100%
> > > > > > > (code
> > > > > > > wise), I won't rule out there are differences.
> > > > > > >
> > > > > > > > From what I can tell, the logic is that all integer and
> > > > > > > > boolean
> > > > > > > > arrays
> > > > > > >
> > > > > > > Did you try that? Because as I said above, IIRC
> > > > > > > broadcasting
> > > > > > > the
> > > > > > > boolean array without first calling `nonzero` isn't
> > > > > > > really
> > > > > > > whats
> > > > > > > going
> > > > > > > on. And I don't know how it could be whats going on,
> > > > > > > since
> > > > > > > adding
> > > > > > > dimensions to a boolean index would have much more
> > > > > > > implications?
> > > > > > >
> > > > > > > - Sebastian
> > > > > > >
> > > > > > >
> > > > > > > > (and scalar ints) are broadcast together, *except* for
> > > > > > > > boolean
> > > > > > > > scalars. Then the first boolean scalar is replaced with
> > > > > > > > and(all
> > > > > > > > boolean scalars) and the rest are removed from the
> > > > > > > > index.
> > > > > > > > Then
> > > > > > > > that
> > > > > > > > index adds a length 1 axis if it is True and 0 if it is
> > > > > > > > False.
> > > > > > > >
> > > > > > > > So they don't broadcast, but rather "fake broadcast". I
> > > > > > > > still
> > > > > > > > contend
> > > > > > > > that it would be much more useful, if True were a
> > > > > > > > synonym
> > > > > > > > for
> > > > > > > > newaxis
> > > > > > > > and False worked like newaxis but instead added a
> > > > > > > > length 0
> > > > > > > > axis.
> > > > > > > > Alternately, True and False scalars should behave
> > > > > > > > exactly
> > > > > > > > like
> > > > > > > > all
> > > > > > > > other boolean arrays with no exceptions (i.e., work
> > > > > > > > like
> > > > > > > > np.nonzero(),
> > > > > > > > broadcast, etc.). This would be less useful, but more
> > > > > > > > consistent.
> > > > > > > >
> > > > > > > > Aaron Meurer
> > > > > > > > _______________________________________________
> > > > > > > > NumPy-Discussion mailing list
> > > > > > > > [hidden email]
> > > > > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > > > > >
> > > > > > >
> > > > > > > _______________________________________________
> > > > > > > NumPy-Discussion mailing list
> > > > > > > [hidden email]
> > > > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > > > _______________________________________________
> > > > > > NumPy-Discussion mailing list
> > > > > > [hidden email]
> > > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > > > >
> > > > >
> > > > > _______________________________________________
> > > > > NumPy-Discussion mailing list
> > > > > [hidden email]
> > > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > >
> > > > _______________________________________________
> > > > NumPy-Discussion mailing list
> > > > [hidden email]
> > > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > > _______________________________________________
> > > NumPy-Discussion mailing list
> > > [hidden email]
> > > https://mail.python.org/mailman/listinfo/numpy-discussion
> > >
> >
> > _______________________________________________
> > NumPy-Discussion mailing list
> > [hidden email]
> > https://mail.python.org/mailman/listinfo/numpy-discussion
> _______________________________________________
> NumPy-Discussion mailing list
> [hidden email]
> https://mail.python.org/mailman/listinfo/numpy-discussion
>

_______________________________________________
NumPy-Discussion mailing list
[hidden email]
https://mail.python.org/mailman/listinfo/numpy-discussion

signature.asc (849 bytes) Download Attachment