2010-07-16 13 views
7

Quindi ho una matrice che contiene diversi numeri. Mentre il mio script viene eseguito, sempre più numeri vengono aggiunti a questo array. Tuttavia, non sono interessato a tutti i numeri ma voglio solo tenere traccia degli ultimi 5 numeri.Qualsiasi modo per tenere traccia degli ultimi 5 punti dati in python

Attualmente, memorizzo solo tutti i numeri nell'array. Tuttavia, questo array diventa davvero grande ed è pieno di informazioni non necessarie.

Ho pensato di creare una funzione che quando aggiunge un elemento alla matrice, rimuove anche l'ultimo elemento se la matrice contiene già 5 numeri.

Ho anche pensato di creare una nuova classe per creare una struttura dati che faccia ciò che voglio. Tuttavia, ho solo bisogno di fare riferimento a questo array occasionalmente ed è solo una piccola parte dello script. Quindi penso che sia eccessivo se creo un'intera nuova classe per farlo.

Qual è il modo migliore per farlo?

risposta

11

Provare a usare una deque:.. http://docs.python.org/library/collections.html#deque-objects

"Se maxlen non è specificato o è None, i deque può crescere fino a una lunghezza arbitraria In caso contrario, il deque è delimitata alla lunghezza massima specificata volta una lunghezza deque limitata è pieno, quando vengono aggiunti nuovi elementi, un numero corrispondente di elementi viene scartato dall'estremità opposta. Le deque di lunghezza limitata forniscono funzionalità simili al filtro di coda in Unix. Sono anche utili per tracciare le transazioni e altri pool di dati dove solo il più l'attività recente è interessante. "

+0

fantastico questo è perfetto. Grazie. – user

+0

+1, ma solo per riferimento questo richiede Python 2.6 o versioni successive. –

4

La classe può essere abbastanza banale:

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 

uscita è:

[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

Le batterie sono già incluse, signore;) – msw

+0

@msw, Ahimè, non in python 2.5.1 che è quello con cui sono bloccato ... Il deque di dimensioni limitate sembra essere solo in Python 2.6 e versioni successive. Quindi se stai usando 2.6+ vai con deques di lunghezza limitata, altrimenti puoi usare qualcosa come la mia soluzione –

+0

+1 nuff giusto, e ben detto – msw

5

Sono pienamente d'accordo con l'idea di usare limitata lunghezza di Python deque se è disponibile, e se no, Michael La semplice soluzione di Anderson è abbastanza adeguata. (Ho svalutato entrambi) Ma volevo solo menzionare la terza opzione di un buffer ad anello, che viene spesso utilizzato per questo tipo di attività quando il footprint di memoria ridotto e l'elevata velocità di esecuzione sono importanti. (In altre parole, in situazioni in cui probabilmente non si utilizzerebbe Python :-p) Ad esempio, il kernel Linux utilizza questa struttura per archiviare i messaggi di log generati durante il processo di avvio, prima dell'avvio del logger di sistema.

attuazione un pitone potrebbe apparire così:

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 

Fondamentalmente ciò che fa è Prealloca un array (in Python, un elenco) della lunghezza desiderata e riempirlo con valori fittizi. Il buffer contiene anche un "indice" che punta al punto successivo nell'elenco che deve essere riempito con un valore. Ogni volta che viene aggiunto un valore, viene memorizzato in quel punto e l'indice viene incrementato. Quando l'indice raggiunge la lunghezza dell'array, viene ripristinato a zero. Ecco un esempio (sto usando 0 invece di None per il valore fittizio solo perché è più veloce da digitare):

[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] 
    ^

e così via.

+0

Si noti che per ottenere le ultime 5 voci in ordine si può fare qualcosa come: def get_all (self): return self._buf [self._index: self._valid] + self._buf [: self._index] –

+0

Perché calcolare ogni volta che si chiama add? self._buf non cambia mai lunghezza, basta aggiungere 'self._buflen = n' in' __init__'. Per incrementare l'indice, è più semplice usare 'self._index = (self._index + 1)% n'. E dovrei pensare che invece di incrementare self._valid se PaulMcG

+0

@Paul: il codice che ho scritto è uno strano mix di ottimizzazione e inefficienza ;-) ma ecco il mio ragionamento: computo 'n' in ogni chiamata a' add' perché è usato più volte e memorizzandolo in una variabile locale salva un ricerca degli attributi. L'operatore '%' tende ad essere lento, ma il ripristino a 0 con un'istruzione 'if' è più veloce. Probabilmente hai un punto sull'impostazione di 'self._valid = self._index'. E se tu sapessi che non avresti mai dovuto accedere al buffer prima che si riempia, '_valid' potrebbe certamente essere omesso del tutto. –

0

Dalla tua descrizione, vorrei aggiungere il seguente tipo di dichiarazione appena dopo il codice che si estende l'elenco:

mylist = mylist[-5:] 

Sarà quindi sempre e solo essere un massimo di 5 valori di lunghezza

Qui un breve esempio:

>>> 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

Un'altra implementazione buffer circolare ordinata può essere trovato nel ActiveState Recipes - l'oggetto buffer circolare inizia come un istanza di RingBuffer durante il riempimento iniziale, quindi l'istanza modifica la sua classe in RingBufferFull, un'implementazione completa ottimizzata. Mi fa sempre sorridere.

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

Cool :-) Probabilmente sarebbe un modo migliore di farlo in Python - quando scrivevo il mio pensavo più alla traduzione di C (dove ovviamente non ci sono classi). +1 –

Problemi correlati