Je souhaite créer un tampon circulaire efficace en python (dans le but de prendre des moyennes des valeurs entières dans le tampon).
Est-ce un moyen efficace d'utiliser une liste pour collecter des valeurs?
def add_to_buffer( self, num ):
self.mylist.pop( 0 )
self.mylist.append( num )
Qu'est-ce qui serait plus efficace (et pourquoi)?
python
circular-buffer
jedierikb
la source
la source
Réponses:
J'utiliserais
collections.deque
avec unmaxlen
argIl y a une recette dans la documentation pour
deque
qui est similaire à ce que vous voulez. Mon affirmation selon laquelle c'est le plus efficace repose entièrement sur le fait qu'il est implémenté en C par une équipe incroyablement qualifiée qui a l'habitude de lancer un code de premier ordre.la source
maxlen
est défini. O (n) est compréhensible lorsque ledeque
peut atteindre l'infini, mais s'ilmaxlen
est donné, l'indexation d'un élément doit être en temps constant.sortir de la tête d'une liste entraîne la copie de toute la liste, ce qui est inefficace
Vous devriez plutôt utiliser une liste / un tableau de taille fixe et un index qui se déplace dans la mémoire tampon lorsque vous ajoutez / supprimez des éléments
la source
Basé sur la réponse de MoonCactus , voici une
circularlist
classe. La différence avec sa version est qu'icic[0]
donnera toujours l'élément le plus ancien,c[-1]
le dernier élément ajouté,c[-2]
l'avant-dernier ... C'est plus naturel pour les applications.Classe:
[Modifié]: Ajout d'un
data
paramètre facultatif pour permettre l'initialisation à partir de listes existantes, par exemple:la source
c[-1]
c'est toujours le bon élément.__getitem__
le fait bien.Le deque de Python est lent. Vous pouvez également utiliser numpy.roll à la place. Comment faire pivoter les nombres dans un tableau numpy de forme (n,) ou (n, 1)?
Dans ce benchmark, deque est de 448ms. Numpy.roll est de 29 ms http://scimusing.wordpress.com/2013/10/25/ring-buffers-in-pythonnumpy/
la source
numpy.roll
renvoie une copie du tableau, non?ok avec l'utilisation de la classe deque, mais pour les requeriments de la question (moyenne) c'est ma solution:
la source
TypeError: 'numpy.float64' object is not callable
en essayant d'appeler laaverage
méthodecollections
fait partie de la bibliothèque standard,numpy
ne l'est pas. Les dépendances sur des bibliothèques tierces feraient une terrible bibliothèque standard.Bien qu'il existe déjà un grand nombre de bonnes réponses ici, je n'ai pas pu trouver de comparaison directe des horaires pour les options mentionnées. Par conséquent, veuillez trouver mon humble tentative de comparaison ci-dessous.
À des fins de test uniquement, la classe peut basculer entre un
list
tampon basé sur un tampon, uncollections.deque
tampon basé sur un tampon et unNumpy.roll
tampon basé sur un tampon.Notez que la
update
méthode n'ajoute qu'une seule valeur à la fois, pour rester simple.Sur mon système, cela donne:
la source
Qu'en est-il de la solution du livre de recettes Python , y compris une reclassification de l'instance de tampon en anneau lorsqu'elle est pleine?
Crédit: Sébastien Keim
la source
Vous pouvez également voir cette recette Python assez ancienne .
Voici ma propre version avec le tableau NumPy:
la source
O(n)
temps. Pour implémenter un tampon circulaire approprié , vous devez avoir à la fois un index et une variable de taille, et vous devez gérer correctement le cas où les données «enveloppent» la fin du tampon. Lors de la récupération de données, vous devrez peut-être concaténer deux sections au début et à la fin du tampon.Celui-ci ne nécessite aucune bibliothèque. Il agrandit une liste, puis cycle à l'intérieur par index.
L'encombrement est très petit (pas de bibliothèque), et il fonctionne au moins deux fois plus vite que la file d'attente. C'est bien pour calculer des moyennes mobiles, mais sachez que les éléments ne sont pas triés par âge comme ci-dessus.
Pour obtenir la valeur moyenne, par exemple:
Résulte en:
C'est environ 1/3 du temps de l'équivalent avec dequeue.
la source
__getitem__
être un peu plus puissantself._data[(key + self._index + 1) % self._size]
:?J'ai eu ce problème avant de faire de la programmation en série. À l'époque, il y a un peu plus d'un an, je ne trouvais pas non plus d'implémentations efficaces, j'ai donc fini par en écrire une en tant qu'extension C et elle est également disponible sur pypi sous une licence MIT. C'est super basique, ne gère que les tampons de caractères signés 8 bits, mais sa longueur est flexible, vous pouvez donc utiliser Struct ou quelque chose en plus si vous avez besoin d'autre chose que des caractères. Je vois maintenant avec une recherche Google qu'il existe plusieurs options ces jours-ci, vous voudrez peut-être les regarder aussi.
la source
Votre réponse n'est pas juste. Le tampon circulaire principal a deux priciples (https://en.wikipedia.org/wiki/Circular_buffer )
votre code ci-dessous:
Considérons une situation où la liste est pleine, en utilisant votre code:
maintenant nous ajoutons 6, la liste est changée en
les éléments attendus 1 dans la liste ont changé de position
votre code est une file d'attente, pas un tampon circulaire.
La réponse de Basj, je pense, est la plus efficace.
À propos, un tampon circulaire peut améliorer les performances de l'opération d'ajout d'un élément.
la source
Depuis Github:
https://github.com/heineman/python-data-structures/blob/master/2.%20Ubiquitous%20Lists/circBuffer.py
la source
La question initiale était: tampon circulaire " efficace ". D'après cette efficacité demandée, la réponse d'aaronasterling semble définitivement correcte. L'utilisation d'une classe dédiée programmée en Python et la comparaison du traitement du temps avec collections.deque montre une accélération x5,2 fois avec deque! Voici un code très simple pour tester ceci:
Pour transformer un deque en liste, il suffit d'utiliser:
Vous obtiendrez alors un accès aléatoire O (1) aux objets deque. Bien sûr, cela n'a de valeur que si vous devez effectuer de nombreux accès aléatoires au deque après l'avoir défini une fois.
la source
Cela applique le même principe à certains tampons destinés à contenir les messages texte les plus récents.
la source
Vous pouvez extraire ce tampon circulaire basé sur un tableau numpy de taille prédéfinie. L'idée est que vous créez un tampon (allouez de la mémoire pour le tableau numpy) et que vous y ajoutez plus tard. L'insertion des données et la récupération sont très rapides. J'ai créé ce module dans un but similaire à celui dont vous avez besoin. Dans mon cas, j'ai un appareil qui génère des données entières. J'ai lu les données et les ai placées dans le tampon circulaire pour une analyse et un traitement futurs.
la source