unzip function?

N

Neal Becker

python has builtin zip, but not unzip

A bit of googling found my answer for my decorate/sort/undecorate problem:

a, b = zip (*sorted ((c,d) for c,d in zip (x,y)))

That zip (*sorted...

does the unzipping.

But it's less than intuitively obvious.

I'm thinking unzip should be a builtin function, to match zip.
 
S

Steven D'Aprano

python has builtin zip, but not unzip

That's because zip is (almost) its own inverse.

A bit of googling found my answer for my decorate/sort/undecorate
problem:
a, b = zip (*sorted ((c,d) for c,d in zip (x,y)))

That does a lot of unnecessary work.

a, b = zip(*sorted(zip(x,y)))

That zip (*sorted...

does the unzipping.

But it's less than intuitively obvious.

*shrug*

If you understand what zip does, it should be obvious.

I'm thinking unzip should be a builtin function, to match zip.

Just create your own utility function. Not everything needs to be a
built-in.

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


Hardly seems worthwhile.
 
D

Devin Jeanpierre

*shrug*

If you understand what zip does, it should be obvious.

Nobody likes to be told the thing they're confused about is trivial.
It's especially bad if nobody ever actually explains what's so simple
about it. Saying it's "almost its own inverse" is just restating the
original question -- yes, that's what it is, but why? (I have put my
own interpretation in a separate reply.
)
-- Devin
 
H

Hrvoje Niksic

Neal Becker said:
python has builtin zip, but not unzip

A bit of googling found my answer for my decorate/sort/undecorate problem:

a, b = zip (*sorted ((c,d) for c,d in zip (x,y)))

That zip (*sorted...

does the unzipping.

But it's less than intuitively obvious.

I'm thinking unzip should be a builtin function, to match zip.

"zip" and "unzip" are one and the same since zip is inverse to itself:
[(1, 2, 3), (4, 5, 6)] [(1, 2, 3), (4, 5, 6)]
zip(*_) [(1, 4), (2, 5), (3, 6)]
zip(*_) [(1, 2, 3), (4, 5, 6)]
zip(*_)
[(1, 4), (2, 5), (3, 6)]

What you seem to call unzip is simply zip with a different signature,
taking a single argument:
.... return zip(*x)
....
[(1, 2, 3), (4, 5, 6)] [(1, 2, 3), (4, 5, 6)]
unzip(_) [(1, 4), (2, 5), (3, 6)]
unzip(_) [(1, 2, 3), (4, 5, 6)]
unzip(_)
[(1, 4), (2, 5), (3, 6)]
 
S

Steven D'Aprano

Nobody likes to be told the thing they're confused about is trivial.

Nobody likes to be told to brush their teeth, eat their vegetables or
clean their room. Then they grow up and learn that life is full of things
that you do because you have to, not because you want to.

Learning that some things that they are confused about are trivial is one
of those things.

It's especially bad if nobody ever actually explains what's so simple
about it. Saying it's "almost its own inverse" is just restating the
original question -- yes, that's what it is, but why? (I have put my own
interpretation in a separate reply. )

Because that's the nature of the Universe.

That's just the way the zip operation works. It is a little more
complicated, but no more mysterious, than the idea that you don't need
separate reverse() and unreverse() methods. If you reverse a list, and
then reverse it again, you get back the original order.

The only complication is that you can't just pass the result of zip back
to zip directly, you have to use the unpack operator * to split the
result "one sequence of N subsequences" into N separate arguments.

But apart from that complication, if you zip a bunch of sequences, then
zip it again, you get back the original order. You describe that as
taking the transpose of a matrix, which is correct, but that just puts a
name to it, it doesn't explain *why* taking the transpose twice gives you
the original matrix.

If you have a function that zips up items from multiple sequences, it
takes the first item from each sequence and puts them together, then does
the same thing with the second item from each sequence, the third item,
and so forth:

zip([a1, a2, a3, ...], [b1, b2, b3, ...], [c1, c2, c3, ...], ...)
=> [(a1, b1, c1, ...), (a2, b2, c2, ...), (a3, b3, c3, ...), ...]

If you take that resulting list-of-sequences, expand it to separate
arguments, and pass them to zip again:

zip((a1, b1, c1, ...), (a2, b2, c2, ...), (a3, b3, c3, ...), ...)

the zip function will zip the items up exactly in the same way, giving:

[(a1, a2, a3, ...), (b1, b2, b3, ...), (c1, c2, c3, ...), ...]

which naturally reverses the process. (Except that it returns a list of
tuples instead of a list of lists.)
 
D

Devin Jeanpierre

On Wed, 18 Jan 2012 11:20:00 -0500, Devin Jeanpierre wrote:
Nobody likes to be told to brush their teeth, eat their vegetables or
clean their room. Then they grow up and learn that life is full of things
that you do because you have to, not because you want to.

I don't tell my coworkers to brush their teeth, clean their room, and
eat their vegetables. You aren't his daddy.

Also, DBAD principle always applies. "I'm just telling you the truth"
is the defense of bullies. It's not about telling the truth, it's
about helping people. Or at least, I assume it is.
That's just the way the zip operation works.

If you can't specifically explain it, who are you to tell others it's trivial?
Because that's the nature of the Universe.

If I asked why 1*-1 has to be -1, and not 1, you could just say
"that's the nature of the universe", but that's just dodging the
question. Statements like these are theorems that can be proven. The
fact that they have been proven in the past does not magically make
them obvious. (You might think so now, but I distinctly remember a lot
of confusion in grade school when negative numbers were first
introduced. For me that confusion was never properly resolved until
much later.) The proof itself is the explanation, together with the
explanations for why the definitions make sense, and why each step in
the proof makes sense, recursively until the person who doesn't know
why this should be true is satisfied, or until the person is lost [in
which case, need to do something more bottom-up; you can't teach
somebody integration if they don't get multiplication]. If possible,
intuitive analogies should be used. (Math is specifically designed
around intuition as much as feasible.)

In the case of 1*-1 = -1, the explanation is that the definition of
"1" is that 1 * x = x, even if x is -1 [*] (this can be worded less
algebraically, since whoever asks you this is probably 7 years old).
Then one might ask, "well, why is that?", and then we must result to
the intuitive explanation of what "one" is, and how we extend that to
negative numbers (e.g. "take one step back" versus "one step forward";
"one backward step", etc.).

The point is that of _course_ it's the nature of the universe. Every
mathematical theorem follows from the definitions. That doesn't mean
it's simple, and that doesn't mean it can't be explained. As a matter
of fact it can be explained: I gave the explanation that made me "get
it". I'm sure there are other ways of wording the idea, but "that's
just the way the universe is" doesn't do anything for anyone except
make one confused person feel a little worse.
The only complication is that you can't just pass the result of zip back
to zip directly, you have to use the unpack operator * to split the
result "one sequence of N subsequences" into N separate arguments.

But apart from that complication, if you zip a bunch of sequences, then
zip it again, you get back the original order. You describe that as
taking the transpose of a matrix, which is correct, but that just puts a
name to it, it doesn't explain *why* taking the transpose twice gives you
the original matrix.

I did more than just say "nyer it's the transpose", I explained what a
transpose is and why it is that transpose is its own inverse. I think
you're right in thinking my explanation was too short, though.

Short version I already stated: the reason taking the transpose twice
results in the original matrix is because exchanges rows and columns
-- the columns of the transpose are the rows of the original.
Exchanging twice leaves you with the original (the comparison with
reverse is appropriate).

Call the transpose of A t(*A). Each row of t(*t(*A)) is a column of
t(*A), and each column of t(*A) is a row of A. so clearly every row of
t(*t(*A)) is a row of A. I assert that there are also the same number
of rows by the definition of the transpose (it only swaps, doesn't
delete), so t(*t(*A)) is the same as A! [All the rows are the same,
and there are the same number of rows <--> they are the same]

At this point I have to explain that t = zip. We could do this by
definition (ignoring that Python uses no such words), or we could do
this by explaining how the docs for zip describe transpose, or we
could say "compare it with transpose on several example matrices. The
second option turns out to pan out better than expected: here is how
Python defines zip:

This function returns a list of tuples, where the i-th tuple
contains the i-th element from each of the argument sequences or
iterables.

This is how wikipedia defines the transpose:

Formally, the (i,j) element of A-Transpose is the (j,i) element of A.

Wikipedia adds the "j" to explain that the elements in the rows are in
the same order as the respective column in the original matrix. This
is also implied in the zip definition, but not made explicitly clear.
I assert that these are equivalent for matrices based on the above
[zip includes an additional sentence addressing non-matrices], and
based on perhaps a few manual experiments to verify that order is kept
by zip().

The only issue left now is that you can pass things that aren't
matrices into zip. zip has its own way of turning them into matrices,
which results in information being cut out -- specifically, if we have
a nested-sequence that doesn't really look like a rectangle (some rows
are longer than others), it cuts it short to force it into the shape
of a rectangle, treats this nested list as a matrix, returns the
transpose of the matrix. So zip(*x) is really only almost its own
inverse -- it's precisely its own inverse for matrices.

-- Devin
 
P

Prasad, Ramit

If you understand what zip does, it should be obvious.
Nobody likes to be told to brush their teeth, eat their vegetables or clean their room. Then they grow up and learn that life is full of things that you do because you have to, not because you want to.
Learning that some things that they are confused about are trivial is one of those things.

Normally, I agree with you, but I think it's less about the truth and how it is stated.

"You are wrong, it is like..." VS. "You are a grade-A moron, it is like..."

They both teach; one just does it less offensively. What is obvious
to one person is not always obvious to everyone. :)

Ramit


Ramit Prasad | JPMorgan Chase Investment Bank | Currencies Technology
712 Main Street | Houston, TX 77002
work phone: 713- 216 - 5423

--

This email is confidential and subject to important disclaimers and
conditions including on offers for thepurchase or sale of
securities, accuracy and completeness of information, viruses,
confidentiality, legal privilege, and legal entity disclaimers,
available at http://www.jpmorgan.com/pages/disclosures/email.
 

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

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,226
Latest member
KristanTal

Latest Threads

Top