Tri des tableaux dans NumPy par colonne

336

Comment puis-je trier un tableau dans NumPy par la nième colonne?

Par exemple,

a = array([[9, 2, 3],
           [4, 5, 6],
           [7, 0, 5]])

Je voudrais trier les lignes par la deuxième colonne, de sorte que je revienne:

array([[7, 0, 5],
       [9, 2, 3],
       [4, 5, 6]])
Paul Wintz
la source
8
C'est un très mauvais exemple car ce np.sort(a, axis=0)serait une solution satisfaisante pour la matrice donnée. J'ai suggéré un montage avec un meilleur exemple mais j'ai été rejeté, bien qu'en réalité la question soit beaucoup plus claire. L'exemple devrait ressembler a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]])à la sortie souhaitéearray([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
David
29
David, vous ne comprenez pas le point de la question. Il veut garder la même commande dans chaque rangée.
marcorossi
@marcorossi J'ai bien compris, mais l'exemple a été très mal formulé car, comme je l'ai dit, il y avait plusieurs réponses possibles (qui, cependant, n'auraient pas satisfait la demande du PO). Un montage ultérieur basé sur mon commentaire a en effet été approuvé (drôle que le mien ait été rejeté, cependant). Alors maintenant, tout va bien.
David

Réponses:

141

La réponse de @steve est en fait la manière la plus élégante de le faire.

Pour la manière "correcte", voir l'argument du mot clé order de numpy.ndarray.sort

Cependant, vous devrez voir votre tableau comme un tableau avec des champs (un tableau structuré).

La façon "correcte" est assez moche si vous n'avez pas défini initialement votre tableau avec des champs ...

Comme exemple rapide, pour le trier et renvoyer une copie:

In [1]: import numpy as np

In [2]: a = np.array([[1,2,3],[4,5,6],[0,0,1]])

In [3]: np.sort(a.view('i8,i8,i8'), order=['f1'], axis=0).view(np.int)
Out[3]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

Pour le trier sur place:

In [6]: a.view('i8,i8,i8').sort(order=['f1'], axis=0) #<-- returns None

In [7]: a
Out[7]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

@ Steve est vraiment la façon la plus élégante de le faire, autant que je sache ...

Le seul avantage de cette méthode est que l'argument "order" est une liste des champs par lesquels ordonner la recherche. Par exemple, vous pouvez trier par la deuxième colonne, puis la troisième colonne, puis la première colonne en fournissant order = ['f1', 'f2', 'f0'].

Joe Kington
la source
3
Dans mon numpy 1.6.1rc1, il lèveValueError: new type not compatible with array.
Clippit
9
Serait-il judicieux de déposer une demande de fonctionnalité pour que la méthode "correcte" soit rendue moins laide?
endolith
4
Et si les valeurs du tableau sont float? Dois-je changer quelque chose?
Marco
1
Et pour le type hybride comme a = np.array([['a',1,2,3],['b',4,5,6],['c',0,0,1]])quelle approche dois-je suivre?
ePascoal
10
Un avantage majeur de cette méthode par rapport à celle de Steve est qu'elle permet de trier de très grandes baies. Pour un tableau suffisamment grand, les index renvoyés par np.argsortpeuvent eux-mêmes occuper beaucoup de mémoire, et en plus, l'indexation avec un tableau générera également une copie du tableau en cours de tri.
ali_m
738

Je suppose que cela fonctionne: a[a[:,1].argsort()]

Cela indique la deuxième colonne de aet la trie en fonction de celle-ci en conséquence.

Steve Tjoa
la source
2
Ce n'est pas clair, qu'y a- 1t-il ici? l'index à trier?
orezvani
29
[:,1]indique la deuxième colonne de a.
Steve Tjoa
60
Si vous voulez le tri inverse, modifiez-lea[a[:,1].argsort()[::-1]]
Steven C. Howell
1
Semble simple et fonctionne! Est-ce plus rapide np.sortou non?
Václav Pavlík
14
Je trouve cela plus facile à lire:ind = np.argsort( a[:,1] ); a = a[ind]
poppie
32

Vous pouvez trier sur plusieurs colonnes selon la méthode de Steve Tjoa en utilisant un tri stable comme mergesort et en triant les indices des colonnes les moins significatives aux colonnes les plus significatives:

a = a[a[:,2].argsort()] # First sort doesn't need to be stable.
a = a[a[:,1].argsort(kind='mergesort')]
a = a[a[:,0].argsort(kind='mergesort')]

Cela trie par colonne 0, puis 1, puis 2.

JJ
la source
4
Pourquoi First Sort n'a-t-il pas besoin d'être stable?
Little Bobby Tables du
10
Bonne question - stable signifie que lorsqu'il y a une égalité, vous conservez l'ordre d'origine et que l'ordre d'origine du fichier non trié n'est pas pertinent.
JJ
Cela semble être un point vraiment super important. avoir une liste qui ne trie pas en silence serait mauvais.
Chat maladroit
19

Dans le cas où quelqu'un souhaite utiliser le tri dans une partie critique de ses programmes, voici une comparaison des performances pour les différentes propositions:

import numpy as np
table = np.random.rand(5000, 10)

