Skip to content

Feature request: axis and keepdims for gufuncs (and use it in np.median) #8810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
7 tasks done
mhvk opened this issue Mar 22, 2017 · 11 comments
Closed
7 tasks done

Feature request: axis and keepdims for gufuncs (and use it in np.median) #8810

mhvk opened this issue Mar 22, 2017 · 11 comments

Comments

@mhvk
Copy link
Contributor

mhvk commented Mar 22, 2017

EDITS following different comments:

Rationale

Following the suggestion that, ideally, np.median and similar functions would be gufuncs so that they could be overridden with __array_ufunc__ (#8247), it was realised that the main show-stopper is the absence of an axis argument for gufuncs (also annoying in the context of the new all_equal gufuncs (#8528). Furthermore, for functions like argmax, etc., a keepdims would be very useful (#8710).

Specification

In practice, the new axis argument would use underneath a more general axes argument, which is a list of tuples of axes for each of the core dimensions. For normal ufunc, axes obviously has to be empty (or absent); for gufunc, defaults would be -1, -2, etc., as needed. For the output, the tuple would be extended by one (or possibly more) from that implied by the signature if keepdims is set . Short-cuts may be possible, e.g., for gufuncs with only one index, a single number (or perhaps even None for ravelling all dimensions); one would distinguish the general case of axes with the simple case of axis.

Implementation

Looking at the reference, it would seem that the C-api would not need to change, but the routine actually calling the underlying iteration machinery in umath/ufunc_object.c would need to do appropriate transposes of the input and output arrays. Going through the call sequence to see places where change would be necessary (not marked with X):

Excluded

In principle, it might be nice to allow axis to refer to multiple axes or all of them (None), but this is substantially more complicated, and it is not obvious it isn't better to let wrapping code deal with this. Indeed, this is done for np.median, which ravels any requested axes as needed before passing the array on to partition (which can handle only a single axis).

@eric-wieser
Copy link
Member

eric-wieser commented Mar 22, 2017

There's some extended discussion about this on the mailing list. (and in #5197)

I think it might be worth special-casing signatures of the form (n)->() first, and additionally adding a keepdims argument (#8710).

@mhvk
Copy link
Contributor Author

mhvk commented Mar 22, 2017

Ah, yes, I should have checked for duplication; maybe this issue can be specifically about implementation. In that respect, good point about keepdims -- I've added it in the description.

@charris
Copy link
Member

charris commented Mar 22, 2017

I don't think gufuncs are really a good fit for an axis argument as they are currently conceived. It might be good to make a list of the functions we would like to implement as ufuncs to see what would be the best solution.

@eric-wieser
Copy link
Member

eric-wieser commented Mar 22, 2017

Thinking more carefully, any reduction of the form (m,n,...)->() or even (m,n,...),(o,p,...)->() (where ... are matching lengths) has an unambiguous meaning of keepdims, but beyond the (n)->() case, we need to think very carefully about the interpretation of the axis argument

@mhvk mhvk changed the title Feature request: axis argument for gufuncs (and use it in np.median) Feature request: axis and keepdims for gufuncs (and use it in np.median) Mar 22, 2017
@mhvk
Copy link
Contributor Author

mhvk commented Mar 22, 2017

@charris - I think it will actually be quite trivial to implement (but realise that I may have to prove that...)

@shoyer
Copy link
Member

shoyer commented Mar 22, 2017

I don't think adding axis to gufuncs helps that much for median, because the default behavior for median is to reduce over all axes (also consider a single axis vs. tuples of various lengths).

We could certainly add another override for functions with the standard logic for handling axis on aggregation functions, but it should probably be more specialized that __array_ufunc__.

@mhvk
Copy link
Contributor Author

mhvk commented Mar 22, 2017

@shoyer - maybe median is not the perfect example, but it would still help: it would need a separate front-end that, for axis=None or multiple axes, would ravel the array appropriately. But that is also all it needs to do before passing it on to the gufunc implementation, and with just that, subclasses could still override the actual calculation, as long as they allow reshaping (which I hope would be the majority).

p.s. Just to be clear: my goal is not to make every numerical function fit in the gufunc mold, but to ensure the actual numerical aspect of all those functions can be overridden by duck-type arrays.

@charris
Copy link
Member

charris commented Mar 22, 2017

I'm not so much interested in the difficulty as the usefulness. Would it be correct to say that the main interest in adding an axis is to reuse the override functionality?

@mhvk
Copy link
Contributor Author

mhvk commented Mar 22, 2017

@charris - It is a combination of liking the idea of being able to override numerical functions and direct usefulness. For the latter, I am thinking in particular of #8528 (all_equal), which to me showed the weakness of the present scheme, and the realisation in #8710 that argsort and co would match the gufunc behaviour well if only it supported axis and keepdims.

@mhvk
Copy link
Contributor Author

mhvk commented Mar 23, 2017

See #8819 for a trial implementation of adding an axis keyword. Since the iterator supports fairly arbitrary axis, it was mostly a matter of adding a layer of indirection in the code preparing arrays.

Added notes:

  1. A few quick timing tests show a 50% increase in time if one passes in axes for small arrays; for large ones, it depends only on memory organisation of the arrays.
  2. Haven't really thought about keepdims yet.

@mhvk
Copy link
Contributor Author

mhvk commented Jul 3, 2018

Looking at this issue again, I realized it can actually be closed: gufuncs now have axis and keepdims, so functions like partition could be rewritten in terms of gufuncs if so desired. Possible raveling of multiple axes would still need to be done outside of the gufunc.

@mhvk mhvk closed this as completed Jul 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants