Ordinamento di un elenco Python per due campi


173

Ho il seguente elenco creato da un CSV ordinato

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

Vorrei effettivamente ordinare l'elenco in base a due criteri: prima in base al valore nel campo 1 e poi in base al valore nel campo 2. Come posso fare?



Lasciamo questa domanda in piedi e limitiamo il suo campo di applicazione a "list-of-lists-of-length-two-builtin-types (es. String / int / float)" . Oppure consentiamo anche "elenco di oggetti definiti dall'utente" , come suggerisce anche il titolo, nel qual caso la risposta è "Definisci __lt__()metodo sulla tua classe o eredita da una classe che lo fa" ? Ciò renderebbe un canonico molto migliore.
smci,

Risposte:


158

come questo:

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

1
+1: più elegante del mio. Ho dimenticato che Itemgetter può prendere più indici.
dappawit,

7
operatorè un modulo che deve essere importato.
trapicki,

3
come procederò se voglio ordinare in ordine crescente su un elemento e decrescente su un altro, usando itemgetter ??.
ashish,

3
@ashish, vedi la mia risposta sotto con funzioni lambda questo è chiaro, ordina per "-x [1]" o anche "x [0] + x [1]" se lo desideri
jaap

che dire se si criteri in modalità inversa?
YaserKH,

328

Non è necessario importare nulla quando si utilizzano le funzioni lambda.
Il seguente ordina listper il primo elemento, quindi per il secondo elemento.

sorted(list, key=lambda x: (x[0], -x[1]))

12
Bello. Come hai notato in commento alla risposta principale sopra, questo è il modo migliore (solo?) Per fare più ordinamenti con diversi ordinamenti. Forse evidenziarlo. Inoltre, il testo non indica che hai ordinato in ordine decrescente sul secondo elemento.
PeterVermont,

2
@ user1700890 Supponevo che il campo fosse già stringa. Dovrebbe ordinare le stringhe in ordine alfabetico per impostazione predefinita. Dovresti pubblicare la tua domanda separatamente su SO se non è specificamente correlata alla risposta qui o alla domanda originale del PO.
bibbia,

5
cosa significa l' -in -x[1]in?
gennaio

7
@jan è l'ordinamento inverso
Jaap

3
Non funzionerà in un caso specifico. Neanche la soluzione accettata funzionerà. Ad esempio, le colonne da utilizzare come chiavi sono tutte stringhe che non possono essere convertite in numeri. In secondo luogo, si vuole ordinare in ordine crescente per una colonna e in ordine decrescente per un'altra colonna.
coder.in.me

20

Python ha un ordinamento stabile, quindi a condizione che le prestazioni non costituiscano un problema, il modo più semplice è quello di ordinarlo per campo 2 e quindi di ordinarlo nuovamente per campo 1.

Questo ti darà il risultato che desideri, l'unico problema è che se si tratta di un grande elenco (o se vuoi ordinarlo spesso) chiamare l'ordinamento due volte potrebbe essere un sovraccarico inaccettabile.

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

Farlo in questo modo semplifica anche la gestione della situazione in cui si desidera ordinare alcune colonne inverse, basta includere il parametro 'reverse = True' quando necessario.

Altrimenti puoi passare più parametri a itemgetter o creare manualmente una tupla. Probabilmente sarà più veloce, ma ha il problema che non si generalizza bene se alcune colonne vogliono essere ordinate al contrario (le colonne numeriche possono ancora essere invertite negandole ma ciò impedisce che l'ordinamento sia stabile).

Quindi se non hai bisogno di colonne ordinate al contrario, vai su più argomenti a itemgetter, se possibile, e le colonne non sono numeriche o vuoi mantenere l'ordinamento stabile per più ordinamenti consecutivi.

Modifica: per i commentatori che hanno problemi a capire come questo risponde alla domanda originale, ecco un esempio che mostra esattamente come la natura stabile dell'ordinamento ci assicura che possiamo fare ordinamenti separati su ogni chiave e finire con i dati ordinati su più criteri:

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

Questo è un esempio eseguibile, ma per salvare le persone che lo eseguono l'output è:

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

Si noti in particolare come nella seconda fase il reverse=Trueparametro mantiene in ordine i nomi, mentre semplicemente ordinando e invertendo l'elenco si perderebbe l'ordine desiderato per la terza chiave di ordinamento.


1
L'ordinamento stabile non significa che non dimenticherà quale era l'ordinamento precedente. Questa risposta è sbagliata
Mike Axiak,

7
L'ordinamento stabile significa che è possibile ordinare per colonne a, b, c semplicemente ordinando per colonna c quindi b quindi a. A meno che non ti interessi ampliare il tuo commento, penso che tu sia sbagliato.
Duncan,

7
Questa risposta è sicuramente corretta, anche se per elenchi più grandi è unideale: se l'elenco era già parzialmente ordinato, perderai la maggior parte dell'ottimizzazione dell'ordinamento di Python mescolando l'elenco molto di più. @ Mike, non sei corretto; Suggerisco di provare le risposte prima di dichiararle errate.
Glenn Maynard,

6
@MikeAxiak: docs.python.org/2/library/stdtypes.html#index-29 afferma nel commento 9: A partire da Python 2.3, il metodo sort () è garantito come stabile. Un ordinamento è stabile se garantisce di non cambiare l'ordine relativo degli elementi che si equivalgono - questo è utile per l'ordinamento in più passaggi (ad esempio, ordinamento per dipartimento, quindi per grado di stipendio).
trapicki,

Questo non è corretto perché non risponde alla domanda che ha posto. vuole un elenco ordinato per primo indice e nel caso in cui vi siano legami nel primo indice, vuole usare il secondo indice come criterio di ordinamento. Un ordinamento stabile garantisce solo che a parità di condizioni, l'ordine originale passato sarà l'ordine in cui gli articoli compaiono.
Jon

14
list1 = sorted(csv1, key=lambda x: (x[1], x[2]) )

4
Non credo che tuple()possa ricevere due argomenti (o meglio, tre, se conti con self)
Filipe Correia,

3
solo le tuple possono prendere un argomento
vero professionista

1
returndichiarazione dovrebbe essere return tuple((x[1], x[2]))o semplicemente return x[1], x[2]. Fai riferimento alla risposta @jaap qui sotto se stai cercando l'ordinamento in diverse direzioni
Jo Kachikaran,

... oppure tuple(x[1:3]), se si desidera utilizzare il costruttore tupla per qualche motivo anziché solo un elenco di visualizzazione delle tuple x[1], x[2]. Oppure keyfunc = operator.itemgetter(1, 2)non scrivere nemmeno una funzione da solo.
Abarnert

3
employees.sort(key = lambda x:x[1])
employees.sort(key = lambda x:x[0])

Possiamo anche usare .sort con lambda 2 volte perché l'ordinamento python è a posto e stabile. Questo per prima cosa ordina l'elenco in base al secondo elemento, x [1]. Quindi, ordinerà il primo elemento, x [0] (massima priorità).

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

Ciò equivale a fare quanto segue: employee.sort (key = lambda x: (x [0], x [1]))


1
no, questa regola di ordinamento deve avere la precedenza poi sul secondo.
CodeFarmer

1

In ordine crescente è possibile utilizzare:

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

oppure in ordine decrescente è possibile utilizzare:

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

0

L'ordinamento dell'elenco dei dadi usando di seguito ordinerà l'elenco in ordine decrescente sulla prima colonna come stipendio e seconda colonna come età

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

Output: [{'stipendio': 123, 'età': 25}, {'stipendio': 123, 'età': 23}]

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.