Skip to content

ENH: sort/argsort: add descending parameter #14728

Closed

Description

@SorteKanin

Currently, in order to sort an array in reverse order you have to use some form of strange hack, which may or may not be guaranteed to work in the future.

I suggest adding a reverse parameter to np.sort, similar to the native Python sort() function. This would make the hacks unnecessary and improve readability of code, by making the sorting order explicit.

This feature should not result in any performance problems - after all, the sorting function simply have to reverse the boolean result when comparing values.

Reproducing code example:

import numpy as np

a = np.arange(10)
np.random.shuffle(a)
a.sort(reverse = True)
print(a) # should print "[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]"

Activity

  1. SorteKanin commented on Oct 16, 2019

    @SorteKanin
    Author

    Yes, but it should also work for the in-place ndarray.sort() version, like I showed in the code example.

  2. changed the title [-]Feature Request: Reverse parameter in np.sort()[/-] [+]ENH: Reverse parameter in np.sort()[/+] on Nov 4, 2019
  3. rossbar commented on Jul 22, 2020

    @rossbar
    Contributor

    This feature request was taken up in #14989 - there's a lot of good discussion there on this topic.

    I just wanted to chime in here that you can already get reversed sorting without any copies with ndarray.sort and indexing:

    >>> a = np.arange(1e6, dtype=int)
    >>> rg = np.random.default_rng()
    >>> rg.shuffle(a)
    >>> a.sort()    # In-place, ascending order
    >>> a = a[::-1] # A view, no copies made
  4. eric-wieser commented on Jul 22, 2020

    @eric-wieser
    Member

    I just wanted to chime in here that you can already get reversed sorting without any copies with ndarray.sort and indexing:

    This approach does not result in a stable sort

  5. seberg commented on Jun 29, 2022

    @seberg
    Member

    Interestingly, the here "descending" is used. I am a bit uncertain about that choice, since it is at odds with Pythons sorted and sort.

    In general, we should probably solve this in C, this may require adding more sorting "methods" in C, which was not possible for a long time.

    However, NEP 41+43 now makes it possible in a pretty straight-forward manner. I could help with that first step (extending the machinery, or at least providing a blueprint for it), but I don't think I can commit time to the feature itself. If anyone can make it a their larger project, please let me know so I can help with that first step.
    (It is pretty new API, so there is no direct blueprint, me making a start on it would probably safe a lot of time for anyone diving into it.)

  6. added
    triagedIssue/PR that was discussed in a triage meeting
    and removed
    triage reviewIssue/PR to be discussed at the next triage meeting
    on Jul 3, 2022
  7. seberg commented on Jun 27, 2024

    @seberg
    Member

    Adding one more note, since it is easy to miss. Not only is just reverting not stable, there is also the problem of NaN sort order.

    To me it seems best if NaNs are always sorted to the end if such an option is added, in other words it preserves -reverse)sort(-x) == sorted(x) (doesn't work for integers mostly because the - doesn't behave right).

  8. tvladyslav commented on Oct 31, 2024

    @tvladyslav

    Strongly upvote and here is why.
    Recently I needed a descending sort function, generic over dimension (3 dimensions, sort across any).
    StackOverflow suggests 2 main options:

    1. arr[::-1], which is OK, but not applicable to any dimension. I'd need 3 ifs.
    2. -np.sort(-arr), which I choose as an elegant working solution.

    Now let's try following code:

    import numpy as np
    
    def sort_descending():
    	shape = (4, 3, 4)
    	x = np.arange(np.prod(shape), dtype=np.uint8)
    	np.random.shuffle(x)
    	x = x.reshape(shape)
    	sorted = -np.sort(-x, axis=0)  # descending
    	print(sorted)
    
    sort_descending()

    It works fine with floats and signed integers. But what is the output for np.uint8? Here it is:
    [[[43 40 37 27]
    [39 22 46 0]
    [42 45 31 47]]

    [[41 36 34 20]
    [35 11 38 44]
    [18 30 23 24]]

    [[33 25 29 12]
    [ 7 6 15 13]
    [17 26 10 14]]

    [[32 21 28 5]
    [ 1 2 3 4]
    [16 19 9 8]]]
    See something strange? Vector [0, 44, 13, 4] is not sorted. Whole output is correct except one vector!
    This happens due to several factors meeting together:

    1. Code uses - for unsigned type;
    2. numpy doesn't warn about negative unsigned value;
    3. result is almost correct. It is difficult to catch this problem visually, only test helps;
    4. developer has no reverse parameter in np.sort and has to use workrounds.

    I bet I'm not alone in making this kind of mistake. How many results out there, which are ... almost correct?

  9. jakevdp commented on Oct 31, 2024

    @jakevdp
    Contributor

    Regarding reverse-sorting along an axis: does np.flip(np.sort(x, axis=axis), axis=axis) do what you want here? Setting aside questions about where NaN values end up in reverse-sorted arrays (which is left unspecified in the array API standard), I think this should give the right result.

  10. tvladyslav commented on Nov 2, 2024

    @tvladyslav

    Regarding reverse-sorting along an axis: does np.flip(np.sort(x, axis=axis), axis=axis) do what you want here? Setting aside questions about where NaN values end up in reverse-sorted arrays (which is left unspecified in the array API standard), I think this should give the right result.

    Yes, it does. I found this discussion and used flip in my code. It works.

  11. changed the title [-]ENH: Reverse parameter in np.sort()[/-] [+]ENH: `sort`/`argsort`: add `descending` parameter[/+] on Dec 1, 2024
  12. mdhaber commented on Dec 1, 2024

    @mdhaber
    Contributor

    Interestingly, the here "descending" is used

    It looks like the array API chose descending to match TensorFlow; see here. (I don't mean to try to justify that choice; I just see that was the rationale given.)

    Based on #25076 (comment), it looks like this was intended to be addressed in gh-25076, but it didn't make it onto the checklist.

  13. added
    triage reviewIssue/PR to be discussed at the next triage meeting
    and removed
    triagedIssue/PR that was discussed in a triage meeting
    on Dec 18, 2024
  14. mattip commented on Dec 18, 2024

    @mattip
    Member

    We discussed this at a recent community meeting and the opinions were that Nan sorting is kind of up to us to decide, there is no standard. It is undefined in PyTorch what happens.

  15. mattip commented on Jan 8, 2025

    @mattip
    Member

    We discussed this in a recent triage meeting. The general feeling was that we should have a new sorting function, but we are limited how to add another function into the DType C struct.

  16. added
    triagedIssue/PR that was discussed in a triage meeting
    and removed
    triage reviewIssue/PR to be discussed at the next triage meeting
    on Feb 5, 2025
  17. carlosgmartin commented on Aug 14, 2025

    @carlosgmartin
    Contributor

    As previously pointed out, a simple reversal breaks stability, and negating fails for non-negatable dtypes. This can even lead to subtle bugs, as pointed out by tvladyslav.

    Since the most common workarounds are error-prone, I'm strongly in favor of adding a descending argument to sort/argsort. JAX's version of argsort already has it.

    In case it's helpful to anyone, here's a workaround that seems to work correctly:

    import numpy as np
    
    def argsort(a, axis=-1, descending=False):
        if descending:
            a = np.flip(a, axis)
            i = np.argsort(a, stable=True)
            i = np.flip(i, axis)
            i = a.shape[axis] - 1 - i
            return i
        else:
            return np.argsort(a, axis, stable=True)
    
    def implies(a, b):
        return ~a | b
    
    def main():
        a = np.array([4, 2, 2, 8, 2, 9, 1], np.uint8)
        i = argsort(a, descending=True)
        ai = a[i]
        print(i)
        print(ai)
        # The array is sorted in descending order.
        assert (ai[:-1] >= ai[1:]).all()
        # If two elements are equal, the lower-index element appears first.
        assert implies(ai[:-1] == ai[1:], i[:-1] < i[1:]).all()
    
    if __name__ == "__main__":
        main()
    [5 3 0 1 2 4 6]
    [9 8 4 2 2 2 1]
    
  18. seberg commented on Aug 14, 2025

    @seberg
    Member

    FWIW, #28516 will probably add the basic infrastructure to do this soon.
    That said it doesn't implement it and we include other fast sorting code that may not do the right thing out of the box (yet) -- that is, NaNs should be sorted to the end also for reverse by default I think.

  19. MaanasArora commented on Jun 30, 2026

    @MaanasArora
    Contributor

    I think this was done in #31345, so closing!

    Edit: there are some follow-ups in #31423 and #31424, but those are their own issues now :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions