2010-07-16 15 views
7

Así que tengo una matriz que contiene varios números. A medida que se ejecuta mi secuencia de comandos, se añaden más y más números a esta matriz. Sin embargo, no estoy interesado en todos los números, solo quiero hacer un seguimiento de los últimos 5 números.Cualquier forma de realizar un seguimiento de los últimos 5 puntos de datos en python

Actualmente, guardo todos los números en la matriz. Sin embargo, este conjunto se vuelve muy grande y está lleno de información innecesaria.

He pensado en hacer una función que cuando agrega un elemento a la matriz, también elimina el último elemento si la matriz ya contiene 5 números.

También pensé en hacer una nueva clase para crear una estructura de datos que hace lo que quiero. Sin embargo, solo necesito hacer referencia a esta matriz ocasionalmente y es solo una pequeña parte de la secuencia de comandos. Entonces creo que es excesivo si creo una clase completamente nueva para hacer esto.

¿Cuál es la mejor manera de hacerlo?

Respuesta

11

Trate de usar un deque:.. http://docs.python.org/library/collections.html#deque-objects

"Si maxlen no se especifica o es Ninguno, deques puede crecer hasta una longitud arbitraria lo contrario, el deque está limitada a la longitud máxima especificada Una vez que un deque longitud acotada está lleno, cuando se agregan nuevos elementos, se descarta una cantidad correspondiente de elementos del extremo opuesto. Deques de longitud limitada proporcionan una funcionalidad similar al filtro de cola en Unix. También son útiles para rastrear transacciones y otras agrupaciones de datos donde solo actividad reciente es de interés ".

+0

impresionante esto es perfecto. Gracias. – user

+0

+1, pero solo como referencia esto requiere Python 2.6 o superior. –

4

La clase puede ser bastante trivial:

class ListOfFive: 
    def __init__(self): 
    self.data = [] 
    def add(self,val): 
    if len(self.data)==5: 
     self.data=self.data[1:]+[val] 
    else: 
     self.data+=[val] 

l = ListOfFive() 
for i in range(1,10): 
    l.add(i) 
    print l.data 

de salida es:

[1] 
[1, 2] 
[1, 2, 3] 
[1, 2, 3, 4] 
[1, 2, 3, 4, 5] 
[2, 3, 4, 5, 6] 
[3, 4, 5, 6, 7] 
[4, 5, 6, 7, 8] 
[5, 6, 7, 8, 9] 
+0

Las baterías ya están incluidas, señor;) – msw

+0

@msw, por desgracia, no en python 2.5.1 que es lo que estoy atascado ... El tamaño limitado deque parece ser solo en python 2.6 y superior. Por lo tanto, si usa 2.6+, use deques de longitud limitada, de lo contrario, puede usar algo como mi solución –

+0

+1 fair nuff, y bien dicho – msw

5

estoy totalmente de acuerdo con la idea de usar limitada de longitud de Python deque si está disponible, y si no, Michael La solución simple de Anderson es bastante adecuada. (Cambié de categoría ambos) Pero solo quería mencionar la tercera opción de un buffer de anillo, que a menudo se usa para este tipo de tareas cuando la huella de memoria baja y la alta velocidad de ejecución son importantes. (En otras palabras, en situaciones en las que probablemente no usaría Python :-p) Por ejemplo, el kernel de Linux usa esta estructura para almacenar los mensajes de registro generados durante el proceso de arranque, antes de que se inicie el registrador del sistema.

aplicación una pitón podría tener este aspecto:

class RingBuffer(object): 
    def __init__(self, n): 
     self._buf = [None] * n 
     self._index = 0 
     self._valid = 0 
    def add(self, obj): 
     n = len(self._buf) 
     self._buf[self._index] = obj 
     self._index += 1 
     if self._index == n 
      self._index = 0 
     if self._valid < n: 
      self._valid += 1 
    def __len__(self): 
     return self._valid 
    # could include other methods for accessing or modifying the contents 

Básicamente lo que hace es asignar previamente una matriz (en Python, una lista) de la longitud deseada y llenarlo con valores ficticios. El búfer también contiene un "índice" que apunta al siguiente punto de la lista que debe llenarse con un valor. Cada vez que se agrega un valor, se almacena en ese punto y el índice se incrementa. Cuando el índice alcanza la longitud de la matriz, se reinicia a cero. He aquí un ejemplo (estoy usando 0 en lugar de None para el valor ficticio simplemente porque es más rápido que escribir):

