[Numpy-discussion] Use-case for np.choose
Sun Nov 8 07:36:00 CST 2009
On Sun, Nov 8, 2009 at 5:00 AM, David Goldsmith <firstname.lastname@example.org> wrote:
> On Sun, Nov 8, 2009 at 12:57 AM, Anne Archibald <email@example.com>
>> 2009/11/8 David Goldsmith <firstname.lastname@example.org>:
>> > On Sat, Nov 7, 2009 at 11:59 PM, Anne Archibald
>> > <email@example.com>
>> > wrote:
>> >> 2009/11/7 David Goldsmith <firstname.lastname@example.org>:
>> >> > So in essence, at least as it presently functions, the shape of 'a'
>> >> > *defines* what the individual choices are within 'choices`, and if
>> >> > 'choices'
>> >> > can't be parsed into an integer number of such individual choices,
>> >> > that's
>> >> > when an exception is raised?
>> >> Um, I don't think so.
>> >> Think of it this way: you provide np.choose with a selector array, a,
>> >> and a list (not array!) [c0, c1, ..., cM] of choices. You construct an
>> >> output array, say r, the same shape as a (no matter how many
>> >> dimensions it has).
>> > Except that I haven't yet seen a working example with 'a' greater than
>> > 1-D,
>> > Josef's last two examples notwithstanding; or is that what you're saying
>> > is
>> > the bug.
>> There's nothing magic about A being one-dimensional.
>> C = np.random.randn(2,3,5)
>> A = (C>-1).astype(int) + (C>0).astype(int) + (C>1).astype(int)
>> R = np.choose(A, (-1, -C, C, 1))
> OK, now I get it: np.choose(A[0,:,:], (-1,-C,C,-1)) and
> np.choose(A[0,:,0].reshape((3,1)), (-1,-C,C,1)), e.g., also work, but
> np.choose(A[0,:,0], (-1,-C,C,-1)) doesn't - what's necessary for choose's
> arguments is that both can be broadcast to a common shape (as you state
> below), but choose won't reshape the arguments for you to make this
> possible, you have to do so yourself first, if necessary. That does appear
> to be what's happening now; but do we want choose to be smarter than that
> (e.g., for np.choose(A[0,:,0], (-1,-C,C,-1)) to work, so that the user
> doesn't need to include the .reshape((3,1)))?
No, I don't think we want to be that smart.
If standard broadcasting rules apply, as I think they do, then I wouldn't want
any special newaxis or reshapes done automatically. It will be confusing,
the function wouldn't know what to do if there are, e.g., as many rows as
columns, and this looks like a big source of errors.
Standard broadcasting is pretty nice (once I got the hang of it), and adding
a lot of np.newaxis (or some reshapes) to the code is only a small price to pay.
>> Requv = np.minimum(np.abs(C),1)
>> def wedge(*functions):
>> """Return a function whose value is the minimum of those of
>> def wedgef(X):
>> fXs = [f(X) for f in functions]
>> A = np.argmin(fXs, axis=0)
>> return np.choose(A,fXs)
>> return wedgef
>> so e.g. np.abs is -wedge(lambda X: X, lambda X: -X)
>> This works no matter what shape of X the user supplies - so a wedged
>> function can be somewhat ufunclike - by making A the same shape.
>> >> The (i0, i1, ..., iN) element of the output array
>> >> is obtained by looking at the (i0, i1, ..., iN) element of a, which
>> >> should be an integer no larger than M; say j. Then r[i0, i1, ..., iN]
>> >> = cj[i0, i1, ..., iN]. That is, each element of the selector array
>> >> determines which of the choice arrays to pull the corresponding
>> >> element from.
>> > That's pretty clear (thanks for doing my work for me). ;-), Yet, see
>> > above.
>> >> For example, suppose that you are processing an array C, and have
>> >> constructed a selector array A the same shape as C in which a value is
>> >> 0, 1, or 2 depending on whether the C value is too small, okay, or too
>> >> big respectively. Then you might do something like:
>> >> C = np.choose(A, [-inf, C, inf])
>> >> This is something you might want to do no matter what shape A and C
>> >> have. It's important not to require that the choices be an array of
>> >> choices, because they often have quite different shapes (here, two are
>> >> scalars) and it would be wasteful to broadcast them up to the same
>> >> shape as C, just to stack them.
>> > OK, that's a pretty generic use-case, thanks; let me see if I understand
>> > it
>> > correctly: A is some how created independently with a 0 everywhere C is
>> > too
>> > small, a 1 everywhere C is OK, and a 2 everywhere C is too big; then
>> > np.choose(A, [-inf, C, inf]) creates an array that is -inf everywhere C
>> > is
>> > too small, inf everywhere C is too large, and C otherwise (and since
>> > -inf
>> > and inf are scalars, this implies broadcasting of these is taking
>> > place).
>> > This is what you're asserting *should* be the behavior. So, unless
>> > there is
>> > disagreement about this (you yourself said the opposite viewpoint might
>> > rationally be held) np.choose definitely presently has a bug, namely,
>> > the
>> > index array can't be of arbitrary shape.
>> There seems to be some disagreement between versions, but both Josef
>> and I find that the index array *can* be arbitrary shape. In numpy
>> 1.2.1 I find that all the choose items must be the same shape as it,
>> which I think is a bug.
>> What I suggested might be okay was if the index array was not
>> broadcasted, so that the outputs always had exactly the same shape as
>> the index array. But upon reflection it's useful to be able to use a
>> 1-d array to select rows from a set of matrices, so I now think that
>> all of A and the elements of choose should be broadcast to the same
>> shape. This seems to be what Josef observes in his version of numpy,
>> so maybe there's nothing to do.
>> > DG
>> >> Anne
>> >> _______________________________________________
>> >> NumPy-Discussion mailing list
>> >> NumPy-Discussion@scipy.org
>> >> http://mail.scipy.org/mailman/listinfo/numpy-discussion
>> > _______________________________________________
>> > NumPy-Discussion mailing list
>> > NumPy-Discussion@scipy.org
>> > http://mail.scipy.org/mailman/listinfo/numpy-discussion
>> NumPy-Discussion mailing list
> NumPy-Discussion mailing list
More information about the NumPy-Discussion