Transpose list of lists
Asked Answered
S

14

384

Let's take:

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

The result I'm looking for is

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

and not

r = [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Sangsanger answered 24/6, 2011 at 20:54 Comment(0)
O
547

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:

There are two things we need to know to understand what's going on:

  1. The signature of zip: zip(*iterables) This means zip expects an arbitrary number of arguments each of which must be iterable. E.g. zip([1, 2], [3, 4], [5, 6]).
  2. Unpacked argument lists: Given a sequence of arguments args, f(*args) will call f such that each element in args is a separate positional argument of f.
  3. itertools.zip_longest does not discard any data if the number of elements of the nested lists are not the same (homogenous), and instead fills in the shorter nested lists then zips them up.

Coming back to the input from the question l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], zip(*l) would be equivalent to zip([1, 2, 3], [4, 5, 6], [7, 8, 9]). The rest is just making sure the result is a list of lists instead of a list of tuples.

Obidiah answered 24/6, 2011 at 20:59 Comment(9)
Beware: if l is not evenly sized (say, some rows are shorter than others), zip will not compensate for it and instead chop away rows from the output. So l=[[1,2],[3,4],[5]] gives you [[1,3,5]].Bethought
The itertools function zip_longest() works with uneven lists. See DOCSJusticiable
An explanation in answer would be nice :)Emmettemmey
I think even list(zip(*l)) works correctly in Python 3.Adowa
@Adowa It works (as does zip(*l) in Python 2), but you get a list of tuples, not a list of lists. Of course, list(list(it)) is always the same thing as list(it).Photocurrent
Simpler version that worked for me is. list(itertools.zip_longest(*data)) the * modifier effectively converts zip to extract, reversing the operation of zip..Mete
I don't know if this worked back then, but a simpler way would be list(zip(*l))Foothill
@JimJam This works, but it doesn't fulfill the requirements. OP wanted a list of lists, not a list of tuples. Hence map(list, ...) :)Obidiah
@BorisChurzin I finally added an explanation to my answer :) Apologies for taking so long.Obidiah
B
108

Equivalently to Jena's solution:

>>> 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]]
Belaud answered 24/6, 2011 at 21:2 Comment(1)
As list comprehension is now preferred to map(), this solution is the one which is the most in the Python spirit...Bobby
S
85

One way to do it is with NumPy transpose. For a list, a:

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

Or another one without zip (python < 3):

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

Or for python >= 3:

>>> list(map(lambda *x: list(x), *l))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Spurling answered 24/6, 2011 at 20:59 Comment(6)
Love your second one -- I didn't realize map could do that. Here's a slight refinement that doesn't require 2 calls, though: map(lambda *a: list(a), *l)Endocentric
Shouldnt this be a better answer as it takes care of uneven lists?Syzran
map(None, ...) doesn't seem to work for Py3. The generator is created but next() raises an error immediately: TypeError: 'NoneType' object is not callable.Reliance
@Lee D please can you explain how the code returns expected data --> map(lambda *a: list(a), *l)Mechanism
The numpy solution does not do jagged lists.Belamy
The numpy solution is using a sledgehammer to crack a nut. One should not import numpy just for this task.Estellaestelle
E
29

just for fun, valid rectangles and assuming that m[0] exists

>>> 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]]
Egocentric answered 24/6, 2011 at 21:6 Comment(8)
this is what I was looking for and couldn't get my head around. Still @jena's solution is really shortSangsanger
Yeah, it took a few tires to get this right. Okay, many tries.Egocentric
Still isn't quite right -- this only happens to work when the dimensions are square! It should be: [[j[i] for j in l] for i in range(len(l[0]))]. Of course, you have to be sure that list l is not empty.Endocentric
@LeeD still doesn't work for me on jena's example l=[[1,2],[3,4],[5]]Outface
@Outface That was badp's example, responding to jena. However, I'm not sure it makes sense to me. IMO, transposition implies a rectangular matrix -- when represented as a list of lists, that means all the internal lists must be the same length. What result would you want as the "transposition" of this example?Endocentric
@LeeD [[1,3,5],[2,4]]. I hear you, but transposition of a sparse matrix should be the same as transposition of a fully-populated matrix (or the enveloping rectangular matrix). The holes should remain holes, or filled with NaNs, if required to maintain the low level data structure. If the foreshortened row for badp's example were in the middle instead of the end, you'd have to use NaNs to maintain the sequence of sequences data structure, but not if you converted it to something that allows sparesness (like a dict of dicts or scipy.sparse matrix).Outface
@badp, perhaps you could pad your matrix with Nones or NaNs so that jena's approach would work.Outface
matchew Please change that len(l) to len(l[0]) as @LeeD suggests.Mull
M
28

Methods 1 and 2 work in Python 2 or 3, and they work on ragged, rectangular 2D lists. That means the inner lists do not need to have the same lengths as each other (ragged) or as the outer lists (rectangular). The other methods, well, it's complicated.

the setup

import itertools
import six

list_list = [[1,2,3], [4,5,6, 6.1, 6.2, 6.3], [7,8,9]]

method 1 — map(), zip_longest()

>>> 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

