Alma perdida
Supongamos que tengo:
list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
Vocación list1.sort()
lo ordenará, resultando en [1, 1, 2, 3, 4]
. Sin embargo, ¿puedo obtener list2
ser reorganizado en sincronía con eso, para obtener un resultado como este?
list1 = [1, 1, 2, 3, 4]
list2 = ['one', 'one2', 'two', 'three', 'four']
A veces, las personas expresan el problema de manera diferente: dadas dos listas, les gustaría usar una para determinar el orden de clasificación para la otra, es decir, ordenar list2
en el orden descrito por los valores correspondientes en list1
. El truco es que esto es equivalente a ordenar los valores “clave” (list1
), y luego reorganizar list2
del mismo modo. En otras palabras, exactamente lo que se describe aquí. Sin embargo, algunas respuestas para la otra pregunta descartan las “claves ordenadas”.
enviarle
Un enfoque clásico para este problema es usar el modismo “decorar, ordenar, desdecorar”, que es especialmente simple usando la función integrada de Python. zip
función:
>>> list1 = [3,2,4,1, 1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 1, 2, 3, 4)
>>> list2
('one', 'one2', 'two', 'three', 'four')
Estas, por supuesto, ya no son listas, pero eso se soluciona fácilmente, si importa:
>>> list1, list2 = (list
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']
Vale la pena señalar que lo anterior puede sacrificar la velocidad por la brevedad; la versión in situ, que ocupa 3 líneas, es un poco más rápida en mi máquina para listas pequeñas:
>>> %timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 3.3 us per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best of 3: 2.84 us per loop
Por otro lado, para listas más grandes, la versión de una línea podría ser más rápida:
>>> %timeit zip(*sorted(zip(list1, list2)))
100 loops, best of 3: 8.09 ms per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100 loops, best of 3: 8.51 ms per loop
Como señala Quantum7, la sugerencia de JSF es un poco más rápida aún, pero probablemente solo será un poco más rápida, porque Python usa el mismo idioma DSU internamente para todas las clasificaciones basadas en claves. Simplemente está sucediendo un poco más cerca del metal desnudo. (Esto muestra lo bien optimizado que está el zip
las rutinas son!)
Pienso que el zip
El enfoque basado en es más flexible y es un poco más legible, así que lo prefiero.
Tenga en cuenta que cuando los elementos de list1
son iguales, este enfoque terminará comparando elementos de list2
. Si elementos de list2
no admiten la comparación, o no producen un valor booleano cuando se comparan (por ejemplo, si list2
es una lista de arreglos NumPy), esto fallará, y si los elementos de list2
son muy caros de comparar, sería mejor evitar la comparación de todos modos.
En ese caso, puede ordenar los índices como se sugiere en la respuesta de jfs, o puede asignarle a la ordenación una función clave que evite comparar elementos de list2
:
result1, result2 = zip(*sorted(zip(list1, list2), key=lambda x: x[0]))
Asimismo, el uso de zip(*...)
ya que una transposición falla cuando la entrada está vacía. Si sus entradas pueden estar vacías, tendrá que manejar ese caso por separado.
-
¿Qué representa el asterisco en la tercera línea?
– Jeffrey
19 de marzo de 2012 a las 5:25
-
Para profundizar en lo anterior, el
*
el operador hace desempaque de argumentos,– senderle
19 de marzo de 2012 a las 15:46
-
El paradigma ordenado de índice/mapa sugerido por JF Sebastian es aproximadamente un 10% más rápido que cualquiera de las soluciones zip para mí (usando listas de 10000 enteros aleatorios): %timeit index = range(len(l1)); index.sort(clave=l1.__getitem__); mapa(l1.__getitem__, índice); map(l2.__getitem__, index) 100 bucles, lo mejor de 3: 8,04 ms por bucle (vs 9,17 ms, 9,07 ms para los timits de senderle)
– Quantum7
12/08/2013 a las 21:35
-
El primer y segundo zip en list1, list2 = zip(*sorted(zip(list1, list2))) hacen cosas tan diferentes. El * hace toda la diferencia.
– flautista de Hamelín
13 de enero de 2018 a las 9:48
-
@ashu, en cierto sentido, ¡sí! Pero en otro sentido, apenas son diferentes en absoluto.
zip(*x)
tiene la propiedad interesante de que es su propio inverso:l = [(1, 2), (3, 4)]; list(zip(*zip(*l))) == l
devolucionesTrue
. Es efectivamente un operador de transposición.zip()
por sí solo es el mismo operador, pero asume que ha desempaquetado la secuencia de entrada manualmente.– senderle
16 de enero de 2018 a las 14:43
jfs
Puede ordenar índices usando valores como claves:
indexes = range(len(list1))
indexes.sort(key=list1.__getitem__)
Para obtener listas ordenadas dados índices ordenados:
sorted_list1 = map(list1.__getitem__, indexes)
sorted_list2 = map(list2.__getitem__, indexes)
en tu caso no debiste list1
, list2
sino más bien una sola lista de pares:
data = [(3, 'three'), (2, 'two'), (4, 'four'), (1, 'one'), (1, 'one2')]
Es fácil de crear; es fácil de ordenar en Python:
data.sort() # sort using a pair as a key
Ordenar solo por el primer valor:
data.sort(key=lambda pair: pair[0])
-
Lo bueno de esto es que puedo mantener los índices y ordenar otras cosas más tarde, en caso de que list1 sea una coordenada importante que afecte a varias otras matrices.
– EL_DON
5 de marzo de 2018 a las 18:35
-
índices = lista (rango (len (lista1))) para python 3
– DonQuiKong
2 de noviembre de 2018 a las 9:38
-
@DonQuiKong también necesitas
list()
alrededormap()
si desea utilizar este código en Python 3.– jfs
2 de noviembre de 2018 a las 16:51
-
O, en lugar de
sorted_list1 = list(map(list1.__getitem__, indexes))
uno podría hacersorted_list1 = [list1[i] for i in indexes]
.– Nathan
21 de junio de 2020 a las 3:57
Daniel Thaagaard Andreasen
He usado la respuesta dada por senderle durante mucho tiempo hasta que descubrí np.argsort
. Así es como funciona.
# idx works on np.array and not lists.
list1 = np.array([3,2,4,1])
list2 = np.array(["three","two","four","one"])
idx = np.argsort(list1)
list1 = np.array(list1)[idx]
list2 = np.array(list2)[idx]
Esta solución me parece más intuitiva y funciona muy bien. El rendimiento:
def sorting(l1, l2):
# l1 and l2 has to be numpy arrays
idx = np.argsort(l1)
return l1[idx], l2[idx]
# list1 and list2 are np.arrays here...
%timeit sorting(list1, list2)
100000 loops, best of 3: 3.53 us per loop
# This works best when the lists are NOT np.array
%timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 2.41 us per loop
# 0.01us better for np.array (I think this is negligible)
%timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best for 3 loops: 1.96 us per loop
A pesar de np.argsort
no es el más rápido, me resulta más fácil de usar.
-
Recibo un error al ejecutar tu ejemplo:
TypeError: only integer arrays with one element can be converted to an index
(Python 2.7.6, numpy 1.8.2). Para solucionarlo, list1 y list2 deben declararse como matrices numpy.– Ben B
7 julio 2015 a las 0:53
-
Gracias. ¿No es esto lo que escribo en el comentario en la función? De todos modos, creo que es una tontería que
np.argsort
no intente convertir a unnp.array
internamente.–Daniel Thaagaard Andreasen
7 julio 2015 a las 12:37
-
Me refería al primer fragmento de código ya que no se ejecuta como está escrito 🙂
– Ben B
7 julio 2015 a las 20:50
-
Lo corregí convirtiendo las listas cuando se asignan a matrices numpy. Gracias por el comentario 🙂
–Daniel Thaagaard Andreasen
8 de julio de 2015 a las 14:47
-
Ahora se convierten en matrices Numpy dos veces;)
– Ben B
08/07/2015 a las 19:32
Carlos Knechtel
Esto se puede hacer usando lo que los programadores de Perl llaman el Transformada Schwartziana, también conocido como el idioma decorar-clasificar-desdecorar. La ordenación integrada de Python es estable, por lo que los dos 1
s no causan un problema.
>>> l1 = [3, 2, 4, 1, 1]
>>> l2 = ['three', 'two', 'four', 'one', 'second one']
>>> zip(*sorted(zip(l1, l2)))
[(1, 1, 2, 3, 4), ('one', 'second one', 'two', 'three', 'four')]
Puedes usar el zip()
y sort()
funciones para lograr esto:
Python 2.6.5 (r265:79063, Jun 12 2010, 17:07:01)
[GCC 4.3.4 20090804 (release) 1] on cygwin
>>> list1 = [3,2,4,1,1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> zipped = zip(list1, list2)
>>> zipped.sort()
>>> slist1 = [i for (i, s) in zipped]
>>> slist1
[1, 1, 2, 3, 4]
>>> slist2 = [s for (i, s) in zipped]
>>> slist2
['one', 'one2', 'two', 'three', 'four']
Espero que esto ayude
-
¿Alguien más recibe el error “AttributeError: el objeto ‘zip’ no tiene atributo ‘sort'”? Me pregunto si esta respuesta funciona para versiones anteriores de Python pero no para las actuales.
– No contradicción
25 oct 2021 a las 18:32
Una forma es rastrear a dónde va cada índice ordenando la identidad [0,1,2,..n]
Esto funciona para cualquier número de listas.
Luego mueva cada elemento a su posición. Usar empalmes es lo mejor.
list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
index = list(range(len(list1)))
print(index)
'[0, 1, 2, 3, 4]'
index.sort(key = list1.__getitem__)
print(index)
'[3, 4, 1, 0, 2]'
list1[:] = [list1[i] for i in index]
list2[:] = [list2[i] for i in index]
print(list1)
print(list2)
'[1, 1, 2, 3, 4]'
"['one', 'one2', 'two', 'three', 'four']"
Tenga en cuenta que podríamos haber iterado las listas sin siquiera ordenarlas:
list1_iter = (list1[i] for i in index)
-
¿Alguien más recibe el error “AttributeError: el objeto ‘zip’ no tiene atributo ‘sort'”? Me pregunto si esta respuesta funciona para versiones anteriores de Python pero no para las actuales.
– No contradicción
25 oct 2021 a las 18:32
Artsiom Rudzenka
Qué pasa:
list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
sortedRes = sorted(zip(list1, list2), key=lambda x: x[0]) # use 0 or 1 depending on what you want to sort
>>> [(1, 'one'), (1, 'one2'), (2, 'two'), (3, 'three'), (4, 'four')]
Debo señalar que sus variables en list2 no apuntan a los enteros en list1. Por ejemplo, si cambia un valor como list1[0]=9 y mira lista2, lista2[0] seguirá siendo 3. Con números enteros en python, no usa la referencia/puntero, copia el valor. Hubiera sido mejor ir lista2 = lista1[:]
– Rob oxidado
19 de marzo de 2012 a las 3:10