Welcome, guest | Sign In | My Account | Store | Cart

Sometimes you get a list of lists and want to swap rows and columns, i.e. transpose the list. Yet, what if the rows have different lengths? Here is some advice you might find useful in such situations.

Python, 7 lines
1
2
3
4
5
6
7
def transposed(lists):
   if not lists: return []
   return map(lambda *row: list(row), *lists)

def transposed2(lists, defval=0):
   if not lists: return []
   return map(lambda *row: [elem or defval for elem in row], *lists)

Transposing a matrix is a not-so-uncommon task, and python's built-in function map makes it practically a one-liner.

The built-in function zip does a similar job, but truncates the result to the length of the shortest list, so some elements from the original data may be lost afterwards. <pre>

>>> matrix = [[1, 2, 3], [4, 5, 6]]
>>> seqbag = [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
>>> zip(*matrix)
[(1, 4), (2, 5), (3, 6)]
>>> zip(*seqbag) # result truncated
[(1, 4, 8), (2, 5, 9)]
</pre>
To prevent this, the recipe exploits a property of the map function, which, when called with more then one list, substitutes None where the list elements are exhausted. Afterwards, None elements can be easily identified and replaced by some other value. (See Python Library Documentation for details on map.)
<pre>
>>> map(None, *matrix) # works like zip with equally sized lists
[(1, 4), (2, 5), (3, 6)]
>>> map(None, *seqbag) # fills in gaps with None => no elements lost
[(1, 4, 8), (2, 5, 9), (3, 6, None), (None, 7, None)]
</pre>
Both, zip and map(None, ...) return a list of tuples - a partially immutable result. To make the result fully mutable, we can pass to map a lambda expression that takes any number of arguments, and let it convert the argument tuple (which is the original column expanded by Nones) into a list.
<pre>
>>> map(lambda *row: list(row), *seqbag)
[[1, 4, 8], [2, 5, 9], [3, 6, None], [None, 7, None]]
</pre>
Now we can replace None elements inplace with some more appropriate value, or we can enhance the lambda expression to replace Nones during the transposition process:
<pre>
>>> map(lambda *row: [elem or 0 for elem in row], *seqbag)
[[1, 4, 8], [2, 5, 9], [3, 6, 0], [0, 7, 0]]
</pre>
Note however that neither map nor zip will work for an empty list of lists - this is equivalent to calling map with only one argument and zip with no arguments at all - so we need to check this case first.

A final note: To make a regular matrix, i.e. expand shorter rows to the lengtgh of the longest row, the following expression is handy (though not the fastest one): <pre> matrix = transposed(transposed2(lists, 0)) </pre>

See Also:

Recipe "Transposing Two-Dimensional Arrays" by Steve Holden (published in the first edition of the Cookbook) describes how to transpose a list of lists that have the same length using list comprehension.

Recipe "transposing a list of lists" by Fee Thee uses the zip function like mentioned here.

Recipe "Looping through multiple lists" shows some details of map(None, *lists) for a different purpose.

Cheers and happy transposing!

Created by Zoran Isailovski on Sun, 24 Apr 2005 (PSF)
Python recipes (4591)
Zoran Isailovski's recipes (13)

Required Modules

  • (none specified)

Other Information and Tasks