The default fillvalue is None. Thanks to @jena's answer, where map() is changing the inner tuples to lists. Here it is turning iterators into lists. Thanks to @Oregano's and @badp's comments.

In Python 3, pass the result through list() to get the same 2D list as method 2.


method 2 — list comprehension, zip_longest()

>>> [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.


method 3 — map() of map()broken in Python 3.6

>>> 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]]

This extraordinarily compact @SiggyF second alternative works with ragged 2D lists, unlike his first code which uses numpy to transpose and pass through ragged lists. But None has to be the fill value. (No, the None passed to the inner map() is not the fill value. It means there is no function to process each column. The columns are just passed through to the outer map() which converts them from tuples to lists.)

Somewhere in Python 3, map() stopped putting up with all this abuse: the first parameter cannot be None, and ragged iterators are just truncated to the shortest. The other methods still work because this only applies to the inner map().


method 4 — map() of map() revisited

>>> 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+

Alas the ragged rows do NOT become ragged columns in Python 3, they are just truncated. Boo hoo progress.

Mull answered 30/1, 2016 at 9:52 Comment(0)
P
9

Three options to choose from:

1. Map with Zip

solution1 = map(list, zip(*l))

2. List Comprehension

solution2 = [list(i) for i in zip(*l)]

3. For Loop Appending

solution3 = []
for i in zip(*l):
    solution3.append((list(i)))

And to view the results:

print(*solution1)
print(*solution2)
print(*solution3)

# [1, 4, 7], [2, 5, 8], [3, 6, 9]
Proterozoic answered 5/9, 2017 at 6:14 Comment(0)
B
2
import numpy as np
r = list(map(list, np.transpose(l)))
Brethren answered 15/4, 2018 at 18:31 Comment(1)
Alternative: np.transpose(l).tolist().Nepotism
R
1

Maybe not the most elegant solution, but here's a solution using nested while loops:

def transpose(lst):
    newlist = []
    i = 0
    while i < len(lst):
        j = 0
        colvec = []
        while j < len(lst):
            colvec.append(lst[j][i])
            j = j + 1
        newlist.append(colvec)
        i = i + 1
    return newlist
Richburg answered 30/4, 2015 at 16:4 Comment(0)
Q
1

more_itertools.unzip() is easy to read, and it also works with generators.

import more_itertools
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
r = more_itertools.unzip(l) # a tuple of generators.
r = list(map(list, r))      # a list of lists

or equivalently

import more_itertools
l = more_itertools.chunked(range(1,10), 3)
r = more_itertools.unzip(l) # a tuple of generators.
r = list(map(list, r))      # a list of lists
Quillan answered 18/5, 2020 at 12:39 Comment(0)
E
1
matrix = [[1,2,3],
          [1,2,3],
          [1,2,3],
          [1,2,3],
          [1,2,3],
          [1,2,3],
          [1,2,3]]
    
rows = len(matrix)
cols = len(matrix[0])

transposed = []
while len(transposed) < cols:
    transposed.append([])
    while len(transposed[-1]) < rows:
        transposed[-1].append(0)

for i in range(rows):
    for j in range(cols):
        transposed[j][i] = matrix[i][j]

for i in transposed:
    print(i)
Emendate answered 20/10, 2020 at 21:28 Comment(0)
S
1

One more way for square matrix. No numpy, nor itertools, use (effective) in-place elements exchange.

def transpose(m):
    for i in range(1, len(m)):
        for j in range(i):
            m[i][j], m[j][i] = m[j][i], m[i][j]
Spectrograph answered 4/1, 2021 at 11:0 Comment(0)
M
1

Just for fun: If you then want to make them all into dicts.

In [1]: l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
   ...: fruits = ["Apple", "Pear", "Peach",]
   ...: [dict(zip(fruits, j)) for j in [list(i) for i in zip(*l)]]
Out[1]:
[{'Apple': 1, 'Pear': 4, 'Peach': 7},
 {'Apple': 2, 'Pear': 5, 'Peach': 8},
 {'Apple': 3, 'Pear': 6, 'Peach': 9}]
Madra answered 27/4, 2022 at 12:51 Comment(0)
P
-1

Here is a solution for transposing a list of lists that is not necessarily square:

maxCol = len(l[0])
for row in l:
    rowLength = len(row)
    if rowLength > maxCol:
        maxCol = rowLength
lTrans = []
for colIndex in range(maxCol):
    lTrans.append([])
    for row in l:
        if colIndex < len(row):
            lTrans[colIndex].append(row[colIndex])
Parisi answered 7/8, 2016 at 14:54 Comment(0)
S
-2
    #Import functions from library
    from numpy import size, array
    #Transpose a 2D list
    def transpose_list_2d(list_in_mat):
        list_out_mat = []
        array_in_mat = array(list_in_mat)
        array_out_mat = array_in_mat.T
        nb_lines = size(array_out_mat, 0)
        for i_line_out in range(0, nb_lines):
            array_out_line = array_out_mat[i_line_out]
            list_out_line = list(array_out_line)
            list_out_mat.append(list_out_line)
        return list_out_mat
Spavined answered 16/7, 2015 at 0:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.