Transponer / Descomprimir Función (inversa de zip)?


Tengo una lista de elemento 2 tuplas y me gustaría convertir a 2 listas donde la primera contiene el primer elemento en 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?

Author: martineau, 2008-08-21

11 answers

zip es su propia inversa! 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))

Except excepto que los argumentos se pasan a zip directamente (después de ser convertidos a una tupla), por lo que no hay necesidad de preocuparse por el número de argumentos que se vuelven demasiado grandes.

 620
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-13 00:09:24

También podrías 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 las comprensiones de la lista a menos que sea necesario.

(Por cierto, hace una 2-tupla (par) de listas, en lugar de una lista de tuplas, como 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 mastican la lista hasta que pides cada elemento, pero por otro lado, mantienen referencias a la lista original.

 26
Author: Anders Eurenius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-24 17:14:46

Si tiene listas que no tienen la misma longitud, es posible que no desee 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 diferentes listas de 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 map with no function para rellenar los resultados vacíos con None:

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

Zip() es ligeramente más rápido.

 19
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-02 12:14:17

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

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

Encuentro unzip más legible.

 12
Author: wassimans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-01 15:00:15
>>> 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.

 10
Author: Noyer282,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-05 11:08:28

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ónica de descomprimirlo y volver al original es esta en mi opinión:

x,y=zip(*XY)

Pero esto devuelve una tupla, por lo que si necesita una matriz puede usar:

xy=(list(x),list(y))
 1
Author: G M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-26 10:45:23

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

Aquí hay una función que realmente le dará el inverso 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
 1
Author: Waylon Flinn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-11 13:35:08

Ninguna de las respuestas anteriores eficientemente proporciona la salida requerida, que es una tupla de listas, en lugar de una lista de tuplas. Para el primero, puede 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, tendrá que pasar el resultado a una función como list o tuple para agotar el iterador. Para iteradores eficientes en memoria, puede omitir las llamadas externas list y tuple para las soluciones respectivas.

 0
Author: jpp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-23 17:36:23

Aunque 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 me parece 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 quieras hacer con el resultado, la elección de la colección puede marcar una gran diferencia. En mi caso de uso real, usando conjuntos y sin bucle interno, es notablemente más rápido que todos los demás enfoques.

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

 0
Author: Charlie Clark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-26 14:08:01

Otra forma de pensar en 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')
 -1
Author: JawSaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-25 03:50:15

Así es como puedes transponer una tupla de 2x4 a una tupla de 4x2.

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

Resultado

[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
 -1
Author: helcode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-25 18:50:16