Reverse zip() ?

  • Thread starter Andreas Waldenburger
  • Start date
A

Andreas Waldenburger

Hi all,

we all know about the zip builtin that combines several iterables into
a list of tuples.

I often find myself doing the reverse, splitting a list of tuples into
several lists, each corresponding to a certain element of each tuple
(e.g. matplotlib/pyplot needs those, rather than lists of points).

This is of course trivial to do via iteration or listcomps, BUT, I was
wondering if there is a function I don't know about that does this
nicely?

redards
/W
 
S

Stefan Behnel

Andreas said:
we all know about the zip builtin that combines several iterables into
a list of tuples.

I often find myself doing the reverse, splitting a list of tuples into
several lists, each corresponding to a certain element of each tuple
(e.g. matplotlib/pyplot needs those, rather than lists of points).

This is of course trivial to do via iteration or listcomps, BUT, I was
wondering if there is a function I don't know about that does this
nicely?

I think you're asking about zip():

>>> l=[1,2,3]
>>> zip(l,l)
[(1, 1), (2, 2), (3, 3)]
>>> zip(*zip(l,l))
[(1, 2, 3), (1, 2, 3)]

Stefan
 
A

Andreas Waldenburger

Andreas said:
[snip]
This is of course trivial to do via iteration or listcomps, BUT, I
was wondering if there is a function I don't know about that does
this nicely?

I think you're asking about zip():

>>> l=[1,2,3]
>>> zip(l,l)
[(1, 1), (2, 2), (3, 3)]
>>> zip(*zip(l,l))
[(1, 2, 3), (1, 2, 3)]
So I am. That sure was weird.

Thanks for the quick and somewhat surreal help. Need I say that between
posting this question and reading your reply I set out to implement
this wonder-function?
:)

/W
 
J

John Machin

Andreas said:
we all know about the zip builtin that combines several iterables into
a list of tuples.
I often find myself doing the reverse, splitting a list of tuples into
several lists, each corresponding to a certain element of each tuple
(e.g. matplotlib/pyplot needs those, rather than lists of points).
This is of course trivial to do via iteration or listcomps, BUT, I was
wondering if there is a function I don't know about that does this
nicely?

I think you're asking about zip():

        >>> l=[1,2,3]
        >>> zip(l,l)
        [(1, 1), (2, 2), (3, 3)]
        >>> zip(*zip(l,l))
        [(1, 2, 3), (1, 2, 3)]

Here's a version that makes it slightly easier to comprehend:

Q: I know how to zip sequences together:
| >>> a = (1, 2, 3)
| >>> b = (4, 5, 6)
| >>> z = zip(a, b)
| >>> z
| [(1, 4), (2, 5), (3, 6)]
but how do I reverse the process?

A: Use zip()!
| >>> a2, b2 = zip(*z)
| >>> a2
| (1, 2, 3)
| >>> b2
| (4, 5, 6)

Cheers,
John
 
Z

Zac Burns

There is a problem with this however, which prompted me to actually
write an unzip function.

One might expect to be able to do something like so (pseudocode)...

def filesAndAttributes():
files = walk()
attributes = [attr(f) for f in files]
return zip(files, attributes)

files, attributes = zip(*filesAndAttributes())

The corner case is when dealing with empty lists and there aren't
enough items to unpack.

The unzip function therefore has an elementsForEmpty keyword that
handles this case. Perhaps something like this could be added to zip?
I have not (yet) dealt with the PEP process, so I'm not sure where
that starts. Perhaps a discussion could start here.

--
Zachary Burns
(407)590-4814
Aim - Zac256FL
Production Engineer (Digital Overlord)
Zindagi Games



Andreas said:
we all know about the zip builtin that combines several iterables into
a list of tuples.
I often find myself doing the reverse, splitting a list of tuples into
several lists, each corresponding to a certain element of each tuple
(e.g. matplotlib/pyplot needs those, rather than lists of points).
This is of course trivial to do via iteration or listcomps, BUT, I was
wondering if there is a function I don't know about that does this
nicely?

I think you're asking about zip():
l=[1,2,3]
zip(l,l) [(1, 1), (2, 2), (3, 3)]
zip(*zip(l,l))
[(1, 2, 3), (1, 2, 3)]

Here's a version that makes it slightly easier to comprehend:

Q: I know how to zip sequences together:
| >>> a = (1, 2, 3)
| >>> b = (4, 5, 6)
| >>> z = zip(a, b)
| >>> z
| [(1, 4), (2, 5), (3, 6)]
but how do I reverse the process?

A: Use zip()!
| >>> a2, b2 = zip(*z)
| >>> a2
| (1, 2, 3)
| >>> b2
| (4, 5, 6)

