高效的循环缓冲区?
我想在 python 中创建一个高效的循环缓冲区(目标是取缓冲区中整数值的平均值)。
这是使用列表收集值的有效方法吗?
def add_to_buffer( self, num ):
self.mylist.pop( 0 )
self.mylist.append( num )
什么会更有效(以及为什么)?
I want to create an efficient circular buffer in python (with the goal of taking averages of the integer values in the buffer).
Is this an efficient way to use a list to collect values?
def add_to_buffer( self, num ):
self.mylist.pop( 0 )
self.mylist.append( num )
What would be more efficient (and why)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
我将使用
collections.deque
与maxlen
arg中有一个食谱
deque
的文档与您想要的类似。我断言它是最有效的,完全基于这样一个事实:它是由一个非常熟练的团队用 C 语言实现的,他们习惯于编写出一流的代码。I would use
collections.deque
with amaxlen
argThere is a recipe in the docs for
deque
that is similar to what you want. My assertion that it's the most efficient rests entirely on the fact that it's implemented in C by an incredibly skilled crew that is in the habit of cranking out top notch code.尽管这里已经有很多很好的答案,但我找不到提到的选项的时间的任何直接比较。因此,请看我在下面进行的比较尝试。
仅出于测试目的,该类可以在基于
list
的缓冲区、基于collections.deque
的缓冲区和Numpy.roll
之间切换基于缓冲区。请注意,为了简单起见,
update
方法一次仅添加一个值。在我的系统上,这会产生:
Although there are already a great number of great answers here, I could not find any direct comparison of timings for the options mentioned. Therefore, please find my humble attempt at a comparison below.
For testing purposes only, the class can switch between a
list
-based buffer, acollections.deque
-based buffer, and aNumpy.roll
-based buffer.Note that the
update
method adds only one value at a time, to keep it simple.On my system this yields:
从列表头部弹出会导致复制整个列表,因此效率
低下您应该使用固定大小的列表/数组和在添加/删除项目时在缓冲区中移动的索引
popping from the head of a list causes the whole list to be copied, so is inefficient
You should instead use a list/array of fixed size and an index which moves through the buffer as you add/remove items
基于 MoonCactus 的回答,这里有一个
circularlist
类。与他的版本的不同之处在于,这里c[0]
将始终给出最旧的附加元素c[-1]
最新附加的元素c [-2]
倒数第二个...这对于应用程序来说更自然。班级:
Based on MoonCactus's answer, here is a
circularlist
class. The difference with his version is that herec[0]
will always give the oldest-appended element,c[-1]
the latest-appended element,c[-2]
the penultimate... This is more natural for applications.Class:
可以使用 deque 类,但是对于问题的要求(平均),这是我的解决方案:
ok with the use of deque class, but for the requeriments of the question (average) this is my solution:
Python 的双端队列很慢。您也可以使用 numpy.roll 代替
如何旋转形状为 (n,) 或 (n,1) 的 numpy 数组中的数字?
在此基准测试中,双端队列为 448ms。 Numpy.roll 为 29ms
http://scimusing.wordpress.com/2013/10/ 25/ring-buffers-in-pythonnumpy/
Python's deque is slow. You can also use numpy.roll instead
How do you rotate the numbers in an numpy array of shape (n,) or (n,1)?
In this benchmark, deque is 448ms. Numpy.roll is 29ms
http://scimusing.wordpress.com/2013/10/25/ring-buffers-in-pythonnumpy/
您还可以查看这个相当古老的 Python 配方。
这是我自己的 NumPy 数组版本:
You can also see this quite old Python recipe.
Here is my own version with NumPy array:
Python Cookbook 中的解决方案怎么样,包括当环形缓冲区实例变满时重新分类?
信用:Sébastien Keim
How about the solution from the Python Cookbook, including a reclassification of the ring buffer instance when it becomes full?
Credit: Sébastien Keim
我在进行串行编程之前就遇到过这个问题。就在一年前,我也找不到任何有效的实现,所以我最终编写了 一个一个 C 扩展,并且在 MIT 许可下在 pypi 上也可以使用它。它是超级基本的,仅处理 8 位有符号字符的缓冲区,但长度灵活,因此如果您需要字符以外的东西,您可以使用 Struct 或在其之上的其他东西。我现在通过谷歌搜索看到现在有几个选项,所以你可能也想看看这些。
I've had this problem before doing serial programming. At the time just over a year ago, I couldn't find any efficient implementations either, so I ended up writing one as a C extension and it's also available on pypi under an MIT license. It's super basic, only handles buffers of 8-bit signed chars, but is of flexible length, so you can use Struct or something on top of it if you need something other than chars. I see now with a google search that there are several options these days though, so you might want to look at those too.
来自 Github:
https:// github.com/heineman/python-data-structs/blob/master/2.%20Ubiquitous%20Lists/circBuffer.py
From Github:
https://github.com/heineman/python-data-structures/blob/master/2.%20Ubiquitous%20Lists/circBuffer.py
这里有很多答案,但没有一个按照 D Left Adjoint to U 的建议对 Numpy ndarray 进行子类化。这避免了使用无法有效扩展的 np.roll,并传递了 Numpy 数组的所有优点(如数组切片)。使用 Numpy 数组将允许您需要运行的大多数分析,包括平均。
RingArray 类
我的解决方案使用 Numpy 文档。
RingArray 使用指定的形状进行初始化,并用 np.nan 值填充。
Itertools 循环用于创建一个一维循环,给出数组中要编辑的下一行位置。这是基于初始化期间数组的高度。
ndarray 方法中添加了一个追加方法,用于将数据写入循环中的下一个位置。
性能
我相信这种方法的扩展时间为 O(1),但是我不是计算机科学家,所以如果我错了,请纠正我!
可能的问题
由于这是 ndarray 的子类,因此该类中的所有方法都可以在 RingArray 上使用。使用 np.delete 等数组函数删除或添加值将更改数组的形状。这将导致循环出现错误,因为它是在初始化时设置的。因此,在使用append()以外的任何其他方法编辑数组时要小心。
这是我的第一篇堆栈溢出帖子,如果有任何我可以改进的地方,请告诉我:)。
Lots of answers here but none subclass the Numpy ndarray as suggested by D Left Adjoint to U. This avoids using np.roll which does not scale efficiently, and passes on all the advantages of Numpy arrays like array slicing. Using Numpy arrays will allow for most analyses you need to run, including averaging.
RingArray class
My solution subclasses np.ndarray using the guidelines written in the Numpy documentation.
The RingArray is initialised with a specified shape, and filled with np.nan values.
Itertools cycle is used to create a one dimensional cycle that gives the next row position to edit in the array. This is based on the height of the array during initialisation.
An append method is added to the ndarray methods to write data over the next position in the cycle.
Performance
I believe this method scales at O(1), however I am not a computer scientist, so please correct me if I'm wrong!
Possible issues
As this is a subclass of ndarray, all the methods from that class can be used on the RingArray. Removing or adding values with array functions like np.delete, will change the shape of the array. This will cause an errors with the cycle as it is set at initialisation. For this reason be cautious when editing the array by any other method than append().
This is my first stack overflow post, if there's anything I can improve upon please let me know :).
这个不需要任何库。它会生成一个列表,然后按索引循环。
占用空间非常小(没有库),并且运行速度至少是 dequeue 的两倍。这确实有助于计算移动平均值,但请注意,项目不会像上面那样按年龄排序。
要获得平均值,例如:
结果:
这大约是出队时间的 1/3。
This one does not require any library. It grows a list and then cycle within by index.
The footprint is very small (no library), and it runs twice as fast as dequeue at least. This is good to compute moving averages indeed, but be aware that the items are not kept sorted by age as above.
To get the average value, e.g.:
Results in:
This is about 1/3 the time of the equivalent with dequeue.
我在这里得不到答案。显然,如果您在 NumPy 中工作,您通常希望对 array 或 ndarray 进行子类化,这样(至少在循环数组已满时)您仍然可以在循环数组上使用 NumPy 数组算术运算。您唯一需要注意的是,对于跨越多个组件的操作(例如移动平均线),您的窗口不能大于缓冲区中累积的窗口。
另外,正如所有评论者提到的那样,不要使用滚动,因为这违背了效率的目的。如果您需要不断增长的数组,则只需在每次需要调整大小时将其大小加倍(这与循环数组实现不同)。
I don't get the answers here. Obviously if you're working within NumPy, you'd want to subclass either array or ndarray (usually), that way (at least once your cyclic array is full) you can still use the NumPy array arithmetic operations on the cyclical array. The only thing you have to be careful of is that for operations that span multiple components (such as a moving average), you don't have your window be larger than what has accumulated in the buffer.
Also, as all the commenters mentioned, don't use rolling as that defeats the purpose of efficiency. If you need a growing array, you simply double its size each time a resize is required (this is different from a cyclical array implementation).
最初的问题是:“高效”循环缓冲区。
按照这个效率要求,aaronasterling 的答案似乎绝对正确。
使用用 Python 编写的专用类,并将时间处理与 collections.deque 进行比较,结果显示 deque 的速度提高了 5.2 倍!
下面是测试此功能的非常简单的代码:
要将双端队列转换为列表,只需使用:
然后您将获得对双端队列项的 O(1) 随机访问。当然,只有当您在设置一次双端队列后需要对其进行多次随机访问时,这才有价值。
The original question was: "efficient" circular buffer.
According to this efficiency asked for, the answer from aaronasterling seems to be definitively correct.
Using a dedicated class programmed in Python and comparing time processing with collections.deque shows a x5.2 times acceleration with deque!
Here is very simple code to test this:
To transform a deque into a list, just use:
You will then get O(1) random access to the deque items. Of course, this is only valuable if you need to do many random accesses to the deque after having set it once.
这将相同的原理应用于一些旨在保存最新文本消息的缓冲区。
This is applying the same principal to some buffers intended to hold the most recent text messages.