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

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.


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.


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.


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

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 ...


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

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.


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

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

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)

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]]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.