Ordenar una lista de tuplas con múltiples condiciones

Actualmente estoy tratando de ordenar la siguiente lista:

list_ = [(1, '0101'), (1, '1010'), (1, '101'), (2, '01'), (2, '010'), (2, '10')] 

Estos son los pasos que quiero seguir para ordenarlos:

  1. Ordena la lista por el valor del primer elemento de las tuplas
  2. A continuación, ordene la lista por la longitud del segundo elemento de las tuplas (no el valor, ¡la longitud!) DESPUÉS de que el paso 1 finalice.
  3. A continuación, ordene la lista por el valor del segundo elemento de las tuplas DESPUÉS de que el paso 1 y el paso 2 finalicen.

Mi bash:

 sorted_by_length = sorted(list_, key=len x:x[1]) 

Sin embargo, recibí un error de syntax con respecto a la x después de la key= len . ¿Cuál es la variable correcta que debería estar usando en este caso?

La lista correcta y ordenada debe ser:

 sorted_by_length = [(1, '101'), (1, '0101'), (1, '1010'), (2, '01'), (2, '10'), (2, '010')] 

Gracias por ayudar.

La función clave puede devolver una tupla.

 sorted_by_length = sorted(list_, key=lambda x: (x[0], len(x[1]), float(x[1]))) 

Esto funciona porque las tuplas se clasifican de forma lexicográfica: (el primer elemento de la tupla se usa para clasificar primero, luego el segundo elemento se usa para romper lazos, y luego el tercer elemento se usa para romper las ataduras restantes).

Consulte el excelente CÓMO Ordenar para obtener una explicación de este y otros problemas relacionados con la clasificación.


 In [1]: list_ = [(1, '0101'), (1, '1010'), (1, '101'), (2, '01'), (2, '010'), (2, '10')] In [2]: sorted_by_length = sorted(list_, key=lambda x: (x[0], len(x[1]), float(x[1]))) ...: In [3]: sorted_by_length Out[3]: [(1, '101'), (1, '0101'), (1, '1010'), (2, '01'), (2, '10'), (2, '010')] 

Si el segundo elemento de cada tupla es la representación de cadena de un int en binario, entonces use int(x, 2) lugar de float(x) en la clave de clasificación. Si se pretende que sean la representación decimal de un entero, entonces use int(x) .

Puedes ordenar usando la función clave que devuelve la colección como resultado

 list_.sort(key=lambda x: [x[0], len(x[1]), x[1]]) 

parámetro key para especificar una función que se llamará en cada elemento de la lista antes de hacer comparaciones.

Si usa la recostackción como resultado key , se ordenará utilizando primero comparando los primeros elementos si son iguales, luego se compararán los segundos y así sucesivamente …

PD: Según tengo entendido, no es necesario convertir el tercer elemento en un tipo numérico porque si el igual es el mismo, para los valores binarios, el ordenamiento lexicográfico y numérico dará el mismo resultado.

La solución correcta es usar una función key que devuelva una tupla, como se muestra en la respuesta de unutbu. Sin embargo hay otra forma de hacerlo. Se garantiza que la clasificación de Python sea estable, por lo que puede hacer varias clasificaciones por teclas diferentes y lograr la salida que desea. En particular:

 list_.sort(key=lambda x: float(x[1])) list_.sort(key=lambda x: len(x[1])) list_.sort(key=lambda x: x[0]) 

Demo con IPython:

 In [1]: list_ = [(1, '0101'), (1, '1010'), (1, '101'), (2, '01'), (2, '010'), (2, '10')] In [2]: list_.sort(key=lambda x: float(x[1])) ...: list_.sort(key=lambda x: len(x[1])) ...: list_.sort(key=lambda x: x[0]) ...: In [3]: list_ Out[3]: [(1, '101'), (1, '0101'), (1, '1010'), (2, '01'), (2, '10'), (2, '010')] 

Nota : esta solución se parece a los tres pasos que describió en su pregunta, ¡ pero los pasos se invierten! Ordenar por la clave primaria al final para obtener la salida correcta.

También tenga en cuenta que el algoritmo utilizado para la clasificación es adaptable. esto significa que cuando una secuencia ya está parcialmente ordenada, puede usar el orden parcial para ordenar de manera más eficiente (a menudo en tiempo lineal en lugar de nlog(n) ). Cuando ordena por varias claves, a menudo alcanza este orden parcial, por lo tanto, las múltiples llamadas a sort() no cuestan mucho. Sin embargo, depende mucho de las claves y los datos. A veces es más eficiente que usar tuplas como teclas, a veces es bastante más lento.


Un ejemplo de tiempo. Tenga en cuenta que las dos soluciones toman principalmente el mismo tiempo.

 In [9]: list_ Out[9]: [(1, '0101'), (1, '1010'), (1, '101'), (2, '01'), (2, '010'), (2, '10')] In [10]: list_ *= 1000 # better to avoid too small benchmarks. In [11]: %%timeit ...: a = sorted(list_, key=lambda x: (x[0], len(x[1]), float(x[1]))) ...: 100 loops, best of 3: 6.04 ms per loop In [12]: %%timeit ...: a = sorted(list_, key=lambda x: float(x[1])) ...: a.sort(key=lambda x: len(x[1])) ...: a.sort(key=lambda x: x[0]) ...: 100 loops, best of 3: 5.72 ms per loop In [13]: import random ...: data = [(random.randint(1, 1000), bin(random.randint(1, 100))[2:]) for _ in range(10000)] ...: In [14]: %%timeit ...: a = sorted(data, key=lambda x: (x[0], len(x[1]), float(x[1]))) ...: 100 loops, best of 3: 15.2 ms per loop In [15]: %%timeit ...: a = sorted(data, key=lambda x: float(x[1])) ...: a.sort(key=lambda x: len(x[1])) ...: a.sort(key=lambda x: x[0]) ...: 100 loops, best of 3: 15.1 ms per loop