Sortieren einer Python-Liste nach zwei Feldern

172

Ich habe die folgende Liste aus einer sortierten CSV erstellt

list1 = sorted(csv1, key=operator.itemgetter(1))

Ich möchte die Liste tatsächlich nach zwei Kriterien sortieren: zuerst nach dem Wert in Feld 1 und dann nach dem Wert in Feld 2. Wie mache ich das?

halb voll
quelle
3
Mögliches Duplikat von Liste nach mehreren Attributen sortieren?
Chris_Rands
Lassen wir diese Frage stehen und beschränken ihren Umfang nur auf "Liste von Listen mit Länge von zwei eingebauten Typen (z. B. string / int / float)" . Oder erlauben wir auch "Liste der benutzerdefinierten Objekte" , wie der Titel andeutet, ist ebenfalls zulässig. In diesem Fall lautet die Antwort " __lt__()Methode für Ihre Klasse definieren oder von einer Klasse erben, die dies tut" ? Das würde es zu einem weitaus besseren Kanon machen.
smci

Antworten:

157

so was:

import operator
list1 = sorted(csv1, key=operator.itemgetter(1, 2))
Mouad
quelle
1
+1: Eleganter als meine. Ich habe vergessen, dass Itemgetter mehrere Indizes annehmen kann.
Dappawit
7
operatorist ein Modul, das importiert werden muss.
Trapicki
3
Wie gehe ich vor, wenn ich mit itemgetter auf einem Element aufsteigend und auf einem anderen absteigend sortieren möchte?
Ashish
3
@ashish, siehe meine Antwort unten mit Lambda-Funktionen dies ist klar, sortieren Sie nach "-x [1]" oder sogar "x [0] + x [1]", wenn Sie möchten
jaap
Was ist, wenn ein Kriterium im umgekehrten Modus?
YaserKH
328

Bei Verwendung von Lambda-Funktionen muss nichts importiert werden.
Das Folgende wird listnach dem ersten Element und dann nach dem zweiten Element sortiert .

sorted(list, key=lambda x: (x[0], -x[1]))
jaap
quelle
12
Nett. Wie Sie im Kommentar zur Hauptantwort oben bemerkt haben, ist dies die beste (einzige?) Möglichkeit, mehrere Sortierungen mit unterschiedlichen Sortierreihenfolgen durchzuführen. Vielleicht das hervorheben. Außerdem zeigt Ihr Text nicht an, dass Sie absteigend nach dem zweiten Element sortiert haben.
PeterVermont
2
@ user1700890 Ich habe angenommen, dass das Feld bereits eine Zeichenfolge ist. Die Zeichenfolgen sollten standardmäßig in alphabetischer Reihenfolge sortiert werden. Sie sollten Ihre eigene Frage separat auf SO posten, wenn sie nicht speziell mit der Antwort hier oder der ursprünglichen Frage des OP zusammenhängt.
Bibel
5
Wofür steht das -in -x[1]?
Januar
7
@jan es ist umgekehrte Sortierung
jaap
3
Funktioniert in einem bestimmten Fall nicht. Die akzeptierte Lösung funktioniert auch nicht. Beispielsweise sind die als Schlüssel zu verwendenden Spalten alle Zeichenfolgen, die nicht in Zahlen konvertiert werden können. Zweitens möchte man in aufsteigender Reihenfolge nach einer Spalte und absteigender Reihenfolge nach einer anderen Spalte sortieren.
coder.in.me
20

Python hat eine stabile Sortierung. Sofern die Leistung kein Problem darstellt, ist es am einfachsten, sie nach Feld 2 zu sortieren und dann erneut nach Feld 1 zu sortieren.

Das gibt Ihnen das gewünschte Ergebnis. Der einzige Haken ist, dass das zweimalige Aufrufen von sort einen inakzeptablen Aufwand bedeuten kann, wenn es sich um eine große Liste handelt (oder wenn Sie sie häufig sortieren möchten).

list1 = sorted(csv1, key=operator.itemgetter(2))
list1 = sorted(list1, key=operator.itemgetter(1))

Auf diese Weise können Sie auch problemlos die Situation bewältigen, in der einige Spalten umgekehrt sortiert werden sollen. Fügen Sie bei Bedarf einfach den Parameter 'reverse = True' hinzu.

