2010-04-29 12 views
12

我一直在尝试应用一种算法,以基于特定标准将python列表缩小为较小的列表。由于大量的原单,在100K元素的顺序,我试图itertools为避免多次内存分配,所以我想出了这个:itertools.islice与列表片段比较

reducedVec = [ 'F' if sum(1 for x in islice(vec, i, i+ratio) if x == 'F') 
         > ratio/3.0 else 'T' 
       for i in xrange(0, len(vec), ratio) ] 

执行时间为这需要一个令人担忧的长时间几分钟的顺序,当vec有大约10万个元素时。当我试图代替:

reducedVec = [ 'F' if sum(1 for x in vec[i:i+ratio] if x == 'F') 
         > ratio/3.0 else 'T' 
       for i in xrange(0, len(vec), ratio) ] 

在本质上与切片执行是瞬间取代islice。

你能想出一个合理的解释吗?我会想,避免重复分配一个新的列表与大量的元素,实际上会节省我几个计算周期,而不是削弱整个执行。

干杯, 忒弥斯

+1

可阅读有关使用''vec.count(“F”是什么,我,我+比值)''而不是'sum'(如果x =='F')'',vec [i:i +比率]在我看来,它更具可读性,可能也更快。 – 2011-11-24 11:26:11

回答

13

islice适用于任意可迭代。要做到这一点,而不是直接跳到第n个元素,它必须迭代第一个n-1,扔掉它们,然后产生你想要的。

退房从itertools documentation纯Python实现:

def islice(iterable, *args): 
    # islice('ABCDEFG', 2) --> A B 
    # islice('ABCDEFG', 2, 4) --> C D 
    # islice('ABCDEFG', 2, None) --> C D E F G 
    # islice('ABCDEFG', 0, None, 2) --> A C E G 
    s = slice(*args) 
    it = iter(xrange(s.start or 0, s.stop or sys.maxint, s.step or 1)) 
    nexti = next(it) 
    for i, element in enumerate(iterable): 
     if i == nexti: 
      yield element 
      nexti = next(it) 

迭代工具文档的说,如果我试图做到这一点的操作,我可能会使用的grouper配方。它实际上并不会为你节省任何记忆,但如果你把它改写成更懒,这可能不会太难。

from __future__ import division 

from itertools import izip_longest 
def grouper(n, iterable, fillvalue=None): 
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" 
    args = [iter(iterable)] * n 
    return izip_longest(fillvalue=fillvalue, *args) 

reducedVec = [] 
for chunk in grouper(ratio, vec): 
    if sum(1 for x in chunk if x == 'F') > ratio/3: 
     reducedVec.append('F') 
    else: 
     reducedVec.append('T') 

我喜欢用grouper抽象出连续切片,找到这段代码容易得多比原来

+0

ouch我现在看到了,谢谢 – Themis 2010-04-29 16:02:51

+0

石斑鱼确实是一个方便的功能,使事情更具可读性 – Themis 2010-04-29 16:33:04

1

我的猜测是,使用islice()涉及一个Python函数调用的vec每一个元素,而扩展切片符号被分析器理解并直接转换到CPython的要求。