titus
titus

Reputation: 5784

Transpose list of lists

Suppose I have

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

How can I get a result like so?

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

I know how to get

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

but I must have lists as elements of the result.

Upvotes: 395

Views: 389748

Answers (15)

Puddle
Puddle

Reputation: 3335

# Simple example
array = [["a","b","c"],["1","2","3"]]
list(zip(*array)) # [('a', '1'), ('b', '2'), ('c', '3')]

# We can also zip strings since they're indexable
array = ["abc","123"]
list(zip(*array)) # [('a', '1'), ('b', '2'), ('c', '3')]

# We can zip more arrays
array = [["a","b","c"],["1","2","3"],[10,20,30]]
list(zip(*array)) # [('a', '1', 10), ('b', '2', 20), ('c', '3', 30)]

# Also zip returns tuples. Use map to convert to lists
list(map(list,zip(*array))) # [['a', '1'], ['b', '2'], ['c', '3']]

Upvotes: 0

jena
jena

Reputation: 8506

For rectangular data

(or to limit each "column" to the length of the shortest input "row")

In Python 3.x, use:

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

In Python 2.x, use:

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

There are two important things to understand here:

  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. Given 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]). See also: Expanding tuples into arguments

The rest is just making sure the result is a list of lists instead of a list of tuples, by using map to create a list from each tuple.

For jagged data

To pad shorter rows with None values in the output, import itertools (this is in the standard library), and then:

In Python 3.x, use:

list(map(list, itertools.zip_longest(*l, fillvalue=None)))

In Python 2.x, use:

list(map(list, itertools.izip_longest(*l, fillvalue=None)))

Upvotes: 561

SiggyF
SiggyF

Reputation: 23145

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

Upvotes: 85

Tim Hughes
Tim Hughes

Reputation: 3541

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

Upvotes: 0

Bob Stein
Bob Stein

Reputation: 17214

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.

Upvotes: 28

Alex
Alex

Reputation: 183

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]

Upvotes: 0

Dishant Mewada
Dishant Mewada

Reputation: 97

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)

Upvotes: 0

god
god

Reputation: 1698

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

Upvotes: 1

reza.cse08
reza.cse08

Reputation: 6178

import numpy as np
r = list(map(list, np.transpose(l)))

Upvotes: 2

matchew
matchew

Reputation: 19645

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

Upvotes: 29

jasonleonhard
jasonleonhard

Reputation: 13887

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]

Upvotes: 8

1man
1man

Reputation: 5634

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

Upvotes: -2

SolarJonathan
SolarJonathan

Reputation: 3

    #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

Upvotes: -3

footballman2399
footballman2399

Reputation: 25

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

Upvotes: 0

inspectorG4dget
inspectorG4dget

Reputation: 113955

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

Upvotes: 114

Related Questions