2009-12-09 131 views
3

在for循環中訪問以前迭代中的值的最好和最快的方法是什麼?假定對象將非常大(例如,一個遊標對象有超過10萬條記錄)使用訪問循環中最後一次迭代的值的最佳方式

一個簡單的例子:

tmp = [ 
     ['xyz', 335], ['zzz', 338], ['yyy', 339], ['yyy', 442], 
     ['abc', 443], ['efg', 444], ['ttt', 446], ['fff', 447] 
     ] 

for x in tmp: 
    if not prev: 
    prev = x[1] 
    print 'seq: ', x[1], 'prev seq:', prev, 'variance: ', x[1]-prev 
    prev = x[1] 

這是處理這一最優化的方式?

基於響應下面我做了一些測試: TMP與500個列表創建,運行它20倍的平均如下所示。

結果:

礦業:0623
戴夫snippet1:0605
戴夫snippet2:0586
Catchmeifyoutry(編輯代碼):0707

+1

另一個注意:這些類型的循環可能會受益於使用真正容易使用Python的JIT編譯器:http://psyco.sourceforge.net/ – catchmeifyoutry 2009-12-09 22:23:19

+0

有趣的是,在WinXP上網本(yeah)上使用python 2.5,沒有psyco我的izip解決方案比解決方案快,但比Dave慢(使用範圍( 100000)),但使用psyco,您的解決方案要快得多,然後是Dave's,然後是我的解決方案。 – catchmeifyoutry 2009-12-09 22:51:50

回答

3

您的代碼將被做「,如果不是prev「每次循環測試,即使它只適用於第一個元素。 另外你的代碼似乎打破了我 - 第一次循環prev和當前值是相同的。

我會做這樣的,假設有至少一個元素:

tmp_iter = iter(tmp) 
[_, prev] = tmp_iter.next() 

for [_, x] in tmp_iter: 
    print 'seq: ', x, 'prev seq:', prev, 'variance: ', x-prev 
    prev = x 

我使用賦值:

tmp_iter = iter(tmp) 
prev = tmp_iter.next() 

for x in tmp_iter: 
    print 'seq: ', x[1], 'prev seq:', prev[1], 'variance: ', x[1]-prev[1] 
    prev = x 

這可以通過擺脫索引的進一步優化將列表吐出到其組成部分,並將第一個元素分配給_因爲它沒有被使用。

+0

Dave感謝您的快速響應,實際上代碼是正確的,在第一次迭代中,變化實際上應該爲0,即沒有任何變化。 進一步優化如? – ismail 2009-12-09 20:37:03

+0

伊西,代碼已經可以。 – 2009-12-09 20:49:40

+0

在這裏第二個選項似乎最快將更新我的問題與平均結果 – ismail 2009-12-09 22:01:51

0

此代碼生成NameError,因爲在if not prev處,prev未定義。在循環之前將其設置爲False或None。您還可以做出不同的循環:

for i in xrange(1, len(tmp)): 
    print 'seq: {0}, prev seq: {1}, variance: {2}'.format(tmp[i][1], tmp[i - 1][1], tmp[i] - tmp[i - 1][1]) 

如果您將使用超過10萬條記錄,瓶頸將是不循環,而是由應用程序使用的內存。不要以這種格式存儲所有數據:每對值(列表)will eat 100+ bytes。如果他們是在一個文件中,這是更好地遍歷它的臺詞:

(假設數據是製表符分隔)

def reader(filename): 
    with open(filename) as f: 
     prev = f.next() 
     for l in f: 
      l = l.split('\t') 
      yield (prev, l) 
      prev = l 

for (prev, curr) in reader(myfile): 
    print 'seq: {0}, prev seq: {1}, variance: {2}'.format(curr[1], prev[1], curr[1] - prev[1]) 

readergenerator,它從一個序列多次返回值。這樣,任何時候只有2行數據將被存儲在內存中,並且您的應用程序將持續數百萬行。

爲了使代碼可讀,我把它放在一邊,以便在程序體中處理數據序列,而不必關心它是如何組成的。

+0