Andernfalls können Sie mehrere Parameter an itemgetter übergeben oder manuell ein Tupel erstellen. Das wird wahrscheinlich schneller sein, hat aber das Problem, dass es nicht gut verallgemeinert wird, wenn einige der Spalten umgekehrt sortiert werden sollen (numerische Spalten können immer noch umgekehrt werden, indem sie negiert werden, aber das verhindert, dass die Sortierung stabil ist).

Wenn Sie also keine Spalten in umgekehrter Reihenfolge benötigen, wählen Sie Itemgetter, wenn Sie möchten, mehrere Argumente, und die Spalten sind nicht numerisch, oder Sie möchten die Sortierung für mehrere aufeinanderfolgende Sortierungen stabil halten.

Bearbeiten: Für die Kommentatoren, die Probleme haben zu verstehen, wie dies die ursprüngliche Frage beantwortet, ist hier ein Beispiel, das genau zeigt, wie die stabile Art der Sortierung sicherstellt, dass wir für jeden Schlüssel separate Sortierungen durchführen und am Ende Daten nach mehreren Kriterien sortieren können:

DATA = [
    ('Jones', 'Jane', 58),
    ('Smith', 'Anne', 30),
    ('Jones', 'Fred', 30),
    ('Smith', 'John', 60),
    ('Smith', 'Fred', 30),
    ('Jones', 'Anne', 30),
    ('Smith', 'Jane', 58),
    ('Smith', 'Twin2', 3),
    ('Jones', 'John', 60),
    ('Smith', 'Twin1', 3),
    ('Jones', 'Twin1', 3),
    ('Jones', 'Twin2', 3)
]

# Sort by Surname, Age DESCENDING, Firstname
print("Initial data in random order")
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
First we sort by first name, after this pass all
Twin1 come before Twin2 and Anne comes before Fred''')
DATA.sort(key=lambda row: row[1])

for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
Second pass: sort by age in descending order.
Note that after this pass rows are sorted by age but
Twin1/Twin2 and Anne/Fred pairs are still in correct
firstname order.''')
DATA.sort(key=lambda row: row[2], reverse=True)
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
Final pass sorts the Jones from the Smiths.
Within each family members are sorted by age but equal
age members are sorted by first name.
''')
DATA.sort(key=lambda row: row[0])
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

Dies ist ein ausführbares Beispiel, aber um die Benutzer zu retten, die es ausführen, lautet die Ausgabe wie folgt:

Initial data in random order
Jones      Jane       58
Smith      Anne       30
Jones      Fred       30
Smith      John       60
Smith      Fred       30
Jones      Anne       30
Smith      Jane       58
Smith      Twin2      3
Jones      John       60
Smith      Twin1      3
Jones      Twin1      3
Jones      Twin2      3

First we sort by first name, after this pass all
Twin1 come before Twin2 and Anne comes before Fred
Smith      Anne       30
Jones      Anne       30
Jones      Fred       30
Smith      Fred       30
Jones      Jane       58
Smith      Jane       58
Smith      John       60
Jones      John       60
Smith      Twin1      3
Jones      Twin1      3
Smith      Twin2      3
Jones      Twin2      3

Second pass: sort by age in descending order.
Note that after this pass rows are sorted by age but
Twin1/Twin2 and Anne/Fred pairs are still in correct
firstname order.
Smith      John       60
Jones      John       60
Jones      Jane       58
Smith      Jane       58
Smith      Anne       30
Jones      Anne       30
Jones      Fred       30
Smith      Fred       30
Smith      Twin1      3
Jones      Twin1      3
Smith      Twin2      3
Jones      Twin2      3

Final pass sorts the Jones from the Smiths.
Within each family members are sorted by age but equal
age members are sorted by first name.

Jones      John       60
Jones      Jane       58
Jones      Anne       30
Jones      Fred       30
Jones      Twin1      3
Jones      Twin2      3
Smith      John       60
Smith      Jane       58
Smith      Anne       30
Smith      Fred       30
Smith      Twin1      3
Smith      Twin2      3

Beachten Sie insbesondere, wie im zweiten Schritt der reverse=TrueParameter die Vornamen in der richtigen Reihenfolge hält, während durch einfaches Sortieren und Umkehren der Liste die gewünschte Reihenfolge für den dritten Sortierschlüssel verloren geht.

Duncan
quelle
1
Stabile Sortierung bedeutet nicht, dass nicht vergessen wird, was Ihre vorherige Sortierung war. Diese Antwort ist falsch.
Mike Axiak
7
Stabile Sortierung bedeutet, dass Sie nach Spalten a, b, c sortieren können, indem Sie einfach nach Spalte c, dann b und dann a sortieren. Ich denke, Sie sind es, die sich irren, es sei denn, Sie möchten Ihren Kommentar erweitern.
Duncan
7
Diese Antwort ist definitiv richtig, obwohl sie für größere Listen unideal ist: Wenn die Liste bereits teilweise sortiert war, verlieren Sie den größten Teil der Optimierung der Python-Sortierung, indem Sie die Liste viel mehr mischen. @ Mike, du bist falsch; Ich schlage vor, die Antworten tatsächlich zu testen, bevor sie für falsch erklärt werden.
Glenn Maynard
6
@MikeAxiak: docs.python.org/2/library/stdtypes.html#index-29 besagt in Kommentar 9: Ab Python 2.3 ist die sort () -Methode garantiert stabil. Eine Sortierung ist stabil, wenn sie garantiert, dass die relative Reihenfolge der Elemente, die gleich sind, nicht geändert wird. Dies ist hilfreich, um in mehreren Durchgängen zu sortieren (z. B. nach Abteilung und dann nach Gehaltsstufe sortieren).
Trapicki
Dies ist nicht korrekt, da dies die von ihm gestellte Frage nicht beantwortet. er möchte eine Liste, die nach dem ersten Index sortiert ist, und falls im ersten Index Bindungen bestehen, möchte er den zweiten Index als Sortierkriterium verwenden. Eine stabile Sortierung garantiert nur, dass alle Dinge gleich sind. Die ursprünglich übergebene Bestellung entspricht der Reihenfolge, in der die Artikel angezeigt werden.
Jon
14
list1 = sorted(csv1, key=lambda x: (x[1], x[2]) )
dappawit
quelle
4
Ich glaube nicht, dass ich tuple()zwei Argumente erhalten kann (oder besser gesagt drei, wenn Sie damit rechnen self)
Filipe Correia
3
Tupel nimmt nur kann ein Argument nehmen
therealprashant
1
returnAussage sollte sein return tuple((x[1], x[2]))oder einfach return x[1], x[2]. Siehe @jaap Antwort unten, wenn Sie in verschiedene Richtungen sortieren
möchten
… Oder tuple(x[1:3])wenn Sie den Tupelkonstruktor aus irgendeinem Grund anstelle einer Tupelanzeigeliste verwenden möchten x[1], x[2]. Oder keyfunc = operator.itemgetter(1, 2)und schreiben Sie nicht einmal selbst eine Funktion.
abarnert
3
employees.sort(key = lambda x:x[1])
employees.sort(key = lambda x:x[0])

Wir können .sort auch zweimal mit Lambda verwenden, da die Python-Sortierung vorhanden und stabil ist. Dadurch wird die Liste zunächst nach dem zweiten Element x [1] sortiert. Dann wird das erste Element x [0] (höchste Priorität) sortiert.

employees[0] = Employee's Name
employees[1] = Employee's Salary

Dies entspricht dem folgenden Vorgang: employee.sort (key = lambda x: (x [0], x [1]))

Deepak Yadav
quelle
1
Nein, diese Sortierregel muss Vorrang vor der zweiten haben.
CodeFarmer
1

In aufsteigender Reihenfolge können Sie verwenden:

sorted_data= sorted(non_sorted_data, key=lambda k: (k[1],k[0]))

oder in absteigender Reihenfolge können Sie verwenden:

sorted_data= sorted(non_sorted_data, key=lambda k: (k[1],k[0]),reverse=True)
Majid Arasteh
quelle
0

Durch Sortieren der Liste der Diktate unter Verwendung der folgenden Liste wird die Liste in absteigender Reihenfolge in der ersten Spalte als Gehalt und in der zweiten Spalte als Alter sortiert

d=[{'salary':123,'age':23},{'salary':123,'age':25}]
d=sorted(d, key=lambda i: (i['salary'], i['age']),reverse=True)

Ausgabe: [{'Gehalt': 123, 'Alter': 25}, {'Gehalt': 123, 'Alter': 23}]

Saurabh
quelle