조옮김 / 압축 해제 기능 (zip의 역)?


505

2 개의 항목 튜플 목록이 있고 첫 번째 항목은 각 튜플의 첫 번째 항목을 포함하고 두 번째 목록은 두 번째 항목을 포함하는 2 개의 목록으로 변환하고 싶습니다.

예를 들면 다음과 같습니다.

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

그렇게하는 내장 함수가 있습니까?


6
아래에 큰 답변이 있지만, numpy의 조옮김
opyate

3
목록 대신 생성기로 동일한 작업을 수행하려면이 멋진 답변을 참조하십시오. how-to-
zip

답변:


778

zip그 자신의 역이다! 특수 * 연산자를 사용했다면

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

이것이 작동하는 방식 zip은 인수를 사용하여 호출하는 것 입니다.

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

… 인수가 zip직접 튜플로 변환 된 후 전달 되므로 인수가 너무 커질 염려가 없습니다.


20
오, 너무 간단하다면. zip([], [])이 방법으로 압축을 풀면 얻을 수 없습니다 [], []. 그것은 당신을 얻는다 []. 경우에만 ...
user2357112 지원 모니카

4
Python3에서는 작동하지 않습니다. 참조 : stackoverflow.com/questions/24590614/...
토미

31
@Tommy 이것은 올바르지 않습니다. zip파이썬 3에서 목록 대신 반복자를 반환한다는 점을 제외하고는 정확히 동일하게 작동합니다. 위와 동일한 출력을 얻으려면 zip 호출을 목록으로 list(zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]))[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
감싸면

4
주의 : 매우 긴 목록으로 메모리 및 성능 문제를 해결할 수 있습니다.
Laurent LAPORTE

1
@ JohnP : lists는 괜찮습니다. 당신이 (모든 한 번에 전체 결과를 실현하기 위해 노력하지만 list결과를 ifying zip) (있기 때문에, 당신은 많은 메모리를 사용할 수있는 모든tuple 의 한 번에 작성해야합니다). 수정 zip하지 않고 결과를 반복 할 수 있으면 list많은 메모리가 절약됩니다. 다른 관심사는 입력에 많은 요소가있는 경우입니다. 비용은 인수로 모두 압축을 풀어야하며 모든 zip반복자를 작성하고 저장해야합니다. 이것은 매우 긴 경우에만 발생하는 실제 문제입니다 list(수십만 요소 이상을 생각하십시오).
ShadowRanger

29

당신은 또한 할 수 있습니다

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

그것은 해야 더 확장 할 수 있습니다. 특히 파이썬이 필요하지 않으면 목록 이해력을 확장하지 않는 것이 좋습니다.

(우연히, 튜플 목록이 아닌 2 튜플 (쌍) 목록을 zip만듭니다.)

실제 목록 대신 생성기가 괜찮다면 다음과 같이하십시오.

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

생성기는 각 요소를 요청할 때까지 목록을 검색하지 않지만 원래 목록을 계속 참조합니다.


8
"특히 파이썬이 필요한 경우가 아니라면 목록 이해력을 확장하지 않는 것이 좋습니다." 음 ... 일반적으로 목록 이해력이 즉시 확장됩니까? 아니면 잘못된 것이 있습니까?
glglgl

1
@glglgl : 아니요, 아마 그렇습니다. 나는 미래의 일부 버전이 옳은 일을 시작하기를 바랐습니다. (그것은 필요로 변경 아마 이미 낙심되어, 부작용의 의미를 변경하는 것은 불가능하지 않습니다.)
앤더스 Eurenius

9
당신이 얻고 싶은 것은 이미 존재하는 발전기 expresion입니다.
glglgl 2013 년

12
이것은 zip(*x)버전 보다 '확장되지 않습니다' . zip(*x)루프를 한 번만 통과하면되며 스택 요소를 사용하지 않습니다.
habnabit

1
"확장 성"인지 여부는 변환 된 데이터와 비교하여 원본 데이터의 수명주기에 따라 다릅니다. zip유스 케이스가 전치 된 데이터를 즉시 사용하고 버리는 반면, 원래 목록은 훨씬 더 오래 메모리에 남아있는 경우 에만이 답변을 사용 하는 것보다 낫습니다 .
Ekevoo

21

길이가 다른 목록이있는 경우 Patricks 답변에 따라 zip을 사용하지 않을 수 있습니다. 이것은 작동합니다 :

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

그러나 길이 목록이 다른 경우 zip은 각 항목을 가장 짧은 목록의 길이로 자릅니다.

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

아무 함수없이 map을 사용하여 빈 결과를 None으로 채울 수 있습니다.

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

zip ()은 약간 빠릅니다.


4
다음을 사용할 수도 있습니다izip_longest
Marcin

3
zip_longestpython3 사용자 로 알려져 있습니다 .
zezollo

1
@GrijeshChauhan이 기능은 실제로 오래되었다는 것을 알고 있지만 이상한 기능이 내장되어 있습니다. docs.python.org/2/library/functions.html#map "함수가 ' n '이면 항등 함수가 가정됩니다. 여러 개의 인수가있는 경우, map ()은 모든 반복 가능 항목 (전치 연산의 일종)에서 해당 항목을 포함하는 튜플로 구성된 목록을 반환합니다. 반복 가능 인수는 시퀀스 또는 반복 가능 객체 일 수 있으며 결과는 항상 목록입니다. "
선인장 1

18

나는 zip(*iterable)내 프로그램에서 (당신이 찾고있는 코드 조각)을 다음 과 같이 사용 하고 싶다.

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

내가 찾아 unzip더 읽기.


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

질문에서와 같이 목록의 튜플을 제공합니다.

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

두 목록의 압축을 풉니 다.


8

순진한 접근

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

다음과 같이 설명 할 수있는 (잠재적으로 무한한) 반복 가능한 유한 반복 가능한 (예 : list/ tuple/ str) 시퀀스에 대해 잘 작동합니다.

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

어디

  • n in ℕ,
  • a_ijiterable의 -th j요소에 해당합니다 i.

신청 후 transpose_finite_iterable우리는 얻을

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

이러한 경우 파이썬 예 여기서 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)

