ट्रांसपोज़ / अनज़िप फंक्शन (ज़िप का उलटा)?


504

मेरे पास 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
नीचे महान जवाब, लेकिन यह भी सुन्न के प्रस्ताव को
opyate

3
सूची के बजाय जनरेटर के साथ ऐसा करने के लिए यह अच्छा जवाब देखें: कैसे-अनज़िप-

जवाबों:


777

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
यह पायथन 3 में काम नहीं करता है। देखें: stackoverflow.com/questions/24590614/…
टॉमी

30
@ टॉमी यह गलत है। zipपायथन 3 में बिल्कुल वैसा ही काम करता है, सिवाय इसके कि यह सूची के बजाय एक पुनरावृत्तिकर्ता को लौटाता है। ऊपर के समान उत्पादन प्राप्त करने के लिए आपको बस एक सूची में जिप कॉल को लपेटना list(zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]))होगा : आउटपुट होगा[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
MJeffryes

4
सूचना: आप बहुत लंबी सूची के साथ स्मृति और प्रदर्शन के मुद्दों को पूरा कर सकते हैं।
लॉरेंट LAPORTE

1
@ जॉन: listएस ठीक हैं। लेकिन अगर आप (द्वारा एक ही बार में पूरा परिणाम का एहसास करने की कोशिश listका नतीजा ifying zip), तो आप स्मृति का एक बहुत का उपयोग कर सकते हैं (क्योंकि सभीtuple रों एक ही बार में बनाया जाना चाहिए)। यदि आप zipबिना listइफिट के परिणाम पर सिर्फ पुनरावृति कर सकते हैं , तो आप बहुत सारी मेमोरी बचा लेंगे। केवल दूसरी चिंता यह है कि इनपुट में कई तत्व हैं; वहाँ लागत यह है कि यह उन सभी को तर्क के रूप में अनपैक करना चाहिए, और zipउन सभी के लिए पुनरावृत्तियों को बनाने और संग्रहीत करने की आवश्यकता होगी। यह केवल बहुत लंबी listएस के साथ एक वास्तविक समस्या है (लगता है कि सैकड़ों हजारों तत्व या अधिक)।
शैडो रेंजर

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
"विशेष रूप से यदि पायथन सूची की समझ का विस्तार नहीं करने पर अच्छा बनाता है जब तक कि जरूरत न हो।" एमएमएम ... आम तौर पर, सूची की समझ तुरंत विस्तारित हो जाती है - या मुझे कुछ गलत लगता है?
ग्लॉगल

1
@glglgl: नहीं, तुम शायद सही हो। मैं उम्मीद कर रहा था कि कुछ भविष्य के संस्करण सही काम करना शुरू कर सकते हैं। (यह बदलने के लिए असंभव नहीं है, साइड-
इफेक्ट्स

9
जो आप प्राप्त करने की उम्मीद करते हैं वह एक जनरेटर का विस्तार है - जो पहले से मौजूद है।
glglgl

12
यह zip(*x)संस्करण की तुलना में 'बेहतर पैमाने' पर नहीं है । zip(*x)केवल लूप के माध्यम से एक पास की आवश्यकता होती है, और स्टैक तत्वों का उपयोग नहीं करता है।
हबनबीत

1
चाहे यह "तराजू बेहतर" हो या नहीं यह ट्रांसपोज़्ड डेटा की तुलना में मूल डेटा के जीवनचक्र पर निर्भर करता है। यह उत्तर केवल उपयोग करने से बेहतर है zipयदि उपयोग-मामला यह है कि ट्रांसपोज़्ड डेटा का उपयोग किया जाता है और तुरंत उसे छोड़ दिया जाता है, जबकि मूल सूचियां बहुत लंबे समय तक स्मृति में रहती हैं।
एकेवू

21

यदि आपके पास ऐसी सूचियाँ हैं जो समान लंबाई की नहीं हैं, तो आप पैट्रिक के उत्तर के अनुसार ज़िप का उपयोग नहीं करना चाह सकते हैं। यह काम:

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

लेकिन अलग-अलग लंबाई सूचियों के साथ, ज़िप प्रत्येक आइटम को सबसे छोटी सूची की लंबाई तक काट देता है:

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

बिना किसी खाली परिणाम के भरने के लिए आप बिना किसी फ़ंक्शन के मानचित्र का उपयोग कर सकते हैं:

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

ज़िप () हालांकि तेजी से तेज है।


4
आप भी इस्तेमाल कर सकते हैंizip_longest
Marcin

3
zip_longestPython3 उपयोगकर्ताओं के लिए जाना जाता है ।
zezollo

1
@GrijeshChauhan मुझे पता है कि यह वास्तव में पुराना है, लेकिन यह एक अजीब सुविधा में बनाया गया है: docs.python.org/2/library/functions.html#map "यदि फ़ंक्शन कोई नहीं है, तो पहचान फ़ंक्शन माना जाता है; यदि कई तर्क हैं, तो नक्शा () एक सूची देता है जिसमें सभी पुनरावृत्तियों (एक प्रकार का पारगमन संचालन) से संबंधित वस्तुओं वाले ट्यूलस से युक्त होता है। पुनरावृत्त तर्क एक अनुक्रम या कोई पुनरावृत्त वस्तु हो सकते हैं; परिणाम हमेशा एक सूची होता है। "
cactus1

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_ijj-th तत्व के अनुरूप है 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)

लेकिन हम transpose_finite_iterableफिर से मूल की संरचना में लौटने के लिए उपयोग नहीं कर सकते iterableक्योंकि resultपरिमित पुनरावृत्तियों की एक अनंत पुनरावृत्ति है ( tupleहमारे मामले में):

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

तो हम इस मामले से कैसे निपट सकते हैं?

... और यहाँ आता है deque

जब हम itertools.teeफ़ंक्शन के डॉक्स पर एक नज़र डालते हैं , तो पायथन नुस्खा है जो कुछ संशोधन के साथ हमारे मामले में मदद कर सकता है

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)

