--- layout: default title: Zip in Python comments: "yes" math: "yes" license: "CC-BY" tags: math, python, computer-science --- In mathematics, one can have a function $f: X \times Y\to Z$ and write $f(x,y) = z$, where $x\in X, y\in Y, z\in Z$. Some books take care to remark that since $X \times Y$ is the domain and $(x,y)\in X\times Y$, one should in fact write $f((x,y))=z$. In Python, this distinction is accomplished using the star (`*`) in function calls. So for example if one had a sequence of parameters $(a_i)_{i=0}^n$, then one can write $$ f(\star(a_i)_{i=0}^n) = f(a_{0}, \ldots, a_n) $$ Now, the built-in function `zip` in Python takes the $i$th element from each iterable in some sequence, and forms a tuple for each $i$, then places all these tuples in one final list. Concretely, suppose we are given some sequence $x = ((x_{i,j})_{j={0}}^m)_{i={0}}^n$. Incidentally, this sequence can be visualized as an $(n+1)\times (m+1)$ matrix: $$ \begin{pmatrix} (x_{0,0}, \ldots, x_{0,m}) \\ \vdots \\ (x_{n,0}, \ldots, x_{n,m}) \end{pmatrix} $$ Now let us examine what $\mathrm{zip}(\star x)$ means. The star unpacks the first layer of the sequence, leaving us with $$ \mathrm{zip}((x_{0,0}, \ldots, x_{0,m}), \ldots, (x_{n,0}, \ldots, x_{n,m})) $$ Now, $\mathrm{zip}$ will first take the $0$th element from each argument, forming the sequence $(x_{0,0},\ldots,x_{n,0})$; then it will take the elements in position $1$ from each argument, forming the sequence $(x_{0,1}, \ldots, x_{n,1})$; this process continues until we reach the sequence $(x_{0,m},\ldots,x_{n,m})$. Finally, $\mathrm{zip}$ will place everything in a sequence of its own, and we obtain a new matrix: $$ \begin{pmatrix} (x_{0,0},\ldots,x_{n,0}) \\ \vdots \\ (x_{0,m},\ldots,x_{n,m}) \end{pmatrix} $$ In other words, everything that was horizontal is now vertical, and the converse holds as well---that is, $\mathrm{zip}$ maps each matrix $x$ to its [transpose] $x^T$ (or, more specifically, $\mathrm{zip}$ maps each matrix $x = (w_0, \ldots, w_n)^T$ with row-vectors $w_0, \ldots, w_n$ to the matrix $(w_0^T, \ldots, w_n^T)$, and then forms row-vectors in the new matrix). If we decide to repeat this operation, we get the original matrix back. In other words, $x = \mathrm{zip}(\star\mathrm{zip}(\star x))$. [transpose]: https://en.wikipedia.org/wiki/Transpose To give an example of this identity in Python: ``` python >>> x = (1, 2, 3) >>> y = (4, 5, 6) >>> lst = [x, y] >>> lst == zip(*zip(*lst)) True ```