614 Stimmen

Transponieren/Entpacken-Funktion (Inverse von zip)?

Ich habe eine Liste von 2-Element-Tupeln und möchte sie in 2 Listen konvertieren, wobei die erste Liste das erste Element in jedem Tupel und die zweite Liste das zweite Element enthält.

Zum Beispiel:

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

Gibt es eine eingebaute Funktion, die das tut?

8 Stimmen

Tolle Antworten unten, aber sehen Sie sich auch an Numpys Transponieren

5 Stimmen

Siehe diese nette Antwort, um dasselbe mit Generatoren anstelle von Listen zu tun: wie-entpacke-einen-iterator

0 Stimmen

Warum wird zip als Transponierung bezeichnet?

7voto

Azat Ibrakov Punkte 8263

Naiver Ansatz

def transpose_finite_iterable(iterable):
    return zip(*iterable)  # `itertools.izip` for Python 2 users

funktioniert gut für endliche iterierbare (z. B. Sequenzen wie list / tuple / str ) von (potentiell unendlichen) Iterablen, die wie folgt dargestellt werden können

| |a_00| |a_10| ... |a_n0| |
| |a_01| |a_11| ... |a_n1| |
| |... | |... | ... |... | |
| |a_0i| |a_1i| ... |a_ni| |
| |... | |... | ... |... | |

where

  • n in ,
  • a_ij entspricht j -te Element von i -th iterable,

und nach der Anwendung transpose_finite_iterable erhalten wir

| |a_00| |a_01| ... |a_0i| ... |
| |a_10| |a_11| ... |a_1i| ... |
| |... | |... | ... |... | ... |
| |a_n0| |a_n1| ... |a_ni| ... |

Ein Beispiel für einen solchen Fall ist die Python. a_ij == j , n == 2

>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterable(iterable)
>>> next(result)
(0, 0)
>>> next(result)
(1, 1)

Aber wir können nicht mit transpose_finite_iterable um zur Struktur des Originals zurückzukehren iterable denn result ist eine unendliche Iterabilität von endlichen Iterabilien ( tuple s in unserem Fall):

>>> transpose_finite_iterable(result)
... hangs ...
Traceback (most recent call last):
  File "...", line 1, in ...
  File "...", line 2, in transpose_finite_iterable
MemoryError

Wie können wir also mit diesem Fall umgehen?

... und hier kommt die deque

Nachdem wir einen Blick auf die Dokumente von itertools.tee Funktion gibt es ein Python-Rezept, das mit einigen Änderungen in unserem Fall helfen kann

def transpose_finite_iterables(iterable):
    iterator = iter(iterable)
    try:
        first_elements = next(iterator)
    except StopIteration:
        return ()
    queues = [deque([element])
              for element in first_elements]

    def coordinate(queue):
        while True:
            if not queue:
                try:
                    elements = next(iterator)
                except StopIteration:
                    return
                for sub_queue, element in zip(queues, elements):
                    sub_queue.append(element)
            yield queue.popleft()

    return tuple(map(coordinate, queues))

Prüfen wir

>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterables(transpose_finite_iterable(iterable))
>>> result
(<generator object transpose_finite_iterables.<locals>.coordinate at ...>, <generator object transpose_finite_iterables.<locals>.coordinate at ...>)
>>> next(result[0])
0
>>> next(result[0])
1

Synthese

Jetzt können wir eine allgemeine Funktion für die Arbeit mit Iterablen von Iterablen definieren, von denen eine endlich und eine andere potentiell unendlich ist, indem wir functools.singledispatch Tapezierer wie

from collections import (abc,
                         deque)
from functools import singledispatch

@singledispatch
def transpose(object_):
    """
    Transposes given object.
    """
    raise TypeError('Unsupported object type: {type}.'
                    .format(type=type))

@transpose.register(abc.Iterable)
def transpose_finite_iterables(object_):
    """
    Transposes given iterable of finite iterables.
    """
    iterator = iter(object_)
    try:
        first_elements = next(iterator)
    except StopIteration:
        return ()
    queues = [deque([element])
              for element in first_elements]

    def coordinate(queue):
        while True:
            if not queue:
                try:
                    elements = next(iterator)
                except StopIteration:
                    return
                for sub_queue, element in zip(queues, elements):
                    sub_queue.append(element)
            yield queue.popleft()

    return tuple(map(coordinate, queues))

def transpose_finite_iterable(object_):
    """
    Transposes given finite iterable of iterables.
    """
    yield from zip(*object_)

try:
    transpose.register(abc.Collection, transpose_finite_iterable)
except AttributeError:
    # Python3.5-
    transpose.register(abc.Mapping, transpose_finite_iterable)
    transpose.register(abc.Sequence, transpose_finite_iterable)
    transpose.register(abc.Set, transpose_finite_iterable)

