Как рассчитать скользящее среднее в Python 3?

Предположим, у меня есть список:

y = ['1', '2', '3', '4','5','6','7','8','9','10'] 

Я хочу создать функцию, которая вычисляет перемещающееся среднее значение n-дня. Поэтому, если n было 5, я бы хотел, чтобы мой код вычислил первый 1-5, добавьте его и найдите среднее значение, которое будет 3,0, затем перейдите к 2-6, вычислите среднее значение, которое будет 4,0, затем 3 -7, 4-8, 5-9, 6-10.

Я не хочу вычислять первые n-1 дней, поэтому начиная с n-го дня он будет считать предыдущие дни.

 def moving_average(x:'list of prices', n): for num in range(len(x)+1): print(x[num-n:num]) 

Кажется, это печатает то, что я хочу:

 [] [] [] [] [] ['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'] ['6', '7', '8', '9', '10'] 

Однако я не знаю, как вычислить числа внутри этих списков. Есть идеи?

5 Solutions collect form web for “Как рассчитать скользящее среднее в Python 3?”

Существует большой скользящий оконный генератор в старой версии документов Python с примерами itertools :

 from itertools import islice def window(seq, n=2): "Returns a sliding window (of width n) over data from the iterable" " s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... " it = iter(seq) result = tuple(islice(it, n)) if len(result) == n: yield result for elem in it: result = result[1:] + (elem,) yield result 

Используя это, ваши скользящие средние тривиальны:

 from __future__ import division # For Python 2 def moving_averages(values, size): for selection in window(values, size): yield sum(selection) / size 

Выполнение этого по отношению к вашему вводу (отображение строк в целые числа) дает:

 >>> y= ['1', '2', '3', '4','5','6','7','8','9','10'] >>> for avg in moving_averages(map(int, y), 5): ... print(avg) ... 3.0 4.0 5.0 6.0 7.0 8.0 

Чтобы вернуть None первые n - 1 итераций для «неполных» наборов просто немного moving_averages функцию moving_averages :

 def moving_averages(values, size): for _ in range(size - 1): yield None for selection in window(values, size): yield sum(selection) / size 

В то время как мне нравится ответ Мартинна на это, как и Джордж, мне было интересно, не будет ли это быстрее, если использовать текущее суммирование вместо того, чтобы применять sum() снова и снова на основном одинаковом числе.

Интересна также идея наличия значений None качестве значения по умолчанию во время фазы нарастания. На самом деле может быть много разных сценариев, которые можно было бы понять для скользящих средних. Разделим вычисления средних на три фазы:

  1. Ramp Up: запуск итераций, где текущее количество итераций <размер окна
  2. Устойчивый прогресс: у нас есть точно количество окон, количество доступных элементов для вычисления average := sum(x[iteration_counter-window_size:iteration_counter])/window_size
  3. Ramp Down: в конце входных данных мы могли бы вернуть еще один номер window_size - 1 «средний».

Вот функция, которая принимает

  • Произвольные итерации (генераторы в порядке) в качестве входных данных
  • Произвольные размеры окон> = 1
  • Параметры для включения / выключения производства значений во время фаз для Ramp Up / Down
  • Функции обратного вызова для этих фаз для контроля того, как производятся значения. Это можно использовать для постоянного предоставления значения по умолчанию (например, « None ) или для предоставления частичных средних значений

Вот код:

 from collections import deque def moving_averages(data, size, rampUp=True, rampDown=True): """Slide a window of <size> elements over <data> to calc an average First and last <size-1> iterations when window is not yet completely filled with data, or the window empties due to exhausted <data>, the average is computed with just the available data (but still divided by <size>). Set rampUp/rampDown to False in order to not provide any values during those start and end <size-1> iterations. Set rampUp/rampDown to functions to provide arbitrary partial average numbers during those phases. The callback will get the currently available input data in a deque. Do not modify that data. """ d = deque() running_sum = 0.0 data = iter(data) # rampUp for count in range(1, size): try: val = next(data) except StopIteration: break running_sum += val d.append(val) #print("up: running sum:" + str(running_sum) + " count: " + str(count) + " deque: " + str(d)) if rampUp: if callable(rampUp): yield rampUp(d) else: yield running_sum / size # steady exhausted_early = True for val in data: exhausted_early = False running_sum += val #print("st: running sum:" + str(running_sum) + " deque: " + str(d)) yield running_sum / size d.append(val) running_sum -= d.popleft() # rampDown if rampDown: if exhausted_early: running_sum -= d.popleft() for (count) in range(min(len(d), size-1), 0, -1): #print("dn: running sum:" + str(running_sum) + " deque: " + str(d)) if callable(rampDown): yield rampDown(d) else: yield running_sum / size running_sum -= d.popleft() 

