【问题标题】:why list comprehension beats zip in transposing matrix为什么列表理解在转置矩阵中胜过 zip
【发布时间】:2012-12-05 10:46:24
【问题描述】:

最近我正在使用 python 来处理几百万个大小的列表。

这里我有一个列表E,它有1,470,000个元素,每个元素是一个包含2个整数的列表。

E 看起来像:[[1, 3], [2, 4], [4, 7] ... ]

我想分别获取第一列和第二列的最大数量。

我可以通过使用列表推导得到它

m1 = max([e[0] for e in E])
m2 = max([e[1] for e in E])
return (m1, m2)

另一种方法是使用zip:

list(map(max, zip(*E)))

起初我认为第二种方式应该更快,因为列表理解将构建一个大列表(更重要的是,两次)。但事实证明列表理解非常快,使用 zip 的方法慢了大约 10 ~ 20 倍(使用 cProfile)。

我认为 zip 不应该那么慢,(更何况,列表理解怎么会那么快?)谁能告诉我原因?

我正在使用 python 3.2

附:通过使用 Windows 任务管理器,我什至看不到任何内存跟踪表明 python 曾经创建过一个新列表。一定有黑魔法。

【问题讨论】:

  • 我不知道 Python 是否需要以特定顺序评估元组的元素,但如果不需要(并且您使用的是 Python 2.x)那么我认为是未定义的行为...e 在两个列表推导中是同一个变量。
  • 如果您正在处理这样的事情 - 您是否考虑过使用 numpy...? >>> a = np.array([ [1, 2], [3, 4], [5, 6] ]) >>> a.max(axis=0) array([5, 6])
  • @Mehrdad 对不起,我的示例代码令人困惑。但是即使我完全分开计算两个最大值,它仍然会快得多,我无法理解。

标签: python performance zip list-comprehension


【解决方案1】:

我已经使用 Python 2.7.3 和 3.3.0 测试了多种方法,但我无法重现您的结果。

以下时序来自 Python 2.7.3(3.3.0 的结果类似):

In [31]: E = [(random.randrange(0,1000),random.randrange(0,1000)) for _ in range(1470000)]

In [32]: %timeit max([e[0] for e in E]), max([e[1] for e in E])
1 loops, best of 3: 319 ms per loop

In [33]: %timeit max(e[0] for e in E), max(e[1] for e in E)
1 loops, best of 3: 343 ms per loop

In [36]: %timeit max(E, key=operator.itemgetter(0)), max(E, key=operator.itemgetter(1))
1 loops, best of 3: 314 ms per loop

In [38]: %timeit list(map(max, zip(*E)))
1 loops, best of 3: 307 ms per loop

我测试过的所有方法的性能都差不多。

如果你关心性能,你应该考虑使用 NumPy:

In [39]: import numpy as np

In [40]: EE = np.array(E)

In [46]: %timeit EE.max(axis=0)
100 loops, best of 3: 3.21 ms per loop

如您所见,在这个数据集上,numpy.max() 比我尝试过的任何纯 Python 方法快大约 100 倍。

【讨论】:

  • +1 用于建议 numpy。但是在这种情况下也应该考虑创建numpy数组的时间。如果后面有更多的计算,可能值得将列表转换为数组,否则如果数组从一开始就不是一个 numpy 数组,它可能太昂贵了。
  • @mata:你是对的。显然不值得将 list 转换为 ndarray 仅用于此计算。
猜你喜欢
  • 2014-07-31
  • 2022-10-07
  • 2015-08-26
  • 1970-01-01
  • 2011-02-04
  • 2017-01-03
  • 1970-01-01
  • 2010-12-03
  • 1970-01-01
相关资源
最近更新 更多