array python list matrix unzip transpose

python - array - Función Transpose/Unzip(inverso de zip)?



transpose list python (11)

Tengo una lista de tuplas de 2 elementos y me gustaría convertirlas en 2 listas donde la primera contiene el primer elemento de cada tupla y la segunda lista contiene el segundo elemento.

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

¿Hay una función incorporada que hace eso?


Así es como puede transponer una tupla de 2x4 en una tupla de 4x2.

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

resultado

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


Dado que devuelve tuplas (y puede usar toneladas de memoria), el truco zip(*zipped) parece más inteligente que útil, para mí.

Aquí hay una función que realmente te dará la inversa de zip.

def unzip(zipped): """Inverse of built-in zip function. Args: zipped: a list of tuples Returns: a tuple of lists Example: a = [1, 2, 3] b = [4, 5, 6] zipped = list(zip(a, b)) assert zipped == [(1, 4), (2, 5), (3, 6)] unzipped = unzip(zipped) assert unzipped == ([1, 2, 3], [4, 5, 6]) """ unzipped = () if len(zipped) == 0: return unzipped dim = len(zipped[0]) for i in range(dim): unzipped = unzipped + ([tup[i] for tup in zipped], ) return unzipped


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

Teniendo esta estructura de datos:

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

Resultando en:

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

La forma más pitón de descomprimirlo y volver al original es esta en mi opinión:

x,y=zip(*XY)

Pero esto devuelve una tupla, así que si necesitas una matriz puedes usar:

xy=(list(x),list(y))


Me gusta usar zip(*iterable) (que es el trozo de código que estás buscando) en mis programas así:

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

Encuentro unzip más legible.


Ninguna de las respuestas anteriores proporciona de manera eficiente el resultado requerido, que es una tupla de listas , en lugar de una lista de tuplas . Para el primero, puedes usar tuple con map . Aquí está la diferencia:

res1 = list(zip(*original)) # [(''a'', ''b'', ''c'', ''d''), (1, 2, 3, 4)] res2 = tuple(map(list, zip(*original))) # ([''a'', ''b'', ''c'', ''d''], [1, 2, 3, 4])

Además, la mayoría de las soluciones anteriores asumen Python 2.7, donde zip devuelve una lista en lugar de un iterador.

Para Python 3.x, deberá pasar el resultado a una función como list o tuple para agotar el iterador. Para los iteradores que utilizan la memoria, puede omitir la list externa y las llamadas de tuple para las soluciones respectivas.


Otra forma de pensar acerca de unzip o transpose es convertir una lista de filas en una lista de columnas.

pitchers = [(''Nolan'', ''Ryan''), (''Roger'', ''Clements''), (''Schilling'',''Curt'')] first_names, last_names = zip(*pitchers) In [45]: first_names Out[45]: (''Nolan'', ''Roger'', ''Schilling'') In [46]: last_names Out[46]: (''Ryan'', ''Clements'', ''Curt'')


Si bien zip(*seq) es muy útil, puede no ser adecuado para secuencias muy largas, ya que creará una tupla de valores que se pasarán. Por ejemplo, he estado trabajando con un sistema de coordenadas con más de un millón de entradas y Significativamente más rápido para crear las secuencias directamente.

Un enfoque genérico sería algo como esto:

from collections import deque seq = ((a1, b1, …), (a2, b2, …), …) width = len(seq[0]) output = [deque(len(seq))] * width # preallocate memory for element in seq: for s, item in zip(output, element): s.append(item)

Pero, dependiendo de lo que quiera hacer con el resultado, la elección de la colección puede hacer una gran diferencia. En mi caso de uso real, usar conjuntos y ningún bucle interno, es notablemente más rápido que todos los otros enfoques.

Y, como han señalado otros, si está haciendo esto con conjuntos de datos, podría tener sentido usar las colecciones Numpy o Pandas en su lugar.


Si tiene listas que no tienen la misma longitud, es posible que no quiera usar zip según 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 listas de diferente longitud, zip trunca cada elemento a la longitud de la lista más corta:

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

Puede usar el mapa sin función para llenar los 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)]

Sin embargo, zip () es ligeramente más rápido.


Tambien podrias hacer

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

Debería escalar mejor. Especialmente si Python hace bien en no expandir la lista de comprensión a menos que sea necesario.

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

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

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

Los generadores no revisan la lista hasta que solicitan cada elemento, pero, por otro lado, mantienen las referencias a la lista original.


zip es su propio inverso! Siempre que utilice 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 a zip con los argumentos:

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

... excepto que los argumentos se pasan directamente a zip (después de convertirlos en una tupla), por lo que no hay que preocuparse por la cantidad de argumentos que son demasiado grandes.


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