[0,0,0,0,0] 
^ 
# add 1 
[1,0,0,0,0] 
^
# add 2 
[1,2,0,0,0] 
    ^
# add 3 
[1,2,3,0,0] 
    ^
# add 4 
[1,2,3,4,0] 
     ^
# add 5 
[1,2,3,4,5] 
^ 
# add 6 
[6,2,3,4,5] 
^
# add 7 
[6,7,3,4,5] 
    ^

y así sucesivamente.

+0

Tenga en cuenta que para obtener las últimas 5 entradas en orden, puede hacer algo como: def get_all (self): return self._buf [self._index: self._valid] + self._buf [: self._index] –

+0

Why compute n cada vez que se llama add? self._buf nunca cambia la longitud, solo agrega 'self._buflen = n' en' __init__'. Para incrementar el índice, es más fácil usar 'self._index = (self._index + 1)% n'. Y debería pensar que en lugar de incrementar self._valid if PaulMcG

+0

@Paul: El código que escribí es una mezcla extraña de optimización e ineficiencia ;-) pero este es mi razonamiento: calculo 'n' en cada llamada a' agregar' porque se usa varias veces y al almacenarlo en una variable local se guarda una búsqueda de atributos. El operador '%' tiende a ser lento, pero reiniciar a 0 con una instrucción 'if' es más rápido. Probablemente tengas un punto acerca de establecer 'self._valid = self._index'. Y si sabía que nunca tendría que acceder al búfer antes de que se llene, '_valid' podría omitirse por completo. –

0

Desde su descripción, me gustaría añadir el siguiente tipo de declaración que se acaba después del código que se extiende su lista:

mylist = mylist[-5:] 

A continuación, sólo alguna vez tener un máximo de 5 valores de longitud

Aquí es un ejemplo rápido:

>>> mylist = [] 
>>> i = 1 
>>> while i<6: 
    print ("\n Pre addition: %r" % mylist) 
    mylist += range(i) 
    print ("  Addition: %r" % mylist) 
    mylist = mylist[-5:] 
    print ("  Chopped: %r" % mylist) 
    i += 1 



Pre addition: [] 
    Addition: [0] 
    Chopped: [0] 

Pre addition: [0] 
    Addition: [0, 0, 1] 
    Chopped: [0, 0, 1] 

Pre addition: [0, 0, 1] 
    Addition: [0, 0, 1, 0, 1, 2] 
    Chopped: [0, 1, 0, 1, 2] 

Pre addition: [0, 1, 0, 1, 2] 
    Addition: [0, 1, 0, 1, 2, 0, 1, 2, 3] 
    Chopped: [2, 0, 1, 2, 3] 

Pre addition: [2, 0, 1, 2, 3] 
    Addition: [2, 0, 1, 2, 3, 0, 1, 2, 3, 4] 
    Chopped: [0, 1, 2, 3, 4] 
>>> 
2

Otra implementación memoria cíclica ordenada se puede encontrar en el ActiveState Recipes - el objeto búfer circular que comienza como una instancia de RingBuffer mientras se llena al principio, y luego su instancia cambia su clase a RingBufferFull, una implementación completa optimizada. Siempre me hace sonreír.

class RingBuffer: 
    def __init__(self,size_max): 
     self.max = size_max 
     self.data = [] 
    def append(self,x): 
     """append an element at the end of the buffer""" 
     self.data.append(x) 
     if len(self.data) == self.max: 
      self.cur=0 
      self.__class__ = RingBufferFull 
    def get(self): 
     """ return a list of elements from the oldest to the newest""" 
     return self.data 


class RingBufferFull: 
    def __init__(self,n): 
     raise "you should use RingBuffer" 
    def append(self,x):  
     self.data[self.cur]=x 
     self.cur=(self.cur+1) % self.max 
    def get(self): 
     return self.data[self.cur:]+self.data[:self.cur] 
+0

Genial :-) Esa sería probablemente una mejor manera de hacerlo en Python: cuando escribí la mía estaba pensando más en la línea de traducción desde C (donde por supuesto no hay clases). +1 –

Cuestiones relacionadas