How to transpose a list of lists?

Question:

Let ll be a list of lists, and tt a tuple of tuples

Input: ll = [["a1","a2"],["b1","b2"],["c1","c2"]]

Desired output: tt = (("a1","b1","c1"),("a2","b2","c2"))

I have managed to solve it for a list of two-element lists, meaning that the internal list only contained two elements each.

def list_of_list_to_tuple_of_tuple(ll):
    first_elements = [i[0] for i in ll]
    second_elements = [i[1] for i in ll]
    
    new_list = []
    new_list.append(tuple(first_elements))
    new_list.append(tuple(second_elements))
    return tuple(new_list)

ll = [["a1","a2"],["b1","b2"],["c1","c2"]]
list_of_list_to_tuple_of_tuple(ll)

Now, the questions are:

  1. Is there any other method to easily accomplish what I have done?

  2. Is there any method to easily generalize this algorithm if we have a list of 3 internal lists and each internal list containing n elements? For example:

Input: ll = [["a1","a2","a3",..."an"],["b1","b2","b3",..."bn"],["c1","c2","c3",..."cn"]]

Desired Output: tt = (("a1","b1","c1"),("a2","b2","c2"),("a3","b3","c3"),...,("an","bn","cn"))

Asked By: user3586493

||

Answers:

Try this one-liner –

tuple(zip(*l))

Example 1

l = [["a1","a2"],
     ["b1","b2"],
     ["c1","c2"]]

tuple(zip(*l))
(('a1', 'b1', 'c1'), 
 ('a2', 'b2', 'c2'))

Example 2

l2 = [["a1","a2","a3","an"],
      ["b1","b2","b3","bn"],
      ["c1","c2","c3","cn"]]

tuple(zip(*l2))
(('a1', 'b1', 'c1'),
 ('a2', 'b2', 'c2'),
 ('a3', 'b3', 'c3'),
 ('an', 'bn', 'cn'))

EXPLANATION

  1. The unpacking operator allows you to unpack the list into the sublists, and passes them as individual parameters to zip, as it expects the same.
  2. The zip combines the first, second, third … nth respective elements of each sublist into n tuples object

enter image description here

  1. The tuple converts this zip object converts the overall zip object to a tuple.

Bonus

Intuitively, this operation resembles taking a transpose of a matrix. This can be seen easily if you convert your list of lists to a numpy array and then take a transpose.

import numpy as np

l = [["a1","a2"],["b1","b2"],["c1","c2"]]
arr = np.array(l)
transpose = arr.T
transpose
array([['a1', 'b1', 'c1'],
       ['a2', 'b2', 'c2']], dtype='<U2')
Answered By: Akshay Sehgal
headers = [[] for i in range(1, len(ll[0]) + 1)]
for i in ll:
    for e in range(0, len(i)):
        headers[e].append(i[e])
res = tuple(tuple(sub) for sub in headers)
print(res)

It is easy to generalise your code to more than two elements in the sublists:

ll = [["a1","a2"],["b1","b2"],["c1","c2"]]

first_elements = [x[0] for x in ll]
second_elements = [x[1] for x in ll]
...
kth_elements(k) = [x[k] for x in ll]

Now we just have to use a list comprehension to iterate on the possible values of k:

tt = [[x[k] for x in ll] for k in range(len(ll[0]))]
# [['a1', 'b1', 'c1'], ['a2', 'b2', 'c2']]

Of course you can get tt as tuples instead of lists:

tt = tuple(tuple(x[k] for x in ll) for k in range(len(ll[0])))
# (('a1', 'b1', 'c1'), ('a2', 'b2', 'c2'))

Note that python already has a builtin function to iterate on several lists simultaneously, zip:

tt = tuple(zip(*ll))
# (('a1', 'b1', 'c1'), ('a2', 'b2', 'c2'))
Answered By: Stef