Sortieren von Arrays in NumPy nach Spalten

336

Wie kann ich ein Array in NumPy nach der n-ten Spalte sortieren?

Zum Beispiel,

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

Ich möchte Zeilen nach der zweiten Spalte sortieren, damit ich zurückkomme:

array([[7, 0, 5],
       [9, 2, 3],
       [4, 5, 6]])
Paul Wintz
quelle
8
Dies ist ein wirklich schlechtes Beispiel, da np.sort(a, axis=0)dies eine zufriedenstellende Lösung für die gegebene Matrix wäre. Ich schlug eine Bearbeitung mit einem besseren Beispiel vor, wurde aber abgelehnt, obwohl die Frage tatsächlich viel klarer wäre. Das Beispiel sollte so etwas wie a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]])mit der gewünschten Ausgabe seinarray([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
David
29
David, du verstehst den Punkt der Frage nicht. Er möchte die Reihenfolge in jeder Zeile gleich halten.
Marcorossi
@marcorossi Ich habe den Punkt verstanden, aber das Beispiel wurde sehr schlecht formuliert, weil es, wie gesagt, mehrere mögliche Antworten gab (die jedoch die Anfrage des OP nicht erfüllt hätten). Eine spätere Bearbeitung aufgrund meines Kommentars wurde tatsächlich genehmigt (lustig, dass meine jedoch abgelehnt wurde). Jetzt ist alles in Ordnung.
David

Antworten:

140

@steve 's ist eigentlich die eleganteste Art, es zu tun.

Die "richtige" Methode finden Sie im order-Schlüsselwortargument von numpy.ndarray.sort

Sie müssen Ihr Array jedoch als Array mit Feldern (ein strukturiertes Array) anzeigen.

Der "richtige" Weg ist ziemlich hässlich, wenn Sie Ihr Array ursprünglich nicht mit Feldern definiert haben ...

Als kurzes Beispiel, um es zu sortieren und eine Kopie zurückzugeben:

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]])

So sortieren Sie es an Ort und Stelle:

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's ist wirklich der eleganteste Weg, soweit ich weiß ...

Der einzige Vorteil dieser Methode besteht darin, dass das Argument "order" eine Liste der Felder ist, nach denen die Suche sortiert werden soll. Sie können beispielsweise nach der zweiten Spalte, dann nach der dritten Spalte und dann nach der ersten Spalte sortieren, indem Sie order = ['f1', 'f2', 'f0'] angeben.

Joe Kington
quelle
3
In meiner Nummer 1.6.1rc1 erhöht esValueError: new type not compatible with array.
Clippit
9
Wäre es sinnvoll, eine Feature-Anfrage einzureichen, damit der "richtige" Weg weniger hässlich wird?
Endolith
4
Was ist, wenn die Werte im Array sind float? Soll ich etwas ändern?
Marco
1
Und für Hybridtypen wie a = np.array([['a',1,2,3],['b',4,5,6],['c',0,0,1]])welchen Ansatz sollte ich folgen?
ePascoal
10
Ein Hauptvorteil dieser Methode gegenüber Steve besteht darin, dass sehr große Arrays an Ort und Stelle sortiert werden können. Für ein ausreichend großes Array beanspruchen die von zurückgegebenen Indizes np.argsortmöglicherweise selbst ziemlich viel Speicher. Darüber hinaus generiert die Indizierung mit einem Array auch eine Kopie des Arrays, das sortiert wird.
Ali_m
735

Ich nehme an, das funktioniert: a[a[:,1].argsort()]

Dies zeigt die zweite Spalte von an aund sortiert sie entsprechend danach.

Steve Tjoa
quelle
2
Das ist nicht klar, was ist 1hier drin? der Index, nach dem sortiert werden soll?
Orezvani
29
[:,1]gibt die zweite Spalte von an a.
Steve Tjoa
60
Wenn Sie die umgekehrte Sortierung wünschen, ändern Sie dies zua[a[:,1].argsort()[::-1]]
Steven C. Howell
1
Sieht einfach aus und funktioniert! Ist es schneller als np.sortoder nicht?
Václav Pavlík
14
Ich finde das leichter zu lesen:ind = np.argsort( a[:,1] ); a = a[ind]
Mohn
32

