Python Deque - 10分钟的数据

时间:2014-02-13 21:07:27

标签: python performance time deque

我正在尝试编写一个脚本,该脚本在执行时会附加新的可用信息并删除超过10分钟的数据。

我想知道最有效的方法,性能方面,跟踪每个信息元素的具体时间,同时还删除10分钟以上的数据。

我的新手想法是将带有时间戳的信息 - [信息,时间] - 附加到双端队列,并在一段时间内继续评估双端队列的结束以删除超过10分钟的任何内容......我怀疑这是最好的方式。

有人能提供一个例子吗?感谢。

3 个答案:

答案 0 :(得分:3)

执行此操作的一种方法是使用按时间戳键入的排序树结构。然后你可以找到第一个元素> = 10分钟前,并在此之前删除所有内容。

使用bintrees库作为示例(因为它的键切片语法使得读取和写入非常容易......):

q = bintrees.FastRBTree.Tree()
now = datetime.datetime.now()
q[now] = 'a'
q[now - datetime.timedelta(seconds=5)] = 'b'
q[now - datetime.timedelta(seconds=10)] = 'c'
q[now - datetime.timedelta(seconds=15)] = 'd'

now = datetime.datetime.now()
del q[:now - datetime.timedelta(seconds=10)]

这将删除所有内容,但不包括now-10s,这应该是cd

这样,找到要删除的第一个元素需要log N时间,并且删除下面的N个元素应该是平均情况下的摊销日志N但是最坏情况N.所以,你的整体最坏情况时间复杂度没有改善,但是你的平均情况确实如此。

当然,管理树而不是双端队列的开销非常高,如果你处理一个非常小的队列,那么很容易高于N / log N步骤的节省。


还有其他对数数据结构映射更合适,如pqueue / heapqueue(由stdlib中的heapq实现)或时钟环;我刚刚选择了一棵红黑树,因为(使用PyPI模块)它是最容易演示的。

答案 1 :(得分:1)

如果你只是追加到最后,并且值总是按照排序的顺序排列,那么你根本不需要像树或堆那样的对数数据结构;您可以在listcollections.deque之类的任何已排序的随机访问结构中进行对数搜索。

问题是删除listdeque中任意点的所有内容需要O(N)时间。它没有理由 ;你应该能够以摊销的常数时间(使用del q[:pos]q.popleft(pos))将N个元素从deque中删除,只是collections.deque不会这样做。如果您找到或编写 具有该功能的双端队列,您可以写下:

q = deque()
now = datetime.datetime.now()
q.append((now, 'a'))
q.append((now - datetime.timedelta(seconds=5), 'b')
q.append((now - datetime.timedelta(seconds=10), 'c')
q.append((now - datetime.timedelta(seconds=15), 'd')

now = datetime.datetime.now()
pos = bisect.bisect_left(q, now - datetime.timedelta(seconds=10))
del q[:pos]

我不确定PyPI上是否存在这样的deque,但the C source to collections.deque可用于fork,Python source from PyPy,或者你可以包装C或C ++ {{ 1}}输入,或者从头开始写一个......


或者,如果您期望双端队列中的“当前”值始终是总长度的一小部分,那么您可以在O(M)时间内通过不破坏性地使用deque来实现:

deque

事实上,在这种情况下,您也可以使用q = q[pos:] ;它在右边有O(1)附加,并且从列表中删除最后M个项目的开销很小,这是一种复制M项目的方法,就像你要找到的一样。

答案 2 :(得分:0)

又一个答案,有更多限制:

如果您可以使用例如一分钟的精确度,那么您只需要10个列表。

与我的其他受限制的答案不同,这并不要求你只是附加在右边;你可以在中间追加(虽然你会在同一分钟后追上任何其他值)。

缺点是你实际上不能删除超过10分钟的所有东西;你只能删除第10个桶中的所有东西,最多可以关闭1分钟。您可以通过选择如何舍入来选择这意味着什么:

  • 以一种方式截断,没有任何东西过早掉落,但一切都迟到了,平均30秒,最差60分。
  • 以另一种方式截断,没有什么事情可以迟到,但一切都提前下降,平均30秒,最差60分。
  • 一半左右,事情在早期和晚期都会下降,但平均为0秒,最差情况为30秒。

当然,您可以使用较小的水桶,例如100个6秒间隔的水桶,而不是10分钟的1分钟间隔,以便尽可能地减少误差。推动太多远,你会破坏效率;一个600毫秒的1ms间隔的列表几乎和1M条目列表一样慢。*但是如果你需要1秒甚至50ms,那可能就好了。

这是一个简单的例子:

def prune_queue(self):
    now = time.time() // 60
    age = now - self.last_bucket_time
    if age:
        self.buckets = self.buckets[-(10-age):] + [[] for _ in range(age)]
        self.last_bucket_time = now

def enqueue(self, thing):
    self.prune_queue()
    self.buckets[-1].append(thing)

*当然你可以将它与对数数据结构结合起来 - 一个600000桶的红黑树很好。