转置列表的列表

377

我们来看一个例子:

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)]
14个回答

536

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 的签名: zip(*iterables) 这意味着zip期望任意数量的参数,每个参数都必须是可迭代的。 例如:zip([1, 2], [3, 4], [5, 6])
  2. 解包参数列表: 给定一个由参数args组成的序列,f(*args)将调用f,以便f中的每个元素都是args中的一个单独的位置参数。
  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])。其余的只是确保结果是一个列表的列表而不是一个元组的列表。


73
注意:如果列表l不是长度相等的(例如,某些行比其他行短),zip函数将 不会 进行补偿,而是从输出中裁剪掉相应的行。因此,l=[[1,2],[3,4],[5]] 会给出 [[1,3,5]] 的结果。 - badp
34
itertools 模块中的 zip_longest() 函数可以处理长度不同的列表。请参阅文档 - Oregano
14
回答中附上解释会更好 :) - Boris Churzin
15
我认为即使在Python 3中,list(zip(*l))也能正常工作。 - Stefano
6
@Stefano 它有效(在Python 2中zip(*l)也有效),但你得到的是一个元组列表,而不是一个列表列表。当然,list(list(it))始终等同于list(it) - Alex Shpilkin
显示剩余4条评论

105

类似于Jena的解决方案:

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

18
由于列表推导式现在比map()更受欢迎,因此这种解决方案是最符合Python精神的。 - perror
3
这应该是被接受的答案。 - Philippe Remy
美观简洁的解决方案。 - wrongbyte

83

使用NumPy转置是一种方法。对于一个列表 a:

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

或者另一个没有 zip 的版本(Python < 3):

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

或者对于Python版本>=3:

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

8
喜欢你的第二个例子--我没有意识到'map'可以这样做。这里有一个稍微改进的版本,不需要2次调用: map(lambda *a: list(a), *l) - Lee D
7
这个答案似乎更好,因为它考虑了列表不均匀的情况。 - Leon
17
map(None, ...) 在 Py3 中似乎无法正常工作。生成器被创建,但 next() 立即引发错误:TypeError: 'NoneType' object is not callable - Mad Physicist
@Lee D,请问您能否解释一下这段代码如何返回预期的数据 --> map(lambda *a: list(a), *l)。 - steve
numpy的解决方案不支持不规则列表。 - ChaimG
使用numpy解决这个问题就像用大锤子砸小坚果一样,我们不应该仅仅为了这个任务而导入numpy。 - d4tm4x

29

仅供娱乐,假定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]]

3
嗯,这需要尝试几次才能做到。好吧,其实是尝试了很多次。 - matchew
10
还不太对——这只有在维度是正方形的情况下才有效!应该是:[[j[i] for j in l] for i in range(len(l[0]))]。当然,你必须确保列表 l 不为空。 - Lee D
@LeeD 在 jena 的例子中仍然对我无效,l=[[1,2],[3,4],[5]]。 - hobs
3
@hobs这是badp针对jena提出的例子。但是我不确定我是否理解它的意思。在我看来,转置意味着一个矩形矩阵--当表示为一个列表的列表时,这意味着所有内部列表的长度必须相等。对于这个例子,你希望得到什么样的"转置"结果? - Lee D
@LeeD [[1,3,5],[2,4]]。我知道你的意思,但是稀疏矩阵的转置应该与完全填充的矩阵(或包围矩形矩阵)的转置相同。空洞应该保持为空洞,或者用NaN填充,如果需要维护低级数据结构。如果badp示例中缩短的行在中间而不是末尾,则必须使用NaN来维护序列数据结构,但如果将其转换为允许稀疏性的内容(如dictdictscipy.sparse矩阵),则不需要这样做。 - hobs
显示剩余3条评论

27

方法1和方法2适用于Python 2或3,并且适用于不规则的矩形二维列表。这意味着内部列表不需要彼此具有相同的长度(不规则),也不需要与外部列表具有相同的长度(矩形)。其他方法,嗯,比较复杂。

设置

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()成为:

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

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


方法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替代方法


方法三 — map()map()在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]]

这个非常紧凑的@SiggyF的第二个选择适用于不整齐的二维列表,与他的第一个代码不同,后者使用numpy进行转置和通过不整齐的列表。但None必须是填充值。(不,传递给内部map()的None不是填充值。它意味着没有函数来处理每一列。这些列只是通过外部map()传递,将它们从元组转换为列表。)

在Python 3的某个地方,map()已经停止容忍所有这些滥用:第一个参数不能是None,不整齐的迭代器只会被截断到最短。其他方法仍然有效,因为这仅适用于内部map()。


方法4 - map() of 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+

哎呀,在Python 3中,那些参差不齐的行并没有变成参差不齐的列,它们只是被截断了。进步真可怜。


9

三种选择:

1. 带有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)))

查看结果:

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

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

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

替代方案:np.transpose(l).tolist() - jared

1
也许不是最优雅的解决方案,但这里有一个使用嵌套 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

1

只是为了好玩:如果你想把它们全部变成字典。

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

1

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接