【问题标题】:Removing elements from the list when looping over it循环遍历列表时从列表中删除元素
【发布时间】:2017-02-08 01:41:26
【问题描述】:

如果 dimension 变大,我的这部分代码将无法扩展。

我遍历我的数据并在每个 dt 时间窗口累积它们。为此,我比较了上下时间值。当我达到上限时,我打破 for 循环 以提高效率。下次我运行 for 循环 时,我不想从它的开头开始,而是从我之前停止的元素开始,以提高效率。 我该怎么做?

我试图删除/弹出列表的元素,但索引搞砸了。我读到我无法修改循环遍历的列表,但我的目标似乎并不少见,所以必须有解决方案。我不关心代码后面的原始数据列表,我只想优化我的积累。

# Here I generate data for you to show my problem
from random import randint
import numpy as np

dimension = 200
times = [randint(0, 1000) for p in range(0, dimension)]
times.sort()
values = [randint(0, dimension) for p in range(0, dimension)]
data = [(values[k], times[k]) for k in range(dimension)]
dt = 50.0
t = min(times)
pixels = []
timestamps = []

# this is my problem
while (t <= max(times)):
    accumulator = np.zeros(dimension)
    for idx, content in enumerate(data):
        # comparing lower bound of the 'time' window
        if content[1] >= t:
            # comparing upper bound of the 'time' window
            if (content[1] < t + dt):
                accumulator[content[0]] += 1
                # if I pop the first element from the list after accumulating, indexes are screwed when looping further
                # data.pop(0)
            else:
                # all further entries are bigger because they are sorted
                break

    pixels.append(accumulator)
    timestamps.append(t)
    t += dt

【问题讨论】:

  • 如果将 for 循环分解为自己的函数,则可以将循环的起始索引作为参数传递(在循环中使用 range() 代替)。然后,当您再次启动循环时,您可以从完成的索引中调用它。第一次调用函数时传递零。该参数也将是您调用 range 时使用的第一个参数。
  • 如果您想删除元素,您可以向后循环或创建列表的副本或使用列表推导。见this
  • 谢谢!我需要更多的 Python 练习,因为我没有想到 range()。我试图找到 c++ 迭代器的对应物。
  • 是的,知道这一点非常有用!由于您是 python 新手,再次鼓励您查看列表推导;对于使用简洁易读的语法快速修改列表非常有用

标签: python list numpy iteration


【解决方案1】:

以更简单的形式,我认为您正在尝试这样做:

In [158]: times=[0, 4, 6, 10]
In [159]: data=np.arange(12)
In [160]: cnt=[0 for _ in times]
In [161]: for i in range(len(times)-1):
     ...:     for d in data:
     ...:         if d>=times[i] and d<times[i+1]:
     ...:             cnt[i]+=1
     ...:             
In [162]: cnt
Out[162]: [4, 2, 4, 0]

您正试图通过在d 变得太大时打破循环并在已经计算过的项目之后开始下一个循环来提高这个data 循环的效率。

添加中断很简单:

In [163]: cnt=[0 for _ in times]
In [164]: for i in range(len(times)-1):
     ...:     for d in data:
     ...:         if d>=times[i]:
     ...:             if d<times[i+1]:
     ...:                 cnt[i]+=1
     ...:             else:
     ...:                 break

In [165]: cnt
Out[165]: [4, 2, 4, 0]

跳过计数内容的一种方法是将for d in data 替换为索引循环;并跟踪我们上次停止的位置:

In [166]: cnt=[0 for _ in times]
In [167]: start=0
     ...: for i in range(len(times)-1):
     ...:     for j in range(start,len(data)):
     ...:         d = data[j]
     ...:         if d>=times[i]:
     ...:             if d<times[i+1]:
     ...:                 cnt[i]+=1
     ...:             else:
     ...:                 start = j
     ...:                 break
     ...:                 
In [168]: cnt
Out[168]: [4, 2, 4, 0]

基于pop 的版本要求我使用列表(我的data 是一个数组),a 需要在中断处插入值

In [186]: datal=data.tolist()
In [187]: cnt=[0 for _ in times]
In [188]: for i in range(len(times)-1):
     ...:     while True:
     ...:         d = datal.pop(0)
     ...:         if d>=times[i]:
     ...:             if d<times[i+1]:
     ...:                 cnt[i]+=1
     ...:             else:
     ...:                 datal.insert(0,d)
     ...:                 break
     ...:             
In [189]: cnt
Out[189]: [4, 2, 4, 0]
In [190]: datal
Out[190]: [10, 11]

这并不完美,因为我在最后的列表中仍有项目(我的 times 不涵盖整个 data 范围)。但它测试了这个想法。

这里有一些更接近你的尝试:

In [203]: for i in range(len(times)-1):
     ...:     for d in datal[:]:
     ...:         if d>=times[i]:
     ...:             if d<times[i+1]:
     ...:                 cnt[i]+=1
     ...:                 datal.pop(0)
     ...:             else:
     ...:                 break
     ...:       

主要区别在于我迭代了datal 的副本。这样pop 会影响datal,但不会影响当前迭代。不可否认,复制是有成本的,所以速度可能会很显着。

另一种方法是循环data,并在跨越tt+dt 边界时步进time

In [222]: times=[0, 4, 6, 10,100]
In [223]: cnt=[0 for _ in times]; i=0
In [224]: for d in data:
     ...:     if d>=times[i]:
     ...:         if d<times[i+1]:
     ...:             cnt[i]+=1
     ...:         else:
     ...:             i += 1
     ...:             cnt[i]+=1
     ...:             
In [225]: cnt
Out[225]: [4, 2, 4, 2, 0]

【讨论】:

    猜你喜欢
    • 2018-07-11
    • 2013-08-05
    • 2018-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-16
    相关资源
    最近更新 更多