Любой способ отслеживать последние 5 точек данных в Python - PullRequest
7 голосов
/ 16 июля 2010

Итак, у меня есть массив, который содержит несколько чисел. По мере выполнения моего скрипта все больше и больше чисел добавляются в этот массив. Тем не менее, я не интересуюсь всеми цифрами, а просто хочу отслеживать последние 5 цифр.

В настоящее время я просто храню все числа в массиве. Тем не менее, этот массив становится действительно большим и полон ненужной информации.

Я думал о создании функции, которая при добавлении элемента в массив также удаляет последний элемент, если массив уже содержит 5 чисел.

Я также думал о создании нового класса для создания структуры данных, которая делает то, что я хочу. Тем не менее, мне нужно только ссылаться на этот массив время от времени и является лишь небольшой частью сценария. Поэтому я думаю, что было бы излишне, если бы я создал для этого целый новый класс.

Каков наилучший способ сделать это?

Ответы [ 5 ]

11 голосов
/ 16 июля 2010

Попробуйте использовать deque: http://docs.python.org/library/collections.html#deque-objects

"Если maxlen не указан или равен None, запросы могут увеличиваться до произвольной длины. В противном случае deque ограничивается указанной максимальной длиной. После ограниченияДлина очереди заполнена, при добавлении новых элементов соответствующее количество элементов отбрасывается с противоположного конца. Запросы ограниченной длины предоставляют функциональность, аналогичную хвостовому фильтру в Unix. Они также полезны для отслеживания транзакций и других пулов данных, где толькопоследнее действие представляет интерес. "

5 голосов
/ 16 июля 2010

Я полностью согласен с идеей использования ограниченной длины Python deque, если она доступна, а если нет, простое решение Майкла Андерсона вполне адекватно.(Я проголосовал за оба) Но я просто хотел упомянуть третий вариант кольцевого буфера, который часто используется для такого рода задач, когда важны низкий объем памяти и высокая скорость выполнения.(Другими словами, в ситуациях, когда вы, вероятно, не будете использовать Python :-p) Например, ядро ​​Linux использует эту структуру для хранения сообщений журнала, сгенерированных во время процесса загрузки, до запуска системного регистратора.

Реализация Python может выглядеть так:

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

В основном, это то, что он предварительно выделяет массив (в Python, список) желаемой длины и заполняет его фиктивными значениями.Буфер также содержит «индекс», который указывает на следующую точку в списке, которая должна быть заполнена значением.Каждый раз, когда значение добавляется, оно сохраняется в этом месте и индекс увеличивается.Когда индекс достигает длины массива, он возвращается к нулю.Вот пример (я использую 0 вместо None для фиктивного значения только потому, что его быстрее набирать):

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

и т. Д.

4 голосов
/ 16 июля 2010

Класс может быть довольно тривиальным:

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

Вывод:

[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]
2 голосов
/ 16 июля 2010

Другая реализация аккуратного кольцевого буфера может быть найдена в ActiveState Recipes - ваш объект кольцевого буфера запускается как экземпляр RingBuffer при первом заполнении, а затем ваш экземпляр меняет свой класс в RingBufferFull, оптимизированная полная реализация. Это всегда заставляет меня улыбаться.

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 голосов
/ 16 июля 2010

Из вашего описания я бы добавил следующий тип утверждения сразу после кода, расширяющего ваш список:

mylist = mylist[-5:]

В этом случае длина будет не более 5 значений

Вот краткий пример:

>>> 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]
>>> 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...