2

一般问题:假设您必须在循环内完成,就效率而言,是否有一种更好的方式来建立列表?例如,这些选项之一是否比构建整数列表更可取:

mylist = []

for x, y in mystuff:
  # x, y are strings that need to be
  # added sequentially to list
  mylist.extend([int(x), int(y)])

相对

for x, y in mystuff:
  mylist.append(int(x))
  mylist.append(int(y))

还是其他?如果相关,可以为此使用 scipy/numpy。谢谢。

4

3 回答 3

11

如果你需要像这样进行微优化,唯一知道什么是最快的方法就是测试。

简短的版本是:append比 快extend,而 Joran Beasley 的建议itertools.chain.from_iterable比任何一个都快——但前提是您将 替换map为列表推导式。

所以:

import itertools
import timeit

def makestuff(count):
    for i in range(count):
        yield (i, i)

def f_extend(mystuff):
    mylist = []
    for x, y in mystuff:
        mylist.extend([int(x), int(y)])
    return mylist

def f_append(mystuff):
    mylist = []
    for x, y in mystuff:
        mylist.append(int(x))
        mylist.append(int(y))
    return mylist

def f_chainmap(mystuff):
    return list(map(int, itertools.chain(*mystuff)))

def f_chaincomp(mystuff):
    return [int(x) for x in itertools.chain(*mystuff)]

def f_chainfrommap(mystuff):
    return list(map(int, itertools.chain.from_iterable(mystuff)))

def f_chainfromcomp(mystuff):
    return [int(x) for x in itertools.chain.from_iterable(mystuff)]

def f_reducecompcomp(mystuff):
    return [int(x) for x in reduce(operator.iadd, (list(y) for y in mystuff), [])]

def f_reducecompmap(mystuff):
    return [int(x) for x in reduce(operator.iadd, map(list, mystuff), [])]


try:
    import numpy
    def f_numpy(mystuff):
        return numpy.array(mystuff).flatten().tolist()
    def f_numpy2(mystuff):
        return numpy.array(list(mystuff)).flatten().tolist()
except:
    pass

if __name__ == '__main__':
  import sys
  main = sys.modules['__main__']
  count = int(sys.argv[1]) if len(sys.argv) > 1 else 10000
  for f in dir(main):
    if f.startswith('f_'):
      func = getattr(main, f)
      mystuff = makestuff(count)
      testfunc = lambda: func(mystuff)
      print('{}: {}'.format(f, timeit.timeit(testfunc, number=count)))

对于 Python 2,我尝试了map不带 extra 的版本list,速度稍快,但仍然没有竞争力。当然,对于 Python 3,这list是必要的。

以下是我的时间安排:

$ python testlister.py 1000000
f_append: 1.34638285637
f_chaincomp: 2.12710499763
f_chainfromcomp: 1.20806899071
f_chainfrommap: 2.77231812477
f_chainmap: 3.67478609085
f_extend: 1.38338398933
f_numpy: 5.52979397774
f_numpy2: 7.5826470852
f_reducecompcomp: 2.17834687233
f_reducecompmap: 3.16517782211

$ python3 ./testlister.py 1000000
f_append: 0.9949617639649659
f_chaincomp: 2.0521950440015644
f_chainfromcomp: 0.9724521590862423
f_chainfrommap: 2.5558998831082135
f_chainmap: 3.5766013460233808
f_extend: 1.149905970087275
f_reducecompcomp: 2.2112889911513776
f_reducecompmap: 1.9317334480583668

python的是苹果公司的 Python 2.7.2,而python3python.org 3.3.0,都是 64 位,都在 OS X 10.8.2 上,在 2012 年中期的 MacBook Pro 上,2.2GHz i7 和 4GB。

如果您在 POSIX 平台上使用 32 位 Python,我过去曾注意到,在不远的过去,迭代器得到了优化,似乎加快了itertools64 位构建中的许多事情,但在 32 位时减慢了它们的速度。因此,您可能会发现append在这种情况下获胜。(与往常一样,在您真正关心优化的平台上进行测试。)

Ashwini Chaudhary 链接到Flattening a shallow list in Python,进一步链接到在 python 关联列表中有效地查找元素。我怀疑我的结果和他们的结果之间的部分差异是 2.6.0 和 2.7.2/3.3.0 之间迭代器的改进,但是我们明确使用 2 元素元素而不是更大的元素这一事实可能更重要.

此外,至少有一个答案声称这reduce是最快的。原始帖子中的reduce实现都非常慢,但我能够提出更快的版本。他们仍然无法与appendor竞争chain.from_iterable,但他们在正确的范围内。

f_numpy函数是 heltonbiker 的实现。由于mystuff是 2D 迭代器,这实际上只是生成了一个包装迭代器的 0D 数组,所以所numpy能做的就是增加开销。我能够想出一个生成一维迭代器数组的实现,但这甚至更慢,因为现在所numpy能做的就是经常增加 N 次开销。我可以获得整数的二维数组的唯一方法是list先调用,如 in f_numpy2,这会使事情变得更慢。(公平地说,在其他函数中添加额外list的内容也会减慢它们的速度,但并不像使用 . 那样糟糕numpy。)

不过,我这里很可能是空白,这里有合理的使用方式numpy。当然,如果您可以确定顶层mystuff或其中的每个元素mystuff是 alist或 a tuple,您就可以编写更好的东西——如果您可以重新设计您的应用程序,让您首先拥有 2D numpy.array,而不是一般的序列序列,那将是一个完全不同的故事。但是,如果您只有序列的一般 2D 迭代,那么对于这个用例来说似乎不是很好。

于 2012-12-22T01:12:32.537 回答
2
>>> my_list = [[1,2],[3,4]]
>>> flat_list_generator = itertools.chain.from_iterable(my_list)  #flatten (note : its a generator!)
>>> map(int,flat_list_generator ) #map to int type (since OP made them ints explicitly)
[1, 2, 3, 4]

我想这就是你想要的

于 2012-12-22T00:46:05.637 回答
2

如果mystuff是对的列表,那么使用 Numpy 你可以:

result = numpy.array(mystuff, dtype=float).flatten()

或可选择将其设为列表:

result = numpy.array(mystuff, dtype=float).flatten().tolist()

根据我的定性经验,这些数组创建例程非常快。

于 2012-12-22T01:26:35.880 回答