2010-04-29 66 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的要求。