Transposer/Décompressez Fonction (l'inverse de zip)?

j'ai une liste de tuples à 2 éléments et j'aimerais les convertir en 2 listes où le premier contient le premier élément dans chaque tuple et la deuxième liste contient le deuxième élément.

par exemple:

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

y a-t-il une fonction intégrée qui fait cela?

391
demandé sur martineau 2008-08-21 08:29:07

11 réponses

zip est son propre inverse! À condition que vous utilisez le spécial * l'opérateur.

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

la façon dont cela fonctionne est en appelant zip avec les arguments:

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

... sauf que les arguments sont passés à zip directement (après avoir été converti en un tuple), donc il n'y a pas besoin de s'inquiéter du nombre d'arguments devenant trop grand.

620
répondu Patrick 2017-06-13 00:09:24

, Vous pouvez aussi le faire

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

Il devrait échelle de mieux. Surtout si Python fait du bien sur le fait de ne pas étendre les compréhensions de liste à moins que nécessaire.

(soit dit en passant, il fait un 2-tuple (paire) de listes, plutôt qu'une liste de tuples, comme zip fait.)

si les générateurs au lieu des listes actuelles sont ok, cela ferait cela:

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

les générateurs ne mâchez pas la liste jusqu'à ce que vous demandiez pour chaque élément, mais d'un autre côté, ils gardent des références à la liste originale.

26
répondu Anders Eurenius 2008-08-24 17:14:46

si vous avez des listes qui ne sont pas de la même longueur, vous pouvez ne pas vouloir utiliser zip que selon Patricks réponse. Cela fonctionne:

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

mais avec des listes de longueur différentes, zip tronque chaque élément à la longueur de la liste la plus courte:

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

vous pouvez utiliser map sans fonction pour remplir des résultats vides avec None:

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

zip () est légèrement plus rapide cependant.

19
répondu Chris 2011-01-02 12:14:17

j'aime utiliser zip(*iterable) (qui est le morceau de code que vous recherchez) dans mes programmes comme suit:

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

je trouve unzip plus lisible.

12
répondu wassimans 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])

donne un tuple de listes comme dans la question.

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

Déballe les deux listes.

10
répondu Noyer282 2016-03-05 11:08:28

C'est seulement une autre façon de le faire mais ça m'a beaucoup aidé alors je l'écris ici:

ayant cette structure de données:

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

résultant en:

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

la façon plus pythonique de le décompresser et de revenir à l'original est celle-ci à mon avis:

x,y=zip(*XY)

mais ce retour un tuple donc si vous avez besoin d'un tableau, vous pouvez utiliser:

xy=(list(x),list(y))
1
répondu G M 2016-01-26 10:45:23

Puisqu'il renvoie des tuples (et peut utiliser des tonnes de mémoire), le zip(*zipped) tour semble plus intelligent que utile, pour moi.

Voici une fonction qui vous donnera l'inverse 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
répondu Waylon Flinn 2018-06-11 13:35:08

aucune des réponses précédentes efficacement fournir la sortie requise, qui est un tuple de listes , plutôt qu'une liste de tuples . Pour le premier, vous pouvez utiliser tuple avec map . Voici la différence:

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

de plus, la plupart des solutions précédentes supposent Python 2.7, où zip renvoie une liste plutôt qu'un itérateur.

Pour Python 3.x, vous aurez besoin de passer le résultat à une fonction telle que list ou tuple pour épuiser l'itérateur. Pour les itérateurs économes en mémoire, vous pouvez omettre les appels externes list et tuple pour les solutions respectives.

0
répondu jpp 2018-08-23 17:36:23

bien que zip(*seq) soit très utile, il peut ne pas convenir pour de très longues séquences car il va créer un nuage de valeurs à passer. Par exemple, j'ai travaillé avec un système de coordonnées avec plus d'un million d'entrées et je le trouve nettement plus rapide pour créer les séquences directement.

Une approche générique serait quelque chose comme ceci:

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)

Mais, selon ce que vous voulez faire avec le résultat, le choix de collection peuvent faire une grande différence. Dans mon cas d'utilisation, à l'aide de jeux et pas de boucle interne, est sensiblement plus rapide que toutes les autres approches.

et, comme d'autres l'ont fait remarquer, si vous faites cela avec des ensembles de données, il pourrait être logique d'utiliser des collections Numpy ou Pandas à la place.

0
répondu Charlie Clark 2018-09-26 14:08:01

une autre façon de penser à unzip ou transpose est de convertir une liste de lignes en une liste de colonnes.

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
répondu JawSaw 2018-08-25 03:50:15

c'est comme ça qu'on peut transposer un tuple 2x4 en un tuple 4x2.

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

résultat

[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
-1
répondu helcode 2018-08-25 18:50:16