%timeit table.view('f8,f8,f8,f8,f8,f8,f8,f8,f8,f8').sort(order=['f9'], axis=0)
1000 loops, best of 3: 1.88 ms per loop

%timeit table[table[:,9].argsort()]
10000 loops, best of 3: 180 µs per loop

import pandas as pd
df = pd.DataFrame(table)
%timeit df.sort_values(9, ascending=True)
1000 loops, best of 3: 400 µs per loop

Il semble donc que l'indexation avec argsort soit la méthode la plus rapide à ce jour ...

prl900
la source
19

Depuis le wiki de documentation Python , je pense que vous pouvez faire:

a = ([[1, 2, 3], [4, 5, 6], [0, 0, 1]]); 
a = sorted(a, key=lambda a_entry: a_entry[1]) 
print a

La sortie est:

[[[0, 0, 1], [1, 2, 3], [4, 5, 6]]]
user541064
la source
21
Avec cette solution, on obtient une liste au lieu d'un tableau NumPy, donc cela peut ne pas toujours être pratique (prend plus de mémoire, est probablement plus lent, etc.).
Eric O Lebigot
cette "solution" est plus lente par la réponse la plus votée d'un facteur de ... enfin, proche de l'infini en fait
Jivan
16

Dans la liste de diffusion NumPy , voici une autre solution:

>>> a
array([[1, 2],
       [0, 0],
       [1, 0],
       [0, 2],
       [2, 1],
       [1, 0],
       [1, 0],
       [0, 0],
       [1, 0],
      [2, 2]])
>>> a[np.lexsort(np.fliplr(a).T)]
array([[0, 0],
       [0, 0],
       [0, 2],
       [1, 0],
       [1, 0],
       [1, 0],
       [1, 0],
       [1, 2],
       [2, 1],
       [2, 2]])
fgregg
la source
3
La généralisation correcte est a[np.lexsort(a.T[cols])]. où cols=[1]dans la question d'origine.
Contrôlé par radio le
5

J'avais un problème similaire.

Mon problème:

Je veux calculer un SVD et j'ai besoin de trier mes valeurs propres par ordre décroissant. Mais je veux garder la correspondance entre les valeurs propres et les vecteurs propres. Mes valeurs propres étaient dans la première ligne et le vecteur propre correspondant en dessous dans la même colonne.

Je veux donc trier un tableau bidimensionnel par colonne en fonction de la première ligne dans l'ordre décroissant.

Ma solution

a = a[::, a[0,].argsort()[::-1]]

Donc comment ça fonctionne?

a[0,] est juste la première ligne que je veux trier.

Maintenant, j'utilise argsort pour obtenir l'ordre des indices.

J'utilise [::-1]car j'ai besoin d'un ordre décroissant.

Enfin, j'utilise a[::, ...]pour obtenir une vue avec les colonnes dans le bon ordre.

xuma202
la source
1

Un lexsortexemple un peu plus compliqué - descendant sur la 1ère colonne, montant secondairement sur la 2ème. Les astuces avec lexsortsont qu'il trie sur les lignes (d'où le .T), et donne la priorité au dernier.

In [120]: b=np.array([[1,2,1],[3,1,2],[1,1,3],[2,3,4],[3,2,5],[2,1,6]])
In [121]: b
Out[121]: 
array([[1, 2, 1],
       [3, 1, 2],
       [1, 1, 3],
       [2, 3, 4],
       [3, 2, 5],
       [2, 1, 6]])
In [122]: b[np.lexsort(([1,-1]*b[:,[1,0]]).T)]
Out[122]: 
array([[3, 1, 2],
       [3, 2, 5],
       [2, 1, 6],
       [2, 3, 4],
       [1, 1, 3],
       [1, 2, 1]])
hpaulj
la source
0

Voici une autre solution considérant toutes les colonnes (manière plus compacte de la réponse de JJ );

ar=np.array([[0, 0, 0, 1],
             [1, 0, 1, 0],
             [0, 1, 0, 0],
             [1, 0, 0, 1],
             [0, 0, 1, 0],
             [1, 1, 0, 0]])

Trier avec lexsort,

ar[np.lexsort(([ar[:, i] for i in range(ar.shape[1]-1, -1, -1)]))]

Production:

array([[0, 0, 0, 1],
       [0, 0, 1, 0],
       [0, 1, 0, 0],
       [1, 0, 0, 1],
       [1, 0, 1, 0],
       [1, 1, 0, 0]])
Sefa
la source
0

En utilisant simplement le tri, utilisez le numéro de colonne en fonction de celui que vous souhaitez trier.

a = np.array([1,1], [1,-1], [-1,1], [-1,-1]])
print (a)
a=a.tolist() 
a = np.array(sorted(a, key=lambda a_entry: a_entry[0]))
print (a)
Jerin
la source
0

C'est une vieille question, mais si vous avez besoin de généraliser cela à des tableaux de dimension supérieure à 2, voici la solution qui peut être facilement généralisée:

np.einsum('ij->ij', a[a[:,1].argsort(),:])

C'est une exagération pour deux dimensions et a[a[:,1].argsort()]serait suffisant selon la réponse de @ steve, mais cette réponse ne peut pas être généralisée à des dimensions plus élevées. Tu peux trouver un exemple de réseau 3D dans cette question.

Production:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
Ehsan
la source