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






Can you use the standard library? If so, check out itertools and statistics modules. It can make your job easier.

– bla
Sep 16 '18 at 15:12



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

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)