Trier efficacement un tableau numpy dans l'ordre décroissant?

121

Je suis surpris que cette question spécifique n'ait pas été posée auparavant, mais je ne l'ai vraiment pas trouvée sur SO ni sur la documentation de np.sort.

Disons que j'ai un tableau numpy aléatoire contenant des entiers, par exemple:

> temp = np.random.randint(1,10, 10)    
> temp
array([2, 4, 7, 4, 2, 2, 7, 6, 4, 4])

Si je le trie, j'obtiens un ordre croissant par défaut:

> np.sort(temp)
array([2, 2, 2, 4, 4, 4, 4, 6, 7, 7])

mais je veux que la solution soit triée par ordre décroissant .

Maintenant, je sais que je peux toujours faire:

reverse_order = np.sort(temp)[::-1]

mais cette dernière déclaration est-elle efficace ? Ne crée-t-il pas une copie dans l'ordre croissant, puis inverse cette copie pour obtenir le résultat dans l'ordre inverse? Si tel est bien le cas, existe-t-il une alternative efficace? Il ne semble pas que les np.sortparamètres acceptent pour changer le signe des comparaisons dans l'opération de tri pour obtenir les choses dans l'ordre inverse.

Amelio Vazquez-Reina
la source

Réponses:

139

temp[::-1].sort()trie le tableau en place, tandis que np.sort(temp)[::-1]crée un nouveau tableau.

In [25]: temp = np.random.randint(1,10, 10)

In [26]: temp
Out[26]: array([5, 2, 7, 4, 4, 2, 8, 6, 4, 4])

In [27]: id(temp)
Out[27]: 139962713524944

In [28]: temp[::-1].sort()

In [29]: temp
Out[29]: array([8, 7, 6, 5, 4, 4, 4, 4, 2, 2])

In [30]: id(temp)
Out[30]: 139962713524944
Padraic Cunningham
la source
30
Merci, mais comment temp[::-1].sort()sait-il qu'il doit trier dans l'ordre inverse ?? La façon dont je le lis est: inversez le tableau d'origine, puis triez-le (par ordre croissant). Pourquoi l'inversion du tableau d'origine (dans un ordre aléatoire) et le tri par ordre croissant renverrait-il le tableau dans l'ordre inverse?
Amelio Vazquez-Reina
14
Ce comportement est-il documenté, car il est assez peu intuitif.
ebarr
18
Cela semble fonctionner car le [::-1]dit simplement numpy d'itérer sur le tableau à l'envers, plutôt que de réorganiser le tableau. Ainsi, lorsque le tri sur place se produit, il trie en fait dans l'ordre croissant et déplace les bits, mais laisse la partie de l'itération arrière intacte.
perimosocordiae
45
Avec a=np.array((...))l'idiome a[::-1]n'inverse rien, c'est juste une nouvelle vue sur les mêmes données, plus précisément une vue miroir. Le procédé a[::-1].sort() opère sur l'image en miroir , ce qui implique que lorsque sortse déplace vers la gauche un élément plus petit dans son image en miroir, il le déplace en réalité vers la droite dans le bloc de mémoire réel du atableau. La vue en miroir est triée par ordre croissant, les données réelles sont triées par ordre décroissant. Essayez-le chez vous seul, avec des pièces de monnaie différentes et un miroir!
gboffi
30
Cela devrait vraiment être ajouté en tant que paramètre lisible, comme np.sort(temp,order='descending')plutôt que d'exiger ce genre de hacks
Nathan
92
>>> a=np.array([5, 2, 7, 4, 4, 2, 8, 6, 4, 4])

>>> np.sort(a)
array([2, 2, 4, 4, 4, 4, 5, 6, 7, 8])

>>> -np.sort(-a)
array([8, 7, 6, 5, 4, 4, 4, 4, 2, 2])
Mike O'Connor
la source
2
Meilleure réponse - courte et douce, et aucune connaissance de ce axisqui a np.sortété appliqué n'est nécessaire.
Luke Davis
2
Ceci est différent np.sort(temp)[::-1]du fait qu'il place nans à l'arrière du tableau au lieu de l'avant. Que ce soit bon ou mauvais est à débattre ..
Ben
15

Pour les tableaux courts, je suggère d'utiliser np.argsort()en recherchant les indices du tableau trié négatif, ce qui est légèrement plus rapide que l'inversion du tableau trié:

In [37]: temp = np.random.randint(1,10, 10)

In [38]: %timeit np.sort(temp)[::-1]
100000 loops, best of 3: 4.65 µs per loop

In [39]: %timeit temp[np.argsort(-temp)]
100000 loops, best of 3: 3.91 µs per loop
Kasravnd
la source
a[np.argsort(-a)]est probablement la meilleure approche par rapport aux autres sur cette page. Pas d'inversion de pas -1 et un signe moins de moins à penser.
Jarad
8

Malheureusement, lorsque vous avez un tableau complexe, cela ne np.sort(temp)[::-1]fonctionne que correctement. Les deux autres méthodes mentionnées ici ne sont pas efficaces.

