Расчет скользящего среднего - PullRequest
0 голосов
/ 16 сентября 2018

Я изо всех сил пытаюсь реализовать формулу скользящего среднего в моей функции. Мне потребовалось немало времени, чтобы понять, где сейчас находится код.

Есть ли библиотека, которую я мог бы взять?

Введите:

ma([2,3,4,3,2,6,9,3,2,1], 4)

Ожидаемый результат:

[None, None, None, 3.0, 3.0, 3.75, 5.0, 5.0, 5.0, 3.75]

Мой вывод:

[None, None, 0.0, 3.0, 3.75, 5.0, 5.0, None, None, None]

Я сталкиваюсь с проблемой, что средние части моего результата верны, а остальное - загадка.

  1. Почему он возвращает None для последних трех значений в списке?

def ma (цены, n):

ma = [] sums = [] s = 0 ave = 0

for idx, i in enumerate(prices):
    s += i
    sums.append(s)
    print('idx: ' + str(idx))
    print('list of sums ' + str(sums))
    #print('sum ' + str(s))

if i >= n+1:
    print('sums[idx] ' + str(sums[idx]))
    print('sums[idx-n] ' + str(sums[idx-n]))
    ave = (sums[idx] - sums[idx-n]) / n
    print('ave ' + str(ave))
    ma.append(ave)
    print('ma ' + str(ma))
else:
    m = None
    ma.append(m)
    print('ma ' + str(ma))

(извините за все эти print вызовы функций, но я действительно хотел найти источник проблемы).

Ответы [ 4 ]

0 голосов
/ 16 сентября 2018

Если вы хорошо используете стандартную библиотеку, это может помочь. Что вам действительно нужно, так это скользящее окно над вашим итератором. Вы можете использовать эту функцию для этого (это было основано на grouper из рецептов itertools):

from itertools import islice

def window(iterable, n=2):
    # window('123', 2) --> '12' '23'
    args = [islice(iterable, i, None) for i in range(n)]
    return zip(*args)

Для среднего вы можете использовать statistics.mean. Часть paddig может быть просто достигнута путем добавления среднего списка с помощью [None] * (n - 1):

from statistics import mean

def moving_average(prices, n):
    avgs = [mean(w) for w in window(prices, n)]
    padding = [None] * (n - 1)

    return padding + avgs

Пример использования:

>>> moving_average([2,3,4,5,8,5,4,3,2,1], 3)
[None, None, 3, 4, 5.666666666666667, 6, 5.666666666666667, 4, 3, 2]
>>> moving_average([1, 2, 3], 3)
[None, None, 2]
>>> moving_average([1, 2, 3], 1)
[1, 2, 3]
>>> moving_average([5, 10, 0], 2)
[None, 7.5, 5]
0 голосов
/ 16 сентября 2018

Причина, по которой ваша программа вернула значение 9-9 / 3 = 0, - отрицательное индексирование.Когда idx равно 2, sums[idx-n] говорит sums[-1], что указывает на последний элемент списка, 9. Понимание обозначения фрагмента Python может помочь объяснить это.

0 голосов
/ 16 сентября 2018

Вы также можете решить эту проблему, используя нарезку списка, чтобы разумно разделить ваш входной список и вычислить среднее значение по разделам списка:

def moving_average(data,window):
    """The partitions begin with window-1 None. Then follow partial lists, containing
       window-sized elements. We do this only up to len(data)-window+1 as the following
       partitions would have less then window elements."""

    parts = [None]*(window-1) + [ data[i:i+window] for i in range(len(data)-window+1)]
    #       The None's           The sliding window of window elements

    # we return None if the value is None else we calc the avg
    return [ sum(x)/window if x else None for x in parts] 

print( moving_average([2,3,4,5,8,5,4,3,2,1], 1) )
print( moving_average([2,3,4,5,8,5,4,3,2,1], 2) )
print( moving_average([2,3,4,5,8,5,4,3,2,1], 3) )

Вывод (parts включен как комментарий):

# [[2], [3], [4], [5], [8], [5], [4], [3], [2], [1]]
[2.0, 3.0, 4.0, 5.0, 8.0, 5.0, 4.0, 3.0, 2.0, 1.0]

# [None, [2, 3], [3, 4], [4, 5], [5, 8], [8, 5], [5, 4], [4, 3], [3, 2], [2, 1]]
[None, 2.5, 3.5, 4.5, 6.5, 6.5, 4.5, 3.5, 2.5, 1.5]

# [None, None, [2, 3, 4], [3, 4, 5], [4, 5, 8], [5, 8, 5], [8, 5, 4], 
#              [5, 4, 3], [4, 3, 2], [3, 2, 1]]
[None, None, 3.0, 4.0, 5.666666666666667, 6.0, 5.666666666666667, 4.0, 3.0, 2.0]
0 голосов
/ 16 сентября 2018

В вашем коде было несколько других логических ошибок.Я пытался исправить это, чтобы заставить его работать, как вы хотите.Ниже приводится только модифицированная версия цикла for.Отдых остается прежним.Добавленные / измененные строки выделены комментарием

for idx, i in enumerate(prices):
    s += i
    sums.append(s)
    if idx == n-1: # Added
        ave = (sums[idx]) / n  # Added 
        ma.append(ave)  # Added
    elif idx >= n: # modified
        ave = (sums[idx] - sums[idx-n]) / n
        ma.append(ave)
    else:
        ma.append(None) # removed extra variable m

Проблема заключалась в том, что вы использовали неверную переменную в качестве индекса:

Одна из основных проблем заключалась в том, что вы были используя

if i >= n+1:

Вы должны использовать:

if idx >= n+1:

Более того, я добавил оператор if, чтобы заботиться о среднем для первых трех элементов.

Теперь

moving_average([2,3,4,5,8,5,4,3,2,1], 3)

дает следующий вывод (вы можете округлить позже):

[None, None, 3.0, 4.0, 5.666666666666667, 6.0, 5.666666666666667, 4.0, 3.0, 2.0]
...