转置列表的列表

让我们来:

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

我想要的结果是

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

而不是

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

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

解释:

要了解发生了什么,我们需要知道两件事:

  1. 邮政编码的签名:zip(*iterables)这意味着zip需要任意数量的参数,每个参数必须是可迭代的。例如zip([1, 2], [3, 4], [5, 6])
  2. 解包参数列表:给定一个参数序列argsf(*args)将调用f,这样args中的每个元素都是f的一个单独的位置参数。
  3. 如果嵌套列表的元素数量不相同(同质),itertools.zip_longest不会丢弃任何数据,而是填充较短的嵌套列表然后将它们压缩。

回到问题l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]的输入,zip(*l)将等价于zip([1, 2, 3], [4, 5, 6], [7, 8, 9])。剩下的就是确保结果是列表的列表,而不是元组的列表。

一种方法是使用NumPy转置。如需列出清单,请填写:

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

或者另一个没有拉链的:

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

等价于耶拿的解决方案:

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

只是为了好玩,有效的矩形假设m[0]存在

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

也许不是最优雅的解决方案,但这里有一个使用嵌套while循环的解决方案:

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

方法1和2适用于Python 2或3,它们适用于衣衫褴褛,矩形 2D列表。这意味着内部列表彼此之间不需要具有相同的长度(粗糙的)或与外部列表(矩形的)相同的长度。其他的方法,很复杂。

设置

import itertools
import six


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

方法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()变得

默认的fillvalue是None。感谢@jena的回答,其中map()将内部元组更改为列表。这里它将迭代器转换为列表。感谢@Oregano和@badp的评论

在Python 3中,将结果通过list()传递,以获得与方法2相同的2D列表。


方法2 -列表理解,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, '-']]

@inspectorG4dget替代


方法3 - map() - 在Python 3.6中被打破map()

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

这个非常紧凑的@SiggyF第二种选择适用于粗糙的2D列表,不像他的第一个代码使用numpy来转置和传递粗糙的列表。但是None必须是填充值。(不,传递给内部map()的None不是填充值。这意味着没有函数来处理每一列。列只是传递给外部map(),它将它们从元组转换为列表。)

在Python 3的某个地方,map()停止了所有这些滥用:第一个形参不能为None,而不规则迭代器只是被截断为最短的迭代器。其他方法仍然有效,因为这只适用于内部map()。


方法4 - map()map()重新访问

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

唉,在python3中,不规则的行不会变成不规则的列,它们只是被截断了。嘘嘘进步。

下面是一个不一定是平方的列表的转置的解决方案:

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

有三种选择:

1. Zip地图

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

2. 列表理解

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

3.For循环附加

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

查看结果:

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


# [1, 4, 7], [2, 5, 8], [3, 6, 9]
import numpy as np
r = list(map(list, np.transpose(l)))

more_itertools.unzip()易于阅读,它也适用于生成器。

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

或者同样的

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

方阵的另一种方法。不使用numpy和itertools,使用(有效的)就地元素交换。

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]

只是为了好玩:如果你想把它们都做成字典的话。

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