[Haskell-beginners] Simple Moving Average of a list of real numbers

Ozgur Akgun ozgurakgun at gmail.com
Tue Nov 26 14:47:48 UTC 2013


Hi Alex.

On 25 November 2013 15:28, Alexandr M <rus314 at gmail.com> wrote:

> Could anybody explain me how to calculate simple moving average of a list ?
>
> I have found several examples in internet but I completely don't
> understand how it works.
>
> Basically it's necessary to iterate over the list of real numbers and
> calculate average values over last n items in the list.
>
> I just can't imagine how to do it without loops.
>

Maybe we can use "inits" from "Data.List".

Prelude> import Data.List
Prelude Data.List> mapM_ print $ inits [1..10]
[]
[1]
[1,2]
[1,2,3]
[1,2,3,4]
[1,2,3,4,5]
[1,2,3,4,5,6]
[1,2,3,4,5,6,7]
[1,2,3,4,5,6,7,8]
[1,2,3,4,5,6,7,8,9]
[1,2,3,4,5,6,7,8,9,10]

Not a bad start. It gives us all the *initial segments* of a list.
Now we need to keep the last n items for each of these. Let's see. Maybe we
can define a function using "drop" and "length" as follows.

Prelude Data.List> let lastN n xs = drop (length xs - n) xs

This function should drop everything but the last n items in a list. If
there are less than n items, it will return the list unchanged.
Try it on a few inputs to see why this is the case. Also try "drop" on a
few inputs.
And the type of this function seems sensible enough:

Prelude Data.List> :t lastN
lastN :: Int -> [a] -> [a]

Now:

Prelude Data.List> mapM_ print $ map (lastN 3) $ inits [1..10]
[]
[1]
[1,2]
[1,2,3]
[2,3,4]
[3,4,5]
[4,5,6]
[5,6,7]
[6,7,8]
[7,8,9]
[8,9,10]

This seems pretty close to what you want. For each item in the list, we
have a corresponding list of n items that are coming just before it.
If only we had a function to calculate the average of a list of numbers,
then we could:

Prelude Data.List> mapM_ print $ map average $ map (lastN 3) $ inits [1..10]
NaN
1.0
1.5
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0

I've defined average locally here to demonstrate the use of it. I'll leave
the definition of it as an exercise to you.

Of course, depending on what you want to do with these averages, you can
implement the same functionality in different ways.
Maybe you only need the last one only or something else. I don't know.
Also you can *fuse* the two maps and use only one map, but I wanted to keep
things explicit.


Hope this helps,
Ozgur.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/beginners/attachments/20131126/678e03a0/attachment.html>


More information about the Beginners mailing list