2009-12-09 133 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): 
    … 
相关问题