繁体   English   中英

从循环中的最后一次迭代访问值的最佳方式

[英]Optimal way to access a value from the last iteration in a loop

假设对象非常大(例如,具有100,000多条记录的游标对象),在for循环中访问上一次迭代中的值的最佳和最快方法是什么?

使用一个简单的例子:

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次的平均值如下所示。

结果:

地雷:0,623
戴夫片段1:0,605
戴夫片段2:0,586
Catchmeifyoutry(编辑代码):0,707

只需使用zip()迭代对,这更易读。

更新:对于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]

注意 :此zip模式在类似问题中也很有用。 例如,从列表中提取连续的三元组:

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返回一个迭代器 ,类似于itertools.izip

您的代码将在每次循环循环时执行“if not prev”测试,即使它仅适用于第一个元素。 你的代码对我来说似乎也没有了 - 第一次围绕循环,prev和current值是相同的。

我会这样做,假设至少有一个元素:

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

这可以通过摆脱索引进一步优化:

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

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

我使用赋值将列表吐入其组成部分,并将第一个元素分配给_,因为它未被使用。

使用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)

Guido的时间机器来救援!

itertools食谱页面:

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)

这应该是最合适的方法(考虑到iterable是(random.randint(100) for x in xrange(1000)) ;这里iter(iterable); next(iterable)作为辅助迭代器可能无法提供正确的功能。

在循环中使用它:

for prev_item, item in pairwise(iterable):
    …

此代码生成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])

如果你将使用100,000多条记录,那么瓶颈将不是循环,而是应用程序使用的内存。 不要以这种格式存储所有数据:每对值(列表) 将占用100多个字节 如果它们在一个文件中,最好迭代它的行:

(假设数据以制表符分隔)

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])

reader是一个生成器 ,它会多次返回序列中的值。 这样,任何时候内存中只会存储2行数据,您的应用程序将支持数百万行。

为了使代码可读,我把它放在一边,以便在程序体中我们处理数据序列,而不关心它是如何组成的。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM