Quiero crear un búfer circular eficiente en Python (con el objetivo de tomar promedios de los valores enteros en el búfer).
¿Es esta una forma eficaz de utilizar una lista para recopilar valores?
def add_to_buffer( self, num ):
self.mylist.pop( 0 )
self.mylist.append( num )
¿Qué sería más eficiente (y por qué)?
python
circular-buffer
jedierikb
fuente
fuente
Respuestas:
Lo usaría
collections.deque
con unmaxlen
argHay una receta en los documentos
deque
que es similar a lo que desea. Mi afirmación de que es el más eficiente se basa completamente en el hecho de que está implementado en C por un equipo increíblemente capacitado que tiene el hábito de generar código de primera categoría.fuente
maxlen
está definido. O (n) es comprensible cuando eldeque
puede crecer hasta el infinito, pero simaxlen
se da, indexar un elemento debe ser un tiempo constante.Salir del encabezado de una lista hace que se copie la lista completa, por lo que es ineficiente
En su lugar, debe usar una lista / matriz de tamaño fijo y un índice que se mueve a través del búfer a medida que agrega / elimina elementos
fuente
Según la respuesta de MoonCactus , aquí hay una
circularlist
clase. La diferencia con su versión es que aquíc[0]
siempre se dará el elemento agregado más antiguo,c[-1]
el elemento agregado más reciente,c[-2]
el penúltimo ... Esto es más natural para las aplicaciones.Clase:
[Editado]:
data
parámetro opcional agregado para permitir la inicialización desde listas existentes, por ejemplo:fuente
c[-1]
que siempre es el elemento correcto.__getitem__
lo hace bien.La deque de Python es lenta. También puedes usar numpy.roll en su lugar. ¿Cómo rotas los números en una matriz numerosa de forma (n,) o (n, 1)?
En este punto de referencia, deque es 448ms. Numpy.roll es de 29 ms http://scimusing.wordpress.com/2013/10/25/ring-buffers-in-pythonnumpy/
fuente
numpy.roll
devuelve una copia de la matriz, ¿verdad?ok con el uso de la clase deque, pero para los requisitos de la pregunta (promedio) esta es mi solución:
fuente
TypeError: 'numpy.float64' object is not callable
al intentar llamar alaverage
métodocollections
es parte de la biblioteca estándar,numpy
no lo es. Las dependencias de bibliotecas de terceros constituirían una biblioteca estándar terrible.Aunque ya hay una gran cantidad de excelentes respuestas aquí, no pude encontrar ninguna comparación directa de tiempos para las opciones mencionadas. Por lo tanto, encuentre mi humilde intento de comparación a continuación.
Solo con fines de prueba, la clase puede cambiar entre un
list
búfercollections.deque
basado en un, un búferNumpy.roll
basado en un y un búfer basado en uno.Tenga en cuenta que el
update
método agrega solo un valor a la vez, para que sea simple.En mi sistema esto produce:
fuente
¿Qué tal la solución del Python Cookbook , incluida una reclasificación de la instancia del búfer de anillo cuando se llena?
Crédito: Sébastien Keim
fuente
También puedes ver esta receta de Python bastante antigua .
Aquí está mi propia versión con la matriz NumPy:
fuente
O(n)
tiempo. Para implementar un búfer circular adecuado , debe tener tanto un índice como una variable de tamaño, y debe manejar correctamente el caso cuando los datos "envuelven" el final del búfer. Al recuperar datos, es posible que deba concatenar dos secciones al principio y al final del búfer.Éste no requiere biblioteca. Crece una lista y luego se desplaza por índice.
La huella es muy pequeña (sin biblioteca) y se ejecuta al menos el doble de rápido que la eliminación de la cola. De hecho, esto es bueno para calcular promedios móviles, pero tenga en cuenta que los elementos no se mantienen ordenados por edad como se indicó anteriormente.
Para obtener el valor promedio, por ejemplo:
Resultados en:
Esto es aproximadamente 1/3 del tiempo del equivalente con dequeue.
fuente
__getitem__
ser un poco más poderosoself._data[(key + self._index + 1) % self._size]
?Tuve este problema antes de hacer programación en serie. En ese momento, hace poco más de un año, tampoco pude encontrar ninguna implementación eficiente, así que terminé escribiendo una como una extensión C y también está disponible en pypi con una licencia del MIT. Es súper básico, solo maneja búferes de caracteres firmados de 8 bits, pero tiene una longitud flexible, por lo que puede usar Struct o algo más si necesita algo más que caracteres. Sin embargo, ahora veo con una búsqueda en Google que hay varias opciones en estos días, por lo que es posible que también desee verlas.
fuente
Tu respuesta no es correcta. El búfer circular principal tiene dos principios (https://en.wikipedia.org/wiki/Circular_buffer )
su código a continuación:
Consideremos una situación en la que la lista está llena, usando su código:
ahora agregamos 6, la lista se cambia a
los elementos que esperan 1 en la lista han cambiado de posición
su código es una cola, no un búfer circular.
La respuesta de Basj, creo que es la más eficaz.
Por cierto, un búfer circular puede mejorar el rendimiento de la operación para agregar un elemento.
fuente
Desde Github:
https://github.com/heineman/python-data-structures/blob/master/2.%20Ubiquitous%20Lists/circBuffer.py
fuente
La pregunta original era: búfer circular " eficiente ". De acuerdo con esta eficiencia solicitada, la respuesta de aaronasterling parece ser definitivamente correcta. Usando una clase dedicada programada en Python y comparando el procesamiento del tiempo con las colecciones. Aquí hay un código muy simple para probar esto:
Para transformar una deque en una lista, simplemente use:
A continuación, obtendrá O (1) acceso aleatorio a los elementos deque. Por supuesto, esto solo es valioso si necesita hacer muchos accesos aleatorios al deque después de haberlo configurado una vez.
fuente
Esto aplica el mismo principio a algunos búferes destinados a contener los mensajes de texto más recientes.
fuente
Puede consultar este búfer circular basado en una matriz numérica de tamaño predefinido. La idea es que cree un búfer (asigne memoria para la matriz numpy) y luego la agregue. La inserción y recuperación de datos es muy rápida. He creado este módulo para un propósito similar al que necesita. En mi caso, tengo un dispositivo que genera datos enteros. Leo los datos y los coloco en el búfer circular para futuros análisis y procesamiento.
fuente