Python中将列表转换为最高效的方法是什么?

3

我有一个Python列表,

a=  [[1,2,3,4],
     [2,4,5,1],
     [3,4,6,2],
     [2,3,4,5]]

我希望你能将列表的横向阅读方式转变为纵向。

b=    [[1,2,3,2],
       [2,4,4,3],
       [3,5,6,4],
       [4,1,2,5]]

什么是最佳和最有效的做法?我对编程很新,抱歉我是个菜鸟。谢谢。


请参见以下有关Python矩阵转置和zip的链接:https://dev59.com/F2kw5IYBdhLWcg3wDWTL、https://dev59.com/Kk7Sa4cB1Zd3GeqP0RBs。 - georg
4个回答

10

你可以这样做:

zip(*your_list)

证明:

>>> a = [[1, 2, 3, 4], [2, 4, 5, 1], [3, 4, 6, 2], [2, 3, 4, 5]]
>>> zip(*a)
[(1, 2, 3, 2), (2, 4, 4, 3), (3, 5, 6, 4), (4, 1, 2, 5)]

1
如果你需要 list(你有元组的列表),你可以这样获取它们:map(list, a)(其中 a 已经被转置)。 - Tadeck
这个很棒。花几分钟时间理解为什么zip(*lst)可以转置lst。 :) - aga
1
为了使这个答案更有用(并且为了证明获得的点赞太容易了:),您能否提供一下这个习语的具体工作原理的解释?这里有100多个答案提到了zip(*x),但是还没有人解释过它。 - georg
还要注意,如果子列表的长度不同,则此方法无法正常工作。在这种情况下,请使用 map(None, a) - jmilloy
@jmilloy:没错,我没有证明它的效率,但基于它被广泛使用(也许不是特别广为人知),我认为这也是出于性能考虑。zip(*x)可以转置列表而不复制它或其元素,因为列表是可变的(而zip()的解包参数仍然是原始列表的元素)。然后它返回一个列表的列表(或者更确切地说是元组的列表),它们是新对象(必须是)。如果你考虑性能,那么你应该放弃使用列表并允许迭代器,例如(请参见itertools.izip()以获取其中一个示例替换)。 - Tadeck
显示剩余2条评论

8

请查看numpy库。您可以将列表放入数组中并像这样转置:

a = array ([[1,2,3,4],
       [2,4,5,1],
       [3,4,6,2],
       [2,3,4,5]])
a.transpose()

P.S.: Tadeck 的解决方案很容易理解。 zip 具有以下签名:

zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]

因此,它接受一些序列(我们不知道确切数量),然后按照以下顺序构建元组:取每个序列的第一个元素并将其放入元组中,然后取每个序列的第二个元素并将其放入第二个元组中,以此类推。它返回在执行过程中构建的所有元组的列表。

*lst 实际上是参数列表的展开。您可以在以下说明中了解更多信息。

我希望现在每个人都能理解这段漂亮的代码是如何工作的。 :)


你可以使用zip轻松进行转置...但是numpy更加强大! - Joran Beasley
@aga:感谢您对我的示例进行解释 :) 不过Numpy是一个很棒的库。 - Tadeck

5

您提到了效率。您可以使用timeit来进行测试。

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]" "zip(*a)"
1000000 loops, best of 3: 0.569 usec per loop

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]" "map(None, *a)"
1000000 loops, best of 3: 0.644 usec per loop    

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]" "[[row[i] for row in a] for i in xrange(len(a[0]))]"
1000000 loops, best of 3: 1.43 usec per loop    

>python -m timeit -s "from numpy import array; a = array([[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]])" "a.transpose()"
1000000 loops, best of 3: 0.249 usec per loop

针对一个包含 [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000 的大型数据集

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000" "zip(*a)"
10 loops, best of 3: 400 msec per loop

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000" "map(None, *a)"
10 loops, best of 3: 458 msec per loop

>python -m timeit -s "a = [[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000" "[[row[i] for row in a] for i in xrange(len(a[0]))]"
10 loops, best of 3: 770 msec per loop

>python -m timeit -s "from numpy import array; a = array([[1,2,3,4],[2,4,5,1],[3,4,6,2],[2,3,4,5]]*1000000)" "a.transpose()"
1000000 loops, best of 3: 0.251 usec per loop

如果您的列表长度不同,zip会截断为最短的长度。您可以使用'map'或itertools.izip_longest来填充缺失值,用None代替。

效率并不一定意味着最快的解决方案,通常它指的是算法。 - jamylak
@jamylak: 没错。我认为在timeit中,导入语句应该是设置的一部分。否则测试就不公平(除非任何后续的导入实际上会使用先前导入的模块,但我怀疑这种情况)。 - Tadeck
@Tadeck 正确,这需要通过为设置添加“-s”来修复。与此同时,我会发布我的结果。 - jamylak
还修复了一些时间问题,结果发现numpy要快得多。 - jamylak
谢谢大家!除了其他事情外,我在numpy中包含import非常懒惰。就效率与速度最快而言,如果这不是一个特定于编程语言的问题,我会同意。 - jmilloy
@jamylak 可能不是必要的,但你能否添加itertools.izip_longest的时间? - jmilloy

1

另一种方法是:

a=  [[1,2,3,4],
     [2,4,5,1],
     [3,4,6,2],
     [2,3,4,5]]
a = [[row[i] for row in a] for i in range(len(a[0]))]

3
[[row[i] for row in a] for i in range(len(a[i]))] 不仅是错误的(因为出现了 NameError 错误),而且很可能只是想表达 [[item for item in sublist] for sublist in a] 的意思。 - Tadeck
我实际上最喜欢这个答案。 - tipsywacky
特别是对于正在学习Python的人来说,这是一个很棒的资源。 - jmilloy

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