जिसे अपने स्वयं के व्युत्क्रम के रूप में माना जा सकता है (गणितज्ञ इस तरह के कार्यों को "इनवोल्यूशन" कहते हैं ) परिमित गैर-खाली पुनरावृत्तियों पर बाइनरी ऑपरेटरों की श्रेणी में आते हैं।


singledispatchआईएनजी के एक बोनस के रूप में हम 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रिटर्न iterators और अगर किसी को एक करने की है tupleकी listओपी में की तरह रों - इस के साथ साथ ही बनाया जा सकता है 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')]

इसे अनज़िप करने और मूल पर वापस जाने का अधिक पाइथोनिक तरीका मेरी राय में यह है:

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)चाल मेरे लिए उपयोगी से अधिक चतुर लगती है।

यहां एक फ़ंक्शन है जो वास्तव में आपको ज़िप का उलटा देगा।

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

लगातार टुपल्स को फिर से बनाना मेरे लिए उतना कुशल नहीं लगता है, लेकिन आप इस दृष्टिकोण को उन देवताओं का उपयोग करके बढ़ा सकते हैं जो मेमोरी का प्रचार कर सकते हैं।
चार्ली क्लार्क

0

पिछले जवाब में से कोई भी कुशलता से अपेक्षित उत्पादन है, जो एक है प्रदान सूचियों का टपल एक के बजाय tuples की सूची । पूर्व के लिए, आप के 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])

इसके अलावा, पिछले समाधानों में से अधिकांश पायथन 2.7 को मानते हैं, जहां zip लेते हैं एक पुनरावृत्त के बजाय एक सूची देता है।

पायथन 3.x के लिए, आपको परिणाम को समारोह में पास करने की आवश्यकता होगी जैसे कि listया tupleइटरेटर को समाप्त करना। स्मृति कुशल iterators के लिए, आप बाहरी छोड़ सकते हैं listऔर tupleसंबंधित समाधान के लिए कॉल।


0

जबकि zip(*seq) बहुत उपयोगी है, यह बहुत लंबे समय के दृश्यों के लिए अनुपयुक्त के रूप में यह मूल्यों में पारित होने के लिए की एक टपल पैदा करेगा हो सकता है। उदाहरण के लिए, मैं एक लाख से अधिक प्रविष्टियों के साथ एक समन्वय प्रणाली के साथ काम कर रहा है और यह तेजी से signifcantly बनाने के लिए लगता है सीधे अनुक्रम।

एक सामान्य दृष्टिकोण कुछ इस तरह होगा:

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

हालांकि सुन्न सारणी और पांडा बेहतर हो सकते हैं, यह फ़ंक्शन 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.