Cheers,
John
 
Z

Zac Burns

More succinct failure:

keys, values = zip(*{}.iteritems())

--
Zachary Burns
(407)590-4814
Aim - Zac256FL
Production Engineer (Digital Overlord)
Zindagi Games



There is a problem with this however, which prompted me to actually
write an unzip function.

One might expect to be able to do something like so (pseudocode)...

def filesAndAttributes():
files = walk()
attributes = [attr(f) for f in files]
return zip(files, attributes)

files, attributes = zip(*filesAndAttributes())

The corner case is when dealing with empty lists and there aren't
enough items to unpack.

The unzip function therefore has an elementsForEmpty keyword that
handles this case. Perhaps something like this could be added to zip?
I have not (yet) dealt with the PEP process, so I'm not sure where
that starts. Perhaps a discussion could start here.

--
Zachary Burns
(407)590-4814
Aim - Zac256FL
Production Engineer (Digital Overlord)
Zindagi Games



Andreas Waldenburger wrote:
we all know about the zip builtin that combines several iterables into
a list of tuples.

I often find myself doing the reverse, splitting a list of tuples into
several lists, each corresponding to a certain element of each tuple
(e.g. matplotlib/pyplot needs those, rather than lists of points).

This is of course trivial to do via iteration or listcomps, BUT, I was
wondering if there is a function I don't know about that does this
nicely?

I think you're asking about zip():

l=[1,2,3]
zip(l,l)
[(1, 1), (2, 2), (3, 3)]
zip(*zip(l,l))
[(1, 2, 3), (1, 2, 3)]

Here's a version that makes it slightly easier to comprehend:

Q: I know how to zip sequences together:
| >>> a = (1, 2, 3)
| >>> b = (4, 5, 6)
| >>> z = zip(a, b)
| >>> z
| [(1, 4), (2, 5), (3, 6)]
but how do I reverse the process?

A: Use zip()!
| >>> a2, b2 = zip(*z)
| >>> a2
| (1, 2, 3)
| >>> b2
| (4, 5, 6)

Cheers,
John
 
B

Bryan Olson

Zac said:
There is a problem with this however, which prompted me to actually
write an unzip function.

One might expect to be able to do something like so (pseudocode)...

def filesAndAttributes():
files = walk()
attributes = [attr(f) for f in files]
return zip(files, attributes)

You could do away with that zip and just let the list comprehension
return tuples.

[(f, attr(f)) for f in walker()]
files, attributes = zip(*filesAndAttributes())

The corner case is when dealing with empty lists and there aren't
enough items to unpack.

Can you give a concrete example?
The unzip function therefore has an elementsForEmpty keyword that
handles this case. Perhaps something like this could be added to zip?

The built-in zip stops when any of the given iterables stops. The
itertools module has izip_longest (or zip_longest in Python 3), which
takes a fillvalue argument. Perhaps that's what you want?
 
B

Bryan Olson

John said:
Here's a version that makes it slightly easier to comprehend:

Q: I know how to zip sequences together:
| >>> a = (1, 2, 3)
| >>> b = (4, 5, 6)
| >>> z = zip(a, b)
| >>> z
| [(1, 4), (2, 5), (3, 6)]
but how do I reverse the process?

A: Use zip()!
| >>> a2, b2 = zip(*z)
| >>> a2
| (1, 2, 3)
| >>> b2
| (4, 5, 6)

zip as its own inverse might be even easier to comprehend if we call zip
by its more traditional name, "transpose".
 
S

Stefan Behnel

Zac said:
More succinct failure:

keys, values = zip(*{}.iteritems())

Simple fix:

some_iterable = {}.iteritems()
keys, values = zip(*list(some_iterable))

or:

keys, values = zip(*(some_iterable
if isinstance(some_iterable, (list, tuple))
else list(some_iterable)))

Stefan
 
A

Andreas Waldenburger

zip as its own inverse might be even easier to comprehend if we call
zip by its more traditional name, "transpose".
Sounds like a Py4k change to me.

/W
 
J

Janto Dreijer

I'd like to point out that since your where thinking in terms of
matplotlib, you might actually find numpy's own transpose useful,
instead of using zip(*seq) :)

untested:

t = linspace(0,2*pi*3)
seq = asarray(zip(t, sin(t)))

t, y = seq.T # or seq.transpose() or numpy.transpose(seq)
pylab.plot(t,y)

Regards
Janto
 
A

Andreas Waldenburger

I'd like to point out that since your where thinking in terms of
matplotlib, you might actually find numpy's own transpose useful,
instead of using zip(*seq) :)
This was, of course, to be expected. :)

Whenever will I have an original problem that has not been solved
millions of times yet?

/W
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top