Calculating a moving average
Calculating a moving average
I struggle to implement the moving average formula in my function.
Took me quite a while to get where the code is right now.
Is there a library I could probably take?
Input:
ma([2,3,4,3,2,6,9,3,2,1], 4)
Expected Output:
[None, None, None, 3.0, 3.0, 3.75, 5.0, 5.0, 5.0, 3.75]
My Output:
[None, None, 0.0, 3.0, 3.75, 5.0, 5.0, None, None, None]
I am running into the problem that the middle parts of my result is right but the rest is a mystery.
def ma(prices, n):
ma =
sums =
s = 0
ave = 0
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))
(Sorry for all those print
function calls, but I really wanted to get to the source of the issue).
print
itertools
statistics
4 Answers
4
There were several other logical errors in your code. I tried to correct it to make it work as you want. Following is only the modified version of the for loop. Rest stays the same. The added/modified lines are highlighted by a comment
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
The problem was that you were using the wrong variable as the index:
One major problem was that you were using
if i >= n+1:
You should use:
if idx >= n+1:
Moreover, I added an if statement to take care of the average of the first three elements.
Now
moving_average([2,3,4,5,8,5,4,3,2,1], 3)
gives the following output (you can round off later):
[None, None, 3.0, 4.0, 5.666666666666667, 6.0, 5.666666666666667, 4.0, 3.0, 2.0]
Thank you so much for that extensive explanation!!! I should work on how I define variables... Since also a major issue I was sitting on for an hour in there resulted of wrong usage...
– dobero
Sep 16 '18 at 15:15
@dobero: If the answer solved you problem, you may accept it
– Bazingaa
Oct 20 '18 at 12:43
If you are ok using the standard library, this might help. What you really need is a sliding window over your iterator. You can use this function for that (this was based on grouper
from itertools recipes):
grouper
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)
For the average you can use statistics.mean
. The paddig part can be simply achieved by adding the average list with [None] * (n - 1)
:
statistics.mean
[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
Sample usage:
>>> 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]
The reason why your program returned that 9-9 / 3 = 0
is negative indexing. When idx
is 2
, sums[idx-n]
is saying sums[-1]
, which points at the last item of the list, 9
. Understanding Python's slice notation could help explain that.
9-9 / 3 = 0
idx
2
sums[idx-n]
sums[-1]
9
definitely gonna have a look on that! Index and slicing is still under construction in my head
– dobero
Sep 16 '18 at 15:16
You could also solve this using list slicing to partition your input list smartly and calculate the avg over the list-partitions:
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) )
Output (parts
included as comment):
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]
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy
Can you use the standard library? If so, check out
itertools
andstatistics
modules. It can make your job easier.– bla
Sep 16 '18 at 15:12