Кажется, это немного быстрее, чем версия Martijn, которая намного более элегантна. Вот тестовый код:

 print("") print("Timeit") print("-" * 80) from itertools import islice def window(seq, n=2): "Returns a sliding window (of width n) over data from the iterable" " s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... " it = iter(seq) result = tuple(islice(it, n)) if len(result) == n: yield result for elem in it: result = result[1:] + (elem,) yield result # Martijn's version: def moving_averages_SO(values, size): for selection in window(values, size): yield sum(selection) / size import timeit problems = [int(i) for i in (10, 100, 1000, 10000, 1e5, 1e6, 1e7)] for problem_size in problems: print("{:12s}".format(str(problem_size)), end="") so = timeit.repeat("list(moving_averages_SO(range("+str(problem_size)+"), 5))", number=1*max(problems)//problem_size, setup="from __main__ import moving_averages_SO") print("{:12.3f} ".format(min(so)), end="") my = timeit.repeat("list(moving_averages(range("+str(problem_size)+"), 5, False, False))", number=1*max(problems)//problem_size, setup="from __main__ import moving_averages") print("{:12.3f} ".format(min(my)), end="") print("") 

И выход:

 Timeit -------------------------------------------------------------------------------- 10 7.242 7.656 100 5.816 5.500 1000 5.787 5.244 10000 5.782 5.180 100000 5.746 5.137 1000000 5.745 5.198 10000000 5.764 5.186 

Теперь исходный вопрос можно решить с помощью этого вызова функции:

 print(list(moving_averages(range(1,11), 5, rampUp=lambda _: None, rampDown=False))) 

Выход:

 [None, None, None, None, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] 

Используйте функции sum и map .

 print(sum(map(int, x[num-n:num]))) 

Функция map в Python 3 в основном представляет собой ленивую версию:

 [int(i) for i in x[num-n:num]] 

Я уверен, что вы можете догадаться, что делает функция sum .

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

 list=range(0,12) def runs(v): global runningsum runningsum+=v return(runningsum) runningsum=0 runsumlist=[ runs(v) for v in list ] result = [ (runsumlist[k] - runsumlist[k-5])/5 for k in range(0,len(list)+1)] 

результат печати

 [2,3,4,5,6,7,8,9] 

сделайте это run (int (v)) .. then .. repr (runningumlist [k] – ranumlist [k-5]) / 5) если вы муравья нести вокруг чисел строки ..


Alt без глобального:

 list = [float[x] for x in range(0,12)] nave = 5 movingave = sum(list[:nave]/nave) for i in range(len(list)-nave):movingave.append(movingave[-1]+(list[i+nave]-list[i])/nave) print movingave 

не забудьте сделать плавающую математику, даже если вы вводите значения целыми числами

 [2.0,3.0,4.0,5.0,6.0,7.0,8.0,9,0] 

Существует еще одно решение, в itertools рецепт itertools pairwise() . Вы можете расширить это на nwise() , что дает вам скользящее окно (и работает, если iterable является генератором):

 def nwise(iterable, n): ts = it.tee(iterable, n) for c, t in enumerate(ts): next(it.islice(t, c, c), None) return zip(*ts) def moving_averages_nw(iterable, n): yield from (sum(x)/n for x in nwise(iterable, n)) >>> list(moving_averages_nw(range(1, 11), 5)) [3.0, 4.0, 5.0, 6.0, 7.0, 8.0] 

В то время как относительно высокая стоимость установки для коротких iterable s эта стоимость уменьшает воздействие, чем дольше набор данных. Это использует sum() но код достаточно элегантен:

 Timeit MP cfi ***** -------------------------------------------------------------------------------- 10 4.658 4.959 7.351 100 5.144 4.070 4.234 1000 5.312 4.020 3.977 10000 5.317 4.031 3.966 100000 5.508 4.115 4.087 1000000 5.526 4.263 4.202 10000000 5.632 4.326 4.242 
Interesting Posts

iTunes API для скриптов python

Как заменить CharField на ForeignKey, используя юг в django?

Как добавить URL-адрес пространства имен к представлению маршрутизатора django-rest-framework

Удалите определенную строку в файле csv с помощью python

Узнайте, если / какая библиотека BLAS используется Numpy

Любой наивный байесовский классификатор в python?

Как фильтровать (или заменять) символы Unicode, которые занимают более 3 байтов в UTF-8?

Подсчитайте частоту слов из столбца в Python, используя внешний файл csv

Pandas CParserError: ошибка для токенизации данных

Захват встроенного изображения карты Google с помощью Python без использования браузера

Как рассортировать дистрибутив scipy.stats (не может сортировать объекты instancemethod)

Как обрезать в большую внутреннюю ограничительную рамку в OpenCV?

Конкатенация многих списков в Python

Почему дизайнеры Python не могут быть привязаны к определенным определениям?

Привязка <Return> к кнопке не работает должным образом

Python - лучший язык программирования в мире.