anishtain4
la source
@ anishtain4: Par "tableau complexe", vouliez-vous dire un tableau de nombres complexes? Ou vouliez-vous dire un tableau avec un autre type de complexité (si c'est le cas, veuillez spécifier le type de complexité). Dans les deux cas, je pense que vous pourriez élaborer davantage sur votre réponse, en expliquant comment les autres méthodes pourraient échouer. Merci.
fountainhead
@fountainhead Je veux dire le tableau de nombres complexes. Comme c'est une vieille question, je ne me souviens pas de mon cas de test à partir de là pour élaborer davantage.
anishtain4
7

Soyez prudent avec les dimensions.

Laisser

x  # initial numpy array
I = np.argsort(x) or I = x.argsort() 
y = np.sort(x)    or y = x.sort()
z  # reverse sorted array

Retour complet

z = x[-I]
z = -np.sort(-x)
z = np.flip(y)
  • flipmodifié dans 1.15, versions précédentes requises . Solution: .1.14 axispip install --upgrade numpy

Première dimension inversée

z = y[::-1]
z = np.flipud(y)
z = np.flip(y, axis=0)

Deuxième dimension inversée

z = y[::-1, :]
z = np.fliplr(y)
z = np.flip(y, axis=1)

Essai

Test sur une matrice 100 × 10 × 10 1000 fois.

Method       | Time (ms)
-------------+----------
y[::-1]      | 0.126659  # only in first dimension
-np.sort(-x) | 0.133152
np.flip(y)   | 0.121711
x[-I]        | 4.611778

x.sort()     | 0.024961
x.argsort()  | 0.041830
np.flip(x)   | 0.002026

Cela est principalement dû à la réindexation plutôt qu'à argsort.

# Timing code
import time
import numpy as np


def timeit(fun, xs):
    t = time.time()
    for i in range(len(xs)):  # inline and map gave much worse results for x[-I], 5*t
        fun(xs[i])
    t = time.time() - t
    print(np.round(t,6))

I, N = 1000, (100, 10, 10)
xs = np.random.rand(I,*N)
timeit(lambda x: np.sort(x)[::-1], xs)
timeit(lambda x: -np.sort(-x), xs)
timeit(lambda x: np.flip(x.sort()), xs)
timeit(lambda x: x[-x.argsort()], xs)
timeit(lambda x: x.sort(), xs)
timeit(lambda x: x.argsort(), xs)
timeit(lambda x: np.flip(x), xs)
A. Ouest
la source
6

Bonjour, je cherchais une solution pour le tri inversé d'un tableau numpy à deux dimensions, et je n'ai rien trouvé qui fonctionnait, mais je pense que je suis tombé sur une solution que je télécharge juste au cas où quelqu'un serait dans le même bateau.

x=np.sort(array)
y=np.fliplr(x)

np.sort trie par ordre croissant, ce qui n'est pas ce que vous voulez, mais la commande fliplr retourne les lignes de gauche à droite! Semble fonctionner!

J'espère que cela vous aidera!

Je suppose que c'est similaire à la suggestion de -np.sort (-a) ci-dessus, mais j'ai été découragé par le commentaire que cela ne fonctionne pas toujours. Peut-être que ma solution ne fonctionnera pas toujours non plus, mais je l'ai testée avec quelques tableaux et semble être OK.

Naz
la source
1

Vous pouvez d'abord trier le tableau (Croissant par défaut), puis appliquer np.flip () ( https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html )

FYI Cela fonctionne également avec les objets datetime.

Exemple:

    x = np.array([2,3,1,0]) 
    x_sort_asc=np.sort(x) 
    print(x_sort_asc)

    >>> array([0, 1, 2, 3])

    x_sort_desc=np.flip(x_sort_asc) 
    print(x_sort_desc)

    >>> array([3,2,1,0])
maleckicoa
la source
Pour ceux qui ont NaN dans leurs tableaux, soyez prudent, diverses méthodes proposées produisent des résultats différents. Par exemple, si x = np.array([2,3,np.nan,1,0]) alors l' np.flip(np.sort(x))approche donne [nan 3. 2. 1. 0.], tandis que l' -np.sort(-x)approche donne [3. 2. 1. 0. nan].
Uwe Mayer
1

Voici un petit truc

In[3]: import numpy as np
In[4]: temp = np.random.randint(1,10, 10)
In[5]: temp
Out[5]: array([5, 4, 2, 9, 2, 3, 4, 7, 5, 8])

In[6]: sorted = np.sort(temp)
In[7]: rsorted = list(reversed(sorted))
In[8]: sorted
Out[8]: array([2, 2, 3, 4, 4, 5, 5, 7, 8, 9])

In[9]: rsorted
Out[9]: [9, 8, 7, 5, 5, 4, 4, 3, 2, 2]
Don Coder
la source
-3

je suggère d'utiliser ceci ...

np.arange(start_index, end_index, intervals)[::-1]

par exemple:

np.arange(10, 20, 0.5)
np.arange(10, 20, 0.5)[::-1]

Puis votre resault:

[ 19.5,  19. ,  18.5,  18. ,  17.5,  17. ,  16.5,  16. ,  15.5,
    15. ,  14.5,  14. ,  13.5,  13. ,  12.5,  12. ,  11.5,  11. ,
    10.5,  10. ]
morteza omidipoor
la source
1
Comment cela résout-il le problème? Vous êtes juste créer un tout, un tableau sans rapport avec, nouveau (décroissant) qui - par ailleurs - pourrait être fait d'une manière plus efficace: np.arange(20-0.5, 10-0.5, -0.5). Mais c'est une histoire différente et peut être, en raison de la mauvaise lisibilité, discutable. Un tableau d'entrée n'est pas trié du tout
Daniel