Ordenar una lista de Python por dos campos [duplicado]

Resuelto half full asked hace 13 años • 10 respuestas

Tengo la siguiente lista creada a partir de un csv ordenado

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

De hecho, me gustaría ordenar la lista según dos criterios: primero por el valor del campo 1 y luego por el valor del campo 2. ¿Cómo hago esto?

half full avatar Mar 07 '11 02:03 half full
Aceptado

No es necesario importar nada cuando se utilizan funciones lambda.
Lo siguiente se clasifica listpor el primer elemento y luego por el segundo elemento. También puedes ordenar por un campo ascendente y otro descendente por ejemplo:

sorted_list = sorted(list, key=lambda x: (x[0], -x[1]))
jaap avatar Jun 14 '2013 13:06 jaap

como esto:

import operator
list1 = sorted(csv1, key=operator.itemgetter(1, 2))
mouad avatar Mar 06 '2011 19:03 mouad

Python tiene una clasificación estable, por lo que, siempre que el rendimiento no sea un problema, la forma más sencilla es ordenarlo por el campo 2 y luego ordenarlo nuevamente por el campo 1.

Eso le dará el resultado que desea, el único inconveniente es que si es una lista grande (o si desea ordenarla con frecuencia), llamar a ordenar dos veces podría ser una sobrecarga inaceptable.

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

Hacerlo de esta manera también facilita el manejo de la situación en la que desea que algunas de las columnas se ordenen de manera inversa, simplemente incluya el parámetro 'reverse=True' cuando sea necesario.

De lo contrario, puede pasar varios parámetros a itemgetter o crear manualmente una tupla. Probablemente será más rápido, pero tiene el problema de que no se generaliza bien si algunas de las columnas quieren ordenarse de forma inversa (las columnas numéricas aún se pueden invertir negándolas, pero eso impide que la ordenación sea estable).

Entonces, si no necesita ninguna columna ordenada de manera inversa, busque múltiples argumentos para itemgetter, si es posible, y las columnas no son numéricas o si desea mantener la clasificación estable, opte por múltiples clasificaciones consecutivas.

Editar: Para los comentaristas que tienen problemas para entender cómo esto responde a la pregunta original, aquí hay un ejemplo que muestra exactamente cómo la naturaleza estable de la clasificación garantiza que podamos realizar clasificaciones separadas en cada clave y terminar con datos ordenados según múltiples criterios:

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

Este es un ejemplo ejecutable, pero para evitar que las personas lo ejecuten, el resultado es:

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

Observe en particular cómo en el segundo paso el reverse=Trueparámetro mantiene los nombres en orden, mientras que simplemente ordenar y luego invertir la lista perdería el orden deseado para la tercera clave de clasificación.

Duncan avatar Mar 06 '2011 19:03 Duncan