Coder Perfect

List of Lists Transpose

Problem

Let’s take:

l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

I’m hoping for the following outcome:

r = [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

and not

r = [(1, 4, 7), (2, 5, 8), (3, 6, 9)]

Much appreciated

Asked by titus

Solution #1

Python 3:

# short circuits at shortest nested list if table is jagged:
list(map(list, zip(*l)))

# discards no data if jagged and fills short nested lists with None
list(map(list, itertools.zip_longest(*l, fillvalue=None)))

Python 2:

map(list, zip(*l))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Explanation:

To comprehend what’s going on, we need to know two things:

Returning to the question’s input, zip(*l) would be identical to zip([1, 2, 3], [4, 5, 6], [7, 8, 9]]. ([1, 2, 3], [4, 5, 6], [7, 8, 9]). The remainder is simply ensuring that the output is a list of lists rather than a list of tuples.

Answered by jena

Solution #2

NumPy transpose is one way to do it. To make a list, start with:

>>> import numpy as np
>>> np.array(a).T.tolist()
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Another option is to go without the zip:

>>> map(list,map(None,*a))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Answered by SiggyF

Solution #3

Jena’s solution is equivalent:

>>> l=[[1,2,3],[4,5,6],[7,8,9]]
>>> [list(i) for i in zip(*l)]
... [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Answered by inspectorG4dget

Solution #4

Valid rectangles and the assumption that m[0] exists are simply for fun.

>>> m = [[1,2,3],[4,5,6],[7,8,9]]
>>> [[row[i] for row in m] for i in range(len(m[0]))]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Answered by matchew

Solution #5

Methods 1 and 2 operate on ragged, rectangular 2D lists in Python 2 or 3. That is to say, the inner lists do not have to be the same length as each other (ragged) or the outer lists (rectangular). The other ways, on the other hand, are a bit more involved.

import itertools
import six

list_list = [[1,2,3], [4,5,6, 6.1, 6.2, 6.3], [7,8,9]]
>>> list(map(list, six.moves.zip_longest(*list_list, fillvalue='-')))
[[1, 4, 7], [2, 5, 8], [3, 6, 9], ['-', 6.1, '-'], ['-', 6.2, '-'], ['-', 6.3, '-']]

six.moves.zip_longest() becomes

None is the default fillvalue. Thanks to @jena’s response, the inner tuples are now converted to lists using map(). It is converting iterators into lists in this case. Thanks to @Oregano and @badp for their input.

To get the same 2D list as method 2, feed the result through list() in Python 3.

>>> [list(row) for row in six.moves.zip_longest(*list_list, fillvalue='-')]
[[1, 4, 7], [2, 5, 8], [3, 6, 9], ['-', 6.1, '-'], ['-', 6.2, '-'], ['-', 6.3, '-']]

The @inspectorG4dget alternative.

>>> map(list, map(None, *list_list))
[[1, 4, 7], [2, 5, 8], [3, 6, 9], [None, 6.1, None], [None, 6.2, None], [None, 6.3, None]]

In contrast to his previous method, which utilizes numpy to transpose and pass through ragged lists, @SiggyF’s second alternative works with ragged 2D lists. However, None must be the fill value. (The None value provided to the inner map() function is not the fill value.) It indicates that no function exists to process each column. The columns are simply supplied to the outer map() function, which turns tuples to lists.)

Map() started tolerating all of this exploitation somewhere around Python 3: the first parameter can’t be None, and ragged iterators are just shortened to the shortest. Because this just pertains to the inner map, the other ways continue to work ().

>>> list(map(list, map(lambda *args: args, *list_list)))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]   // Python 2.7
[[1, 4, 7], [2, 5, 8], [3, 6, 9], [None, 6.1, None], [None, 6.2, None], [None, 6.3, None]] // 3.6+

Unfortunately, in Python 3, ragged rows do not produce ragged columns; instead, they are truncated. Progress is a bummer.

Answered by Bob Stein

Post is based on https://stackoverflow.com/questions/6473679/transpose-list-of-lists