Eu quero criar um buffer circular eficiente em python (com o objetivo de tirar médias dos valores inteiros no buffer).
Esta é uma maneira eficiente de usar uma lista para coletar valores?
def add_to_buffer( self, num ):
self.mylist.pop( 0 )
self.mylist.append( num )
O que seria mais eficiente (e por quê)?
python
circular-buffer
Jedierikb
fonte
fonte
Respostas:
Eu usaria
collections.deque
com ummaxlen
argExiste uma receita nos documentos para
deque
isso é semelhante ao que você deseja. Minha afirmação de que é o mais eficiente repousa inteiramente no fato de que é implementado em C por uma equipe incrivelmente habilidosa que tem o hábito de produzir código de primeira linha.fonte
maxlen
é definido. O (n) é compreensível quando odeque
pode crescer até o infinito, mas semaxlen
for fornecido, a indexação de um elemento deve ser um tempo constante.aparecer no topo de uma lista faz com que toda a lista seja copiada, por isso é ineficiente
Em vez disso, você deve usar uma lista / array de tamanho fixo e um índice que se move pelo buffer conforme você adiciona / remove itens
fonte
Com base na resposta do MoonCactus , aqui está uma
circularlist
aula. A diferença com sua versão é que aquic[0]
sempre dará o elemento mais antigo anexado,c[-1]
o último elemento anexado,c[-2]
o penúltimo ... Isso é mais natural para aplicativos.Classe:
[Editado]:
data
Parâmetro opcional adicionado para permitir a inicialização de listas existentes, por exemplo:fonte
c[-1]
é sempre o elemento certo.__getitem__
faz direito.O deque do Python é lento. Você também pode usar numpy.roll. Como você gira os números em uma matriz numpy de forma (n,) ou (n, 1)?
Neste benchmark, deque é 448ms. Numpy.roll tem 29ms http://scimusing.wordpress.com/2013/10/25/ring-buffers-in-pythonnumpy/
fonte
numpy.roll
retorna uma cópia do array, certo?ok com o uso da classe deque, mas para os requisitos da questão (média) esta é a minha solução:
fonte
TypeError: 'numpy.float64' object is not callable
ao tentar chamar oaverage
métodocollections
faz parte da biblioteca padrão,numpy
não. Dependências de bibliotecas de terceiros criariam uma biblioteca padrão terrível.Embora já haja um grande número de ótimas respostas aqui, não consegui encontrar nenhuma comparação direta de tempos para as opções mencionadas. Portanto, encontre minha humilde tentativa de comparação abaixo.
Apenas para fins de teste, a classe pode alternar entre um
list
buffer baseado em-, umcollections.deque
buffer baseado em e umNumpy.roll
buffer baseado em.Observe que o
update
método adiciona apenas um valor por vez, para mantê-lo simples.No meu sistema, isso produz:
fonte
Que tal a solução do Python Cookbook , incluindo uma reclassificação da instância do buffer de anel quando ela ficar cheia?
Crédito: Sébastien Keim
fonte
Você também pode ver esta receita Python bastante antiga .
Aqui está minha própria versão com a matriz NumPy:
fonte
O(n)
tempo. Para implementar um buffer circular apropriado , você deve ter um índice e uma variável de tamanho, e você precisa lidar corretamente com o caso em que os dados 'envolvem' o final do buffer. Ao recuperar dados, pode ser necessário concatenar duas seções no início e no final do buffer.Este não requer nenhuma biblioteca. Ele cria uma lista e depois circula por índice.
O espaço físico é muito pequeno (sem biblioteca) e é executado duas vezes mais rápido do que retirar da fila, pelo menos. Isso é bom para calcular médias móveis, mas esteja ciente de que os itens não são mantidos classificados por idade como acima.
Para obter o valor médio, por exemplo:
Resulta em:
Isso é cerca de 1/3 do tempo equivalente com desenfileiramento.
fonte
__getitem__
ser um pouco mais poderosoself._data[(key + self._index + 1) % self._size]
:?Eu tive esse problema antes de fazer a programação serial. Na época, há pouco mais de um ano, também não consegui encontrar nenhuma implementação eficiente, então acabei escrevendo uma como uma extensão C e também está disponível em pypi sob uma licença do MIT. É superbásico, só lida com buffers de chars assinados de 8 bits, mas é de comprimento flexível, então você pode usar Struct ou algo em cima dele se precisar de algo diferente de chars. Vejo agora, com uma pesquisa no Google, que existem várias opções atualmente, então você pode querer dar uma olhada nelas também.
fonte
Sua resposta não está certa. O buffer circular principal tem dois princípios (https://en.wikipedia.org/wiki/Circular_buffer )
seu código abaixo:
Vamos considerar uma situação em que a lista está cheia, usando seu código:
agora acrescentamos 6, a lista é alterada para
os itens esperados 1 na lista mudaram de posição
seu código é uma fila, não um buffer circular.
A resposta do Basj, eu acho que é a mais eficiente.
A propósito, um buffer de círculo pode melhorar o desempenho da operação para adicionar um item.
fonte
Do Github:
https://github.com/heineman/python-data-structures/blob/master/2.%20Ubiquitous%20Lists/circBuffer.py
fonte
A pergunta original era: buffer circular " eficiente ". De acordo com esta eficiência solicitada, a resposta de aaronasterling parece ser definitivamente correta. Usando uma classe dedicada programada em Python e comparando o processamento do tempo com as coleções.deque mostra uma aceleração de x5,2 vezes com deque! Aqui está um código muito simples para testar isso:
Para transformar um deque em uma lista, basta usar:
Você então obterá acesso aleatório O (1) aos itens deque. Claro, isso só é valioso se você precisar fazer muitos acessos aleatórios ao deque depois de configurá-lo uma vez.
fonte
Isso aplica o mesmo princípio a alguns buffers destinados a conter as mensagens de texto mais recentes.
fonte
Você pode verificar esse buffer circular com base em uma matriz numpy de tamanho predefinido. A ideia é que você crie um buffer (aloque memória para o array numpy) e depois acrescente a ele. A inserção e recuperação de dados é muito rápida. Criei este módulo para um propósito semelhante ao que você precisa. No meu caso, tenho um dispositivo que gera dados inteiros. Eu li os dados e os coloquei no buffer circular para análise e processamento futuros.
fonte