Sie können nach Steve Tjoas Methode nach mehreren Spalten sortieren, indem Sie eine stabile Sortierung wie Mergesort verwenden und die Indizes von den niedrigstwertigen zu den höchstwertigen Spalten sortieren:

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')]

Dies sortiert nach Spalte 0, dann 1, dann 2.

JJ
quelle
4
Warum muss First Sort nicht stabil sein?
Little Bobby Tables
10
Gute Frage - stabil bedeutet, dass Sie bei einem Gleichstand die ursprüngliche Reihenfolge beibehalten und die ursprüngliche Reihenfolge der unsortierten Datei irrelevant ist.
JJ
Dies scheint ein wirklich sehr wichtiger Punkt zu sein. Eine Liste zu haben, die stillschweigend nicht sortiert, wäre schlecht.
Unbeholfene Katze
20

Aus dem Python-Dokumentations-Wiki können Sie Folgendes tun:

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

Die Ausgabe ist:

[[[0, 0, 1], [1, 2, 3], [4, 5, 6]]]
user541064
quelle
20
Mit dieser Lösung erhält man eine Liste anstelle eines NumPy-Arrays, was möglicherweise nicht immer praktisch ist (benötigt mehr Speicher, ist wahrscheinlich langsamer usw.).
Eric O Lebigot
18

Für den Fall, dass jemand die Sortierung in einem kritischen Teil seines Programms nutzen möchte, finden Sie hier einen Leistungsvergleich für die verschiedenen Vorschläge:

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

Es sieht also so aus, als wäre die Indizierung mit argsort die bisher schnellste Methode ...

prl900
quelle
16

In der NumPy-Mailingliste finden Sie eine weitere Lösung:

>>> 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
quelle
3
Die richtige Verallgemeinerung ist a[np.lexsort(a.T[cols])]. wo cols=[1]in der ursprünglichen Frage.
Radio Controlled
5

Ich hatte ein ähnliches Problem.

Mein Problem:

Ich möchte eine SVD berechnen und muss meine Eigenwerte in absteigender Reihenfolge sortieren . Aber ich möchte die Abbildung zwischen Eigenwerten und Eigenvektoren beibehalten. Meine Eigenwerte befanden sich in der ersten Zeile und der entsprechende Eigenvektor darunter in derselben Spalte.

Daher möchte ich ein zweidimensionales Array spaltenweise nach der ersten Zeile in absteigender Reihenfolge sortieren.

Meine Lösung

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

Wie funktioniert das?

a[0,] ist nur die erste Zeile, nach der ich sortieren möchte.

Jetzt benutze ich argsort, um die Reihenfolge der Indizes zu erhalten.

Ich benutze, [::-1]weil ich absteigende Reihenfolge brauche.

Zuletzt verwende ich a[::, ...], um eine Ansicht mit den Spalten in der richtigen Reihenfolge zu erhalten.

xuma202
quelle
1

Ein etwas komplizierteres lexsortBeispiel - absteigend in der 1. Spalte, sekundär aufsteigend in der 2 .. Die Tricks dabei lexsortsind, dass es nach Zeilen sortiert (daher die .T) und der letzten Priorität einräumt.

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
quelle
0

Hier ist eine andere Lösung, die alle Spalten berücksichtigt (kompaktere Art der Antwort von 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]])

Sortieren mit lexsort,

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

Ausgabe:

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
quelle
0

Verwenden Sie einfach sort, und verwenden Sie die Spaltennummer, nach der Sie sortieren möchten.

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
quelle
0

Es ist eine alte Frage, aber wenn Sie dies auf Arrays mit mehr als 2 Dimensionen verallgemeinern müssen, ist hier die Lösung, die leicht verallgemeinert werden kann:

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

Dies ist ein Overkill für zwei Dimensionen und a[a[:,1].argsort()]würde pro @ steves Antwort ausreichen, diese Antwort kann jedoch nicht auf höhere Dimensionen verallgemeinert werden. In dieser Frage finden Sie ein Beispiel für ein 3D-Array.

Ausgabe:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
Ehsan
quelle