2008-08-21 11 views
342

Tengo una lista de tuplas de 2 elementos y me gustaría convertirlas a 2 listas donde la primera contiene el primer elemento en cada tupla y la segunda lista contiene el segundo elemento.Función de transposición/descompresión (inversa de zip)?

Por ejemplo:

original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] 
# and I want to become... 
result = (['a', 'b', 'c', 'd'], [1, 2, 3, 4]) 

¿Existe una función incorporada que hace eso?

+3

Grandes respuestas a continuación, pero también vea [transposición de numpy] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html) – opyate

+1

Vea esta buena respuesta para hacer lo mismo con generadores en lugar de list: [how-to-unzip-an-iterator] (http://stackoverflow.com/questions/30805000/how-to-unzip-an-iterator) – YvesgereY

Respuesta

546

zip es su propia inversa! Siempre que use el operador especial *.

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]) 
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)] 

La forma en que esto funciona es llamando zip con los argumentos:

zip(('a', 1), ('b', 2), ('c', 3), ('d', 4)) 

... excepto los argumentos se pasan a zip directamente (después de ser convertido a una tupla), así que no hay necesidad de preocuparse sobre la cantidad de argumentos que se hacen demasiado grandes

+6

Oh, si fuera tan simple. Descomprimir 'zip ([], [])' de esta manera no se obtiene '[], []'. Te consigue '[]'. Si solo ... – user2357112

+0

@ user2357112 le da 'zip (* zip ([list1], [list2]))' le da '([list1, list2])'. – cdhagmann

+0

@cdhagmann: 'zip ([list1], [list2])' nunca es lo que quieres, sin embargo. Eso solo te da '[(list1, list2)]'. – user2357112

22

También puede hacer

result = ([ a for a,b in original ], [ b for a,b in original ]) 

Se debe escala mejor. Especialmente si Python hace bien en no expandir la lista de comprensiones a menos que sea necesario.

(Por cierto, se hace una 2-tupla (par) de las listas, en lugar de una lista de tuplas, como zip hace.)

Si generadores en lugar de las listas reales están bien, esto haría que:

result = ((a for a,b in original), (b for a,b in original)) 

Los generadores no muerden la lista hasta que solicite cada elemento, pero, por otro lado, sí conservan referencias a la lista original.

+6

"Especialmente si Python cumple con no expandir las listas de comprensión a menos que sea necesario". mmm ... normalmente, las listas de comprensiones se expanden de inmediato, ¿o me sale algo mal? – glglgl

+0

@glglgl: No, probablemente tengas razón. Esperaba que alguna versión futura comenzara a hacer lo correcto. (No es imposible cambiar, la semántica del efecto secundario que necesita cambios probablemente ya esté desaconsejada). –

+8

Lo que espera obtener es una expresión del generador, que ya existe. – glglgl

19

Si tiene listas que no tienen la misma longitud, es posible que no desee utilizar el código postal de acuerdo con la respuesta de Patricks. Esto funciona:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]) 
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)] 

pero con diferentes listas de longitud, código postal trunca cada elemento a la longitud de la lista corta:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e',)]) 
[('a', 'b', 'c', 'd', 'e')] 

puede utilizar Map sin función para llenar resultados vacíos con Ninguno:

>>> map(None, *[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e',)]) 
[('a', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, None)] 

zip() es un poco más rápido.

+3

interesante, ¿puedes explicar cómo funciona 'map'? –

+4

También podría usar 'izip_longest' – Marcin

+2

Conocido como' zip_longest' para usuarios de python3. – zezollo

11

me gusta usar zip(*iterable) (que es el trozo de código que está buscando) en mis programas como tan:

def unzip(iterable): 
    return zip(*iterable) 

me parece unzip más legible.

2

Es sólo otra manera de hacerlo, pero me ayudó mucho, así que escribo aquí:

Teniendo esta estructura de datos:

X=[1,2,3,4] 
Y=['a','b','c','d'] 
XY=zip(X,Y) 

El resultado es:

In: XY 
Out: [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')] 

El más manera pitónica para descomprimirlo y volver al original es este en mi opinión:

x,y=zip(*XY) 

Pero esto devuelve una tupla así que si necesitas una matriz que puede utilizar:

xy=(list(x),list(y)) 
7
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] 
>>> tuple([list(tup) for tup in zip(*original)]) 
(['a', 'b', 'c', 'd'], [1, 2, 3, 4]) 

Da una tupla de listas como en la pregunta.

list1, list2 = [list(tup) for tup in zip(*original)] 

Desempaqueta las dos listas.