그러나 우리는 유한 iterables의 무한 iterable ( 우리의 경우 s) 이기 때문에 transpose_finite_iterable원래의 구조로 돌아 가기 위해 다시 사용할 수 없습니다 :iterableresulttuple

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

이 사건을 어떻게 다룰 수 있을까요?

... 그리고 여기에 온다 deque

우리는 docs of itertools.teefunction을 살펴본 후 약간의 수정으로 우리의 경우에 도움이 될 수있는 Python 레시피가 있습니다.

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

점검 해보자

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

합성

이제 우리는 한정되어있는 반복 가능 객체 것들의 반복 가능 객체 작업을위한 일반 함수를 정의 할 수 있으며 다른 사람이 사용하여 잠재적으로 무한 functools.singledispatch장식을 같이

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)

유한 한 비어 있지 않은 이터 러블에 대한 이진 연산자의 클래스에서 자체 역수 (수학자들은 이런 종류의 함수를 " 진화 "라고 부름)로 간주 할 수 있습니다 .


singledispatching 의 보너스로 다음 numpy과 같은 배열 을 처리 할 수 ​​있습니다.

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

그런 다음 사용하십시오

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

노트

때문에 transpose누군가가 가지고 싶어하는 경우 반환 반복자와 tuplelist영업 이익처럼들 -이 함께 추가로 만들 수 있습니다 map내장 된 기능 과 같은

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

광고

나는에 일반화 된 솔루션을 추가 한 lz패키지 에서 0.5.0처럼 사용할 수있는 버전

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

추신

잠재적으로 무한 반복 가능한 무한 반복을 처리하는 솔루션은 (적어도 명백한) 없지만,이 경우는 덜 일반적입니다.


4

그것을하는 또 다른 방법 일뿐이지만 여기에 많은 도움이되었습니다.

이 데이터 구조를 가지고 :

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

를 야기하는:

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

압축을 풀고 원본으로 돌아가는 더 pythonic 방법은 내 의견으로는 다음과 같습니다.

x,y=zip(*XY)

그러나 이것은 튜플을 반환하므로 목록이 필요하면 다음을 사용할 수 있습니다.

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

3

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

1

그것이 튜플을 반환하고 ( zip(*zipped)많은 메모리를 사용할 수 있기 때문에) 트릭은 유용하지 않습니다.

다음은 실제로 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

지속적으로 튜플을 다시 만드는 것은 나에게 효과적이지 않지만 메모리를 미리 할당 할 수있는 deques를 사용 하여이 접근법을 확장 할 수 있습니다.
Charlie Clark

0

이전 답변 없음 효율적 A는 필요한 출력을 제공하지 리스트의 튜플 보다는 튜플의 목록을 . 전자의 경우 tuple와 함께 사용할 수 있습니다 map. 차이점은 다음과 같습니다.

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

또한 대부분의 이전 솔루션은 Python 2.7을 가정합니다. zip 하며 반복자 대신 목록을 반환합니다.

Python 3.x의 경우 반복기를 소진 list하거나 tuple소진하는 등의 함수에 결과를 전달해야 합니다. 메모리 효율이 반복자를 들어, 외부 생략 할 수 listtuple각각의 솔루션에 대한 호출을.


0

zip(*seq)매우 유용 하지만 전달 될 튜플 값을 생성하기 때문에 매우 긴 시퀀스에는 적합하지 않을 수 있습니다. 직접 서열.

일반적인 접근 방식은 다음과 같습니다.

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)

그러나 결과로 수행하려는 작업에 따라 컬렉션을 선택하면 큰 차이를 만들 수 있습니다. 실제 사용 사례에서 내부 루프를 사용하지 않고 세트를 사용하면 다른 모든 방법보다 눈에 띄게 빠릅니다.

그리고 다른 사람들이 지적했듯이 데이터 세트 로이 작업을 수행하는 경우 대신 Numpy 또는 Pandas 컬렉션을 사용하는 것이 좋습니다.


0

numpy 배열과 팬더가 바람직 할 수 있지만이 함수는 zip(*args)로 호출 될 때 의 동작을 모방합니다 unzip(args).

args값을 반복 할 때 생성기를 전달할 수 있습니다 . 컨테이너 초기화를 장식 cls및 / 또는 main_cls미세 관리합니다.

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

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

    :param cls: Callable that returns iterable with callable append attribute.
        Defaults to `list`.
    :type  cls: callable, optional

    :param main_cls: Callable that returns iterable with callable append
        attribute. Defaults to `tuple`.
    :type  main_cls: callable, optional

    :returns: Unzipped items in instances returned from `cls`, in an instance
        returned from `main_cls`.

    :Example:

        assert unzip(zip(["a","b","c"],[1,2,3])) == (["a","b",c"],[1,2,3])
        assert unzip([("a",1),("b",2),("c",3)]) == (["a","b","c"],[1,2,3])
        assert unzip([("a",1)], deque, list) == [deque(["a"]),deque([1])]
        assert unzip((["a"],["b"]), lambda i: deque(i,1)) == (deque(["b"]),)
    """
    items = iter(items)

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

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

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

    return unzipped
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.