嗨culebron,是在我的代碼我確實設置prev =無(但沒有包括在這裏) – ismail 2009-12-09 20:38:39

4

只是迭代對,使用zip(),這是更具可讀性。

UPDATE: for python 2。x,請使用itertools.izip,因爲它更高效!

from itertools import izip 
for prev, next in izip(tmp, tmp[1:]): 
    print 'seq: ', next[1], 'prev seq:', prev[1], 'variance: ', next[1]-prev[1] 

也可以使用值拆包,以避免指數:

for (_, prev), (_, next) in izip(tmp, tmp[1:]): 
    print 'seq: ', next, 'prev seq:', prev, 'variance: ', next-prev 

或者,如果你真的需要在第一次迭代太

for prev, next in izip(tmp, tmp[:1] + tmp): 
    print 'seq: ', next[1], 'prev seq:', prev[1], 'variance: ', next[1]-prev[1] 

編輯

如果你想避免在第二個參數中創建一個列表還使用一個明確的迭代器:

itr = iter(tmp) 
itr.next() # here I assume tmp is not empty, otherwise an exception will be thrown 
for prev, next in izip(tmp, itr): 
    print 'seq: ', next[1], 'prev seq:', prev[1], 'variance: ', next[1]-prev[1] 

:此壓縮模式是類似的問題有用的。 例如提取從列表中連續的三胞胎:

xs = range(9) 
triplets = zip(xs[::3], xs[1::3], xs[2::3]) # python 2.x, zip returns a list 

print xs  # [0, 1, 2, 3, 4, 5, 6, 7, 8] 
print triplets # [(0, 1, 2), (3, 4, 5), (6, 7, 8)] 

還要注意的是,在python 3 zip returns an iterator,類似itertools.izip

+0

謝謝你的迴應,剛剛測試過代碼),它似乎是所有選項中最慢的(甚至比我上面的原始代碼慢) – ismail 2009-12-09 22:01:09

+0

如上所述,這可能是因爲zip在python 2.x中在內存中構建了一個完整列表。無論如何,在這種情況下,你應該使用一個明確的循環。太糟糕了,恕我直言,這是最佳的解決方案(最佳的「理想pythonic方式」意義上)。 祝你好運! – catchmeifyoutry 2009-12-09 22:08:45

+0

SCRAP這個,python 2.x有'itertools.izip':p,請再次註明時間 – catchmeifyoutry 2009-12-09 22:17:25

0
it = imap(operator.itemgetter(1), tmp) # get all 2nd items 
prev = next(it, None) # get 1st element (doesn't throw exception for empty `tmp`) 
for x in it: 
    print 'seq: %s prev seq: %s variance: %s' % (x, prev, x-prev) 
    prev = x 
+0

如果我可能會返回優惠:'prev in:break'現在寫成'next(it,None)':) – tzot 2009-12-10 00:35:07

+0

@ΤΖΩΤΖΙΟΥ:謝謝。既然Python 2.6'next(it,None)'就是這樣。 – jfs 2009-12-10 18:18:08

2

使用itertools

from itertools import izip, islice 
for prev, cur in izip(l, islice(l, 1, None)): 
    print 'seq:', cur[1], 'prev seq:', prev[1], 'delta:', cur[1]-prev[1] 

對於在問題中給出的特定示例中,注意的是,如果數字 可以使用32位整數來表示,並且號碼列表配合到 內存的計算差異最快的方法之一將是 使用numpy

import numpy 
a = numpy.array([x[1] for x in tmp]) 
delta = numpy.diff(a) 
1

Guido的時間機器來拯救!

itertools recipes頁:

import itertools 
def pairwise(iterable): 
    "s -> (s0,s1), (s1,s2), (s2, s3), ..." 
    a, b = itertools.tee(iterable) 
    next(b, None) 
    return itertools.izip(a, b) 

這應該是最合適的方法(考慮迭代是(random.randint(100) for x in xrange(1000));這裏iter(iterable); next(iterable)作爲輔助迭代器可能無法提供正確的功能

在使用它的。循環爲:

for prev_item, item in pairwise(iterable): 
    … 
相關問題