die als ihre eigene Umkehrung betrachtet werden kann (Mathematiker nennen diese Art von Funktionen "Verwicklungen" ) in der Klasse der binären Operatoren über endlichen nicht-leeren Iterablen.


Als Bonus von singledispatch die wir bewältigen können numpy Arrays wie

import numpy as np
...
transpose.register(np.ndarray, np.transpose)

und verwenden Sie es dann wie

>>> array = np.arange(4).reshape((2,2))
>>> array
array([[0, 1],
       [2, 3]])
>>> transpose(array)
array([[0, 2],
       [1, 3]])

Hinweis

Seit transpose gibt Iteratoren zurück und wenn jemand eine tuple de list s wie in OP -- dies kann zusätzlich gemacht werden mit map eingebaute Funktion wie

>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> tuple(map(list, transpose(original)))
(['a', 'b', 'c', 'd'], [1, 2, 3, 4])

Anzeige

Ich habe eine verallgemeinerte Lösung zu lz Paket de 0.5.0 Version, die wie folgt verwendet werden kann

>>> from lz.transposition import transpose
>>> list(map(tuple, transpose(zip(range(10), range(10, 20)))))
[(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (10, 11, 12, 13, 14, 15, 16, 17, 18, 19)]

P.S.

Es gibt keine (zumindest offensichtliche) Lösung für den Umgang mit potenziell unendlichen Iterablen von potenziell unendlichen Iterablen, aber dieser Fall ist weniger häufig.

4voto

G M Punkte 17276

Es ist nur eine andere Art, es zu tun, aber es hat mir sehr geholfen, also schreibe ich es hier:

Mit dieser Datenstruktur:

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

Daraus ergibt sich:

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

Der pythonischere Weg, es zu entpacken und zum Original zurückzukehren, ist meiner Meinung nach dieser:

x,y=zip(*XY)

Aber dies gibt ein Tupel zurück, wenn Sie also eine Liste benötigen, können Sie diese verwenden:

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

4voto

Neil G Punkte 30323

Erwägen Sie die Verwendung von more_itertools.unzip :

>>> from more_itertools import unzip
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> [list(x) for x in unzip(original)]
[['a', 'b', 'c', 'd'], [1, 2, 3, 4]]

3voto

jpp Punkte 146159

Keine der vorherigen Antworten effizient die erforderliche Leistung erbringen, d. h. eine Tupel von Listen und nicht eine Liste von Tupeln . Für ersteres können Sie Folgendes verwenden tuple mit map . Hier ist der Unterschied:

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

Darüber hinaus gehen die meisten der bisherigen Lösungen von Python 2.7 aus, wo zip gibt eine Liste statt eines Iterators zurück.

Für Python 3.x müssen Sie das Ergebnis an eine Funktion wie list o tuple um den Iterator zu erschöpfen. Für speichereffiziente Iteratoren können Sie die äußere list y tuple fordert die entsprechenden Lösungen.

1 Stimmen

Dies sollte die erste Antwort sein. Es ist frustrierend, die anderen Antworten zu sehen, die derzeit als "erste" gelten.

2voto

Trasp Punkte 1058

Auch wenn Numpy-Arrays und Pandas vorzuziehen sind, imitiert diese Funktion das Verhalten von zip(*args) beim Aufruf als unzip(args) .

Erlaubt die Verwendung von Generatoren, wie das Ergebnis von zip in Python 3, die als args wenn es durch die Werte geht.

def unzip(items, cls=list, ocls=tuple):
    """Zip function in reverse.

    :param items: Zipped-like iterable.
    :type  items: iterable

    :param cls: Container factory. Callable that returns iterable containers,
        with a callable append attribute, to store the unzipped items. Defaults
        to ``list``.
    :type  cls: callable, optional

    :param ocls: Outer container factory. Callable that returns iterable
        containers. with a callable append attribute, to store the inner
        containers (see ``cls``). Defaults to ``tuple``.
    :type  ocls: callable, optional

    :returns: Unzipped items in instances returned from ``cls``, in an instance
        returned from ``ocls``.
    """
    # iter() will return the same iterator passed to it whenever possible.
    items = iter(items)

    try:
        i = next(items)
    except StopIteration:
        return ocls()

    unzipped = ocls(cls([v]) for v in i)

    for i in items:
        for c, v in zip(unzipped, i):
            c.append(v)

    return unzipped

Um List-Cointainer zu verwenden, führen Sie einfach unzip(zipped) als

unzip(zip(["a","b","c"],[1,2,3])) == (["a","b","c"],[1,2,3])

Zur Verwendung von Deques, oder anderen sportlichen Behältern append übergeben Sie eine Fabrikfunktion.

from collections import deque

unzip([("a",1),("b",2)], deque, list) == [deque(["a","b"]),deque([1,2])]

(Dekorieren cls und/oder main_cls zur Mikroverwaltung der Containerinitialisierung, wie in der abschließenden Assert-Anweisung oben kurz dargestellt).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X