Pythonで効率的な循環バッファーを作成したい(バッファー内の整数値の平均を取ることを目的としています)。
これは、リストを使用して値を収集する効率的な方法ですか?
def add_to_buffer( self, num ):
self.mylist.pop( 0 )
self.mylist.append( num )
何がより効率的でしょうか (そしてその理由)?
Pythonで効率的な循環バッファーを作成したい(バッファー内の整数値の平均を取ることを目的としています)。
これは、リストを使用して値を収集する効率的な方法ですか?
def add_to_buffer( self, num ):
self.mylist.pop( 0 )
self.mylist.append( num )
何がより効率的でしょうか (そしてその理由)?
私は引数で使用collections.deque
しますmaxlen
>>> import collections
>>> d = collections.deque(maxlen=10)
>>> d
deque([], maxlen=10)
>>> for i in xrange(20):
... d.append(i)
...
>>> d
deque([10, 11, 12, 13, 14, 15, 16, 17, 18, 19], maxlen=10)
ドキュメントには、あなたが望むものに似たレシピがあります。deque
それが最も効率的であるという私の主張は、一流のコードを作成する習慣がある信じられないほど熟練した乗組員によって C で実装されているという事実に完全に基づいています。
リストの先頭からポップすると、リスト全体がコピーされるため、非効率的です
代わりに、固定サイズのリスト/配列と、アイテムを追加/削除するときにバッファを移動するインデックスを使用する必要があります
MoonCactus の答えに基づいて、ここにcircularlist
クラスがあります。彼のバージョンとの違いは、ここで c[0]
は常に、最後に追加された要素、最後c[-1]
に追加された要素、c[-2]
最後から 2 番目の要素が提供されることです。これは、アプリケーションにとってより自然です。
c = circularlist(4)
c.append(1); print(c, c[0], c[-1]) #[1] (1/4 items) 1 1
c.append(2); print(c, c[0], c[-1]) #[1, 2] (2/4 items) 1 2
c.append(3); print(c, c[0], c[-1]) #[1, 2, 3] (3/4 items) 1 3
c.append(8); print(c, c[0], c[-1]) #[1, 2, 3, 8] (4/4 items) 1 8
c.append(10); print(c, c[0], c[-1]) #[2, 3, 8, 10] (4/4 items) 2 10
c.append(11); print(c, c[0], c[-1]) #[3, 8, 10, 11] (4/4 items) 3 11
d = circularlist(4, [1, 2, 3, 4, 5]) #[2, 3, 4, 5]
クラス:
class circularlist(object):
def __init__(self, size, data = []):
"""Initialization"""
self.index = 0
self.size = size
self._data = list(data)[-size:]
def append(self, value):
"""Append an element"""
if len(self._data) == self.size:
self._data[self.index] = value
else:
self._data.append(value)
self.index = (self.index + 1) % self.size
def __getitem__(self, key):
"""Get element by index, relative to the current index"""
if len(self._data) == self.size:
return(self._data[(key + self.index) % self.size])
else:
return(self._data[key])
def __repr__(self):
"""Return string representation"""
return (self._data[self.index:] + self._data[:self.index]).__repr__() + ' (' + str(len(self._data))+'/{} items)'.format(self.size)
Python の deque は遅いです。代わりに numpy.roll を使用することもでき ます。形状 (n,) または (n,1) の numpy 配列で数値をどのように回転させますか?
このベンチマークでは、deque は 448ms です。Numpy.roll は 29ms http://scimusing.wordpress.com/2013/10/25/ring-buffers-in-pythonnumpy/
このかなり古いPython レシピも参照できます。
NumPy配列を使用した私自身のバージョンは次のとおりです。
#!/usr/bin/env python
import numpy as np
class RingBuffer(object):
def __init__(self, size_max, default_value=0.0, dtype=float):
"""initialization"""
self.size_max = size_max
self._data = np.empty(size_max, dtype=dtype)
self._data.fill(default_value)
self.size = 0
def append(self, value):
"""append an element"""
self._data = np.roll(self._data, 1)
self._data[0] = value
self.size += 1
if self.size == self.size_max:
self.__class__ = RingBufferFull
def get_all(self):
"""return a list of elements from the oldest to the newest"""
return(self._data)
def get_partial(self):
return(self.get_all()[0:self.size])
def __getitem__(self, key):
"""get element"""
return(self._data[key])
def __repr__(self):
"""return string representation"""
s = self._data.__repr__()
s = s + '\t' + str(self.size)
s = s + '\t' + self.get_all()[::-1].__repr__()
s = s + '\t' + self.get_partial()[::-1].__repr__()
return(s)
class RingBufferFull(RingBuffer):
def append(self, value):
"""append an element when buffer is full"""
self._data = np.roll(self._data, 1)
self._data[0] = value
シリアルプログラミングを行う前に、この問題が発生しました。ちょうど 1 年ほど前の時点でも、効率的な実装を見つけることができなかったので、C 拡張機能として 1 つ作成することになり、MIT ライセンスの下で pypiでも利用できます。これは非常に基本的なもので、8 ビットの符号付き char のバッファーのみを処理しますが、長さが柔軟であるため、char 以外のものが必要な場合は、Struct またはその上に何かを使用できます。最近はいくつかのオプションがあることがグーグル検索でわかりましたので、それらも見たいと思うかもしれません.