Mainly Tech projects on Python and Electronic Design Automation.

Friday, February 09, 2007

unzip un-needed in Python

Someone blogged about Python not having an unzip function to go with zip(). unzip is straight-forward to calculate because:
>>> t1 = (0,1,2,3)
>>> t2 = (7,6,5,4)
>>> [t1,t2] == zip(*zip(t1,t2))
True

Explanation

In answer to a commentator, I have written a (large), program to explain the above. unzip_explained.py:
'''
Explanation of unzip expression zip(*zip(A,B))

References:
 1: Unpacking argumment lists
    http://www.network-theory.co.uk/docs/pytut/UnpackingArgumentLists.html
 2: Zip
    >>> help(zip)
    Help on built-in function zip in module __builtin__:

    zip(...)
        zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
        
        Return a list of tuples, where each tuple contains the i-th element
        from each of the argument sequences.  The returned list is truncated
        in length to the length of the shortest argument sequence.


'''

def show_args(*positional, **kwargs):
  "Straight-forward function to show its arguments"
  n = 0
  for p in positional:
    print "    positional argument", n, "is", p
    n += 1
  for k,v in sorted(kwargs.items()):
    print "    keyword argument", k, "is", v

A = tuple( "A%i" % n for n in range(3) )
print "\n\nTuple A is:"; print "   ", A
B = tuple( "B%i" % n for n in range(3) )
print "Tuple B is:"; print "   ", B

print "\nLets go slowly through the expression: [A,B] == zip(*zip(A,B))\n"

print "List [A,B] is:"
print "   ", [A,B]
print "zip(A,B) has arguments:"; show_args(A,B)
print "zip(A,B) returns:"
print "   ", zip(A,B)
print "The leftmost zip in zip(*zip(A,B)), due"
print " to the 'list unpacking' of the previous"
print " value has arguments of:"; show_args(*zip(A,B))
print "The outer zip therefore returns:"
print "   ", zip(*zip(A,B))
print "Which is the same as [A,B]\n"
And here is the program output:
Tuple A is:
    ('A0', 'A1', 'A2')
Tuple B is:
    ('B0', 'B1', 'B2')

Lets go slowly through the expression: [A,B] == zip(*zip(A,B))

List [A,B] is:
    [('A0', 'A1', 'A2'), ('B0', 'B1', 'B2')]
zip(A,B) has arguments:
    positional argument 0 is ('A0', 'A1', 'A2')
    positional argument 1 is ('B0', 'B1', 'B2')
zip(A,B) returns:
    [('A0', 'B0'), ('A1', 'B1'), ('A2', 'B2')]
The leftmost zip in zip(*zip(A,B)), due
 to the 'list unpacking' of the previous
 value has arguments of:
    positional argument 0 is ('A0', 'B0')
    positional argument 1 is ('A1', 'B1')
    positional argument 2 is ('A2', 'B2')
The outer zip therefore returns:
    [('A0', 'A1', 'A2'), ('B0', 'B1', 'B2')]
Which is the same as [A,B]

9 comments:

repei said...

What happens in the third line, can you explain that in more details?

Anonymous said...

Cool!

Doug Napoleone said...

@repei

>>> zip(*zip(A,B)) == [A, B]
True
>>> zip(*zip(*zip(A,B))) == zip(A, B)
True

zip is unzip is zip. This is a bit weird to think about.

Lisp hacks seem to consider this obvious, but us mere mortals sometimes need a little help. Thanks for a fantastic post!

I considered having this as one of the problems for the Python Lab at PyCon, but decided it was too much of a 'trick' problem.

Kyle said...

This property of the zip function in Python is only true if you zip tuples and not lists.

>>> A = [1,2,3]
>>> B = [4,5,6]
>>> zip(*zip(A,B)) == [A,B]
False

When I give zip a tuple of lists, I want a tuple of lists back from unzip:

>>> def unzip(a):
.......return tuple(map(list,zip(*a)))
>>> unzip(zip(A,B)) == (A,B)
True
>>> zip(*unzip(zip(A,B))) == zip(A,B)
True

Anonymous said...

Now that you mention it, it's obvious! Even more so if you think of zip as (a truncating) matrix transpose.

repei said...

I just didn't know about unpacking function arguments by '*'. It all became clear for me now. Thank you.

Dan said...

That is really funny... how could anyone use Python's zip() function and NOT realize that it is its own inverse???

Good to see another Python/EDA hacker out there. I'm in the same boat... Pythonista and a bit of a hardware hacker. I've got some random utilities up at http://tonquil.homeip.net/~dlenski

Anonymous said...
This post has been removed by a blog administrator.
Anonymous said...

LOL. That's awesome. I've never thought about it. <3 self-composable functions in python library.

Post a Comment

Subscribe Now: google

Add to Google Reader or Homepage

Go deh too!

FEEDJIT Live Traffic Map

whos.amung.us

About Me

Blog Archive