Averages/Simple moving average
You are encouraged to solve this task according to the task description, using any language you may know.
Computing the simple moving average of a series of numbers.
Create a stateful function/class/instance that takes a number as argument and returns a simple moving average of its arguments so far.
Note that the moving average has a period and numbers beyond the period must be dropped from the calculation of the average. (See the Python example, amongst others, for a correct implementation) .
See also: Standard Deviation
AutoHotkey
ahk forum: discussion For Integers: <lang AutoHotkey>MsgBox % MovingAverage(5,3) ; 5, averaging length <- 3 MsgBox % MovingAverage(1) ; 3 MsgBox % MovingAverage(-3) ; 1 MsgBox % MovingAverage(8) ; 2 MsgBox % MovingAverage(7) ; 4
MovingAverage(x,len="") { ; for integers (faster)
Static Static sum:=0, n:=0, m:=10 ; default averaging length = 10 If (len>"") ; non-blank 2nd parameter: set length, reset sum := n := i := 0, m := len If (n < m) ; until the buffer is not full sum += x, n++ ; keep summing data Else ; when buffer is full sum += x-v%i% ; add new, subtract oldest v%i% := x, i := mod(i+1,m) ; remember last m inputs, cycle insertion point Return sum/n
}</lang> For floating point numbers: <lang AutoHotkey>MovingAverage(x,len="") { ; for floating point numbers
Static Static n:=0, m:=10 ; default averaging length = 10 If (len>"") ; non-blank 2nd parameter: set length, reset n := i := 0, m := len n += n < m, sum := 0 v%i% := x, i := mod(i+1,m) ; remember last m inputs, cycle insertion point Loop %n% ; recompute sum to avoid error accumulation j := A_Index-1, sum += v%j% Return sum/n
}</lang>
ALGOL 68
Note: This following code is a direct translation of the C code sample. It mimics C's var_list implementation, and so it probably isn't the most natural way of dong this actual task in ALGOL 68. <lang Algol68>MODE SMAOBJ = STRUCT(
LONG REAL sma, LONG REAL sum, INT period, REF[]LONG REAL values, INT lv
);
MODE SMARESULT = UNION (
REF SMAOBJ # handle #, LONG REAL # sma #, REF[]LONG REAL # values #
);
MODE SMANEW = INT,
SMAFREE = STRUCT(REF SMAOBJ free obj), SMAVALUES = STRUCT(REF SMAOBJ values obj), SMAADD = STRUCT(REF SMAOBJ add obj, LONG REAL v), SMAMEAN = STRUCT(REF SMAOBJ mean obj, REF[]LONG REAL v);
MODE ACTION = UNION ( SMANEW, SMAFREE, SMAVALUES, SMAADD, SMAMEAN );
PROC sma = ([]ACTION action)SMARESULT: (
SMARESULT result; REF SMAOBJ obj; LONG REAL v;
FOR i FROM LWB action TO UPB action DO CASE action[i] IN (SMANEW period):( # args: INT period # HEAP SMAOBJ handle; sma OF handle := 0.0; period OF handle := period; values OF handle := HEAP [period OF handle]LONG REAL; lv OF handle := 0; sum OF handle := 0.0; result := handle ), (SMAFREE args):( # args: REF SMAOBJ free obj # free obj OF args := REF SMAOBJ(NIL) # let the garbage collector do it's job # ), (SMAVALUES args):( # args: REF SMAOBJ values obj # result := values OF values obj OF args ), (SMAMEAN args):( # args: REF SMAOBJ mean obj # result := sma OF mean obj OF args ), (SMAADD args):( # args: REF SMAOBJ add obj, LONG REAL v # obj := add obj OF args; v := v OF args; IF lv OF obj < period OF obj THEN (values OF obj)[lv OF obj+:=1] := v; sum OF obj +:= v; sma OF obj := sum OF obj / lv OF obj ELSE sum OF obj -:= (values OF obj)[ 1+ lv OF obj MOD period OF obj]; sum OF obj +:= v; sma OF obj := sum OF obj / period OF obj; (values OF obj)[ 1+ lv OF obj MOD period OF obj ] := v; lv OF obj+:=1 FI; result := sma OF obj ) OUT SKIP ESAC OD; result
);
[]LONG REAL v = ( 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 );
main: (
INT i; REF SMAOBJ h3 := ( sma(SMANEW(3)) | (REF SMAOBJ obj):obj ); REF SMAOBJ h5 := ( sma(SMANEW(5)) | (REF SMAOBJ obj):obj ); FOR i FROM LWB v TO UPB v DO printf(($"next number "g(0,6)", SMA_3 = "g(0,6)", SMA_5 = "g(0,6)l$, v[i], (sma(SMAADD(h3, v[i]))|(LONG REAL r):r), ( sma(SMAADD(h5, v[i])) | (LONG REAL r):r ) )) OD#; sma(SMAFREE(h3)); sma(SMAFREE(h5))
)</lang>Output:
next number 1.000000, SMA_3 = 1.000000, SMA_5 = 1.000000 next number 2.000000, SMA_3 = 1.500000, SMA_5 = 1.500000 next number 3.000000, SMA_3 = 2.000000, SMA_5 = 2.000000 next number 4.000000, SMA_3 = 3.000000, SMA_5 = 2.500000 next number 5.000000, SMA_3 = 4.000000, SMA_5 = 3.000000 next number 5.000000, SMA_3 = 4.666667, SMA_5 = 3.800000 next number 4.000000, SMA_3 = 4.666667, SMA_5 = 4.200000 next number 3.000000, SMA_3 = 4.000000, SMA_5 = 4.200000 next number 2.000000, SMA_3 = 3.000000, SMA_5 = 3.800000 next number 1.000000, SMA_3 = 2.000000, SMA_5 = 3.000000
C
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <stdarg.h>
typedef struct sma_obj {
double sma; double sum; int period; double *values; int lv;
} sma_obj_t;
typedef union sma_result {
sma_obj_t *handle; double sma; double *values;
} sma_result_t;
enum Action { SMA_NEW, SMA_FREE, SMA_VALUES, SMA_ADD, SMA_MEAN }; sma_result_t sma(enum Action action, ...) {
va_list vl; sma_result_t r; sma_obj_t *o; double v;
va_start(vl, action); switch(action) { case SMA_NEW: // args: int period r.handle = malloc(sizeof(sma_obj_t)); r.handle->sma = 0.0; r.handle->period = va_arg(vl, int); r.handle->values = malloc(r.handle->period * sizeof(double)); r.handle->lv = 0; r.handle->sum = 0.0; break; case SMA_FREE: // args: sma_obj_t *handle r.handle = va_arg(vl, sma_obj_t *); free(r.handle->values); free(r.handle); r.handle = NULL; break; case SMA_VALUES: // args: sma_obj_t *handle o = va_arg(vl, sma_obj_t *); r.values = o->values; break; case SMA_MEAN: // args: sma_obj_t *handle o = va_arg(vl, sma_obj_t *); r.sma = o->sma; break; case SMA_ADD: // args: sma_obj_t *handle, double value o = va_arg(vl, sma_obj_t *); v = va_arg(vl, double); if ( o->lv < o->period ) { o->values[o->lv++] = v; o->sum += v; o->sma = o->sum / o->lv; } else { o->sum -= o->values[ o->lv % o->period]; o->sum += v; o->sma = o->sum / o->period; o->values[ o->lv % o->period ] = v; o->lv++; } r.sma = o->sma; break; } va_end(vl); return r;
}</lang>
<lang c>double v[] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 };
int main() {
int i;
sma_obj_t *h3 = sma(SMA_NEW, 3).handle; sma_obj_t *h5 = sma(SMA_NEW, 5).handle;
for(i=0; i < sizeof(v)/sizeof(double) ; i++) { printf("next number %lf, SMA_3 = %lf, SMA_5 = %lf\n",
v[i], sma(SMA_ADD, h3, v[i]).sma, sma(SMA_ADD, h5, v[i]).sma);
}
sma(SMA_FREE, h3); sma(SMA_FREE, h5); return 0;
}</lang>
Common Lisp
This implementation uses a circular list to store the numbers within the window; at the beginning of each iteration pointer refers to the list cell which holds the value just moving out of the window and to be replaced with the just-added value.
<lang lisp>(defun simple-moving-average (period &aux
(sum 0) (count 0) (values (make-list period)) (pointer values)) (setf (rest (last values)) values) ; construct circularity (lambda (n) (when (first pointer) (decf sum (first pointer))) ; subtract old value (incf sum n) ; add new value (incf count) (setf (first pointer) n) (setf pointer (rest pointer)) ; advance pointer (/ sum (min count period))))</lang>
D
This should work properly for any types that act like numerics. <lang d>import std.stdio; import std.string;
class MovingAvg(T) {
T[]elements; int window = 5; MovingAvg!(T) add(T input) { elements ~= input; return this; } void setWindow(int win) {window = win;} T getAvg() { if (!window) return 0; T sum = 0; int start = (elements.length-window<0?elements.length:elements.length-window); if (start == elements.length) return 0; foreach(ele;elements[start..$]) {sum += ele;} return sum/(elements.length-start); }
}
int main() {
double[]nums = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1]; auto mavg = new MovingAvg!(double); foreach(index,num;nums) { writefln("Average number %d is %s",index,toString(mavg.add(num).getAvg)); } return 0;
}</lang>
E
This implementation produces two (function) objects sharing state. It is idiomatic in E to separate input from output (read from write) rather than combining them into one object.
The structure is the same as the implementation of Standard Deviation#E.
<lang e>pragma.enable("accumulator") def makeMovingAverage(period) {
def values := ([null] * period).diverge() var index := 0 var count := 0 def insert(v) { values[index] := v index := (index + 1) %% period count += 1 } /** Returns the simple moving average of the inputs so far, or null if there have been no inputs. */ def average() { if (count > 0) { return accum 0 for x :notNull in values { _ + x } / count.min(period) } } return [insert, average]
}</lang>
> def [insert, average] := makeMovingAverage(period) > println(`Period $period:`) > for value in [1,2,3,4,5,5,4,3,2,1] { > insert(value) > println(value, "\t", average()) > } > println() > }
Period 3: 1 1.0 2 1.5 3 2.0 4 3.0 5 4.0 5 4.666666666666667 4 4.666666666666667 3 4.0 2 3.0 1 2.0
Period 5: 1 1.0 2 1.5 3 2.0 4 2.5 5 3.0 5 3.8 4 4.2 3 4.2 2 3.8
1 3.0</lang>Forth
<lang forth>: f+! ( f addr -- ) dup f@ f+ f! ;
- ,f0s ( n -- ) falign 0 do 0e f, loop ;
- period @ ;
- used cell+ ;
- head 2 cells + ;
- sum 3 cells + faligned ;
- ring ( addr -- faddr )
dup sum float+ swap head @ floats + ;
- update ( fvalue addr -- addr )
dup ring f@ fnegate dup sum f+! fdup dup ring f! dup sum f+! dup head @ 1+ over period mod over head ! ;
- moving-average
create ( period -- ) dup , 0 , 0 , 1+ ,f0s does> ( fvalue -- avg ) update dup used @ over period < if 1 over used +! then dup sum f@ used @ 0 d>f f/ ;
3 moving-average sma 1e sma f. \ 1. 2e sma f. \ 1.5 3e sma f. \ 2. 4e sma f. \ 3.</lang>
Fortran
<lang fortran>program Movavg
implicit none
integer :: i
write (*, "(a)") "SIMPLE MOVING AVERAGE: PERIOD = 3"
do i = 1, 5 write (*, "(a, i2, a, f8.6)") "Next number:", i, " sma = ", sma(real(i)) end do do i = 5, 1, -1 write (*, "(a, i2, a, f8.6)") "Next number:", i, " sma = ", sma(real(i)) end do
contains
function sma(n)
real :: sma real, intent(in) :: n real, save :: a(3) = 0 integer, save :: count = 0
if (count < 3) then count = count + 1 a(count) = n else a = eoshift(a, 1, n) end if
sma = sum(a(1:count)) / real(count)
end function
end program Movavg</lang>
Groovy
<lang groovy>def simple_moving_average = { size ->
def nums = [] double total = 0.0 return { newElement -> nums += newElement oldestElement = nums.size() > size ? nums.remove(0) : 0 total += newElement - oldestElement total / nums.size() }
}
ma5 = simple_moving_average(5)
(1..5).each{ printf( "%1.1f ", ma5(it)) } (5..1).each{ printf( "%1.1f ", ma5(it)) }</lang> Sample output:
1.0 1.5 2.0 2.5 3.0 3.8 4.2 4.2 3.8 3.0
Haskell
<lang Haskell>import Data.List import Control.Arrow import Control.Monad
sMA p = map (head *** head ).tail.
scanl (\(y,_) -> (id &&& return. av) . (: if length y == p then init y else y)) ([],[]) where av = liftM2 (/) sum (fromIntegral.length)
printSMA n p = mapM_ (\(n,a) -> putStrLn $ "Next number: " ++ show n ++ " Average: " ++ show a)
. take n . sMA p $ [1..5]++[5,4..1]++[3..]</lang>
Output:
*Main> sequence_ [putStrLn "Moving Average Period 3:",printSMA 10 3 ,putStrLn "\nMoving Average Period 5:",printSMA 10 5] Moving Average Period 3: Next number: 1.0 Average: 1.0 Next number: 2.0 Average: 1.5 Next number: 3.0 Average: 2.0 Next number: 4.0 Average: 3.0 Next number: 5.0 Average: 4.0 Next number: 5.0 Average: 4.666666666666667 Next number: 4.0 Average: 4.666666666666667 Next number: 3.0 Average: 4.0 Next number: 2.0 Average: 3.0 Next number: 1.0 Average: 2.0 Moving Average Period 5: Next number: 1.0 Average: 1.0 Next number: 2.0 Average: 1.5 Next number: 3.0 Average: 2.0 Next number: 4.0 Average: 2.5 Next number: 5.0 Average: 3.0 Next number: 5.0 Average: 3.8 Next number: 4.0 Average: 4.2 Next number: 3.0 Average: 4.2 Next number: 2.0 Average: 3.8 Next number: 1.0 Average: 3.0
J
Note: J is block-oriented, not stream oriented. That is, J expresses algorithms with the semantics that all the data is available at once (rather than maintaining state and waiting for the next item).
In that context, moving average is expressed very concisely in J as (+/%#)\
.
In the context of the task, we need to produce a stateful function to consume streams. Since J does not have native lexical closure, we need to implement it. Thus the streaming solution is more complex: <lang j> lex =: 1 :'(a[n__a=.m#_.[a=.18!:3$~0)&(4 :(+/%#)(#~1-128!:5)n__x=.1|.!.y n__x)'</lang> Example: <lang j> sma =: 5 lex
sma&> 1 2 3 4 5 5 4 3 2 1
1 1.5 2 2.5 3 3.8 4.2 4.2 3.8 3</lang>
Here, the &>
is analogous to the "for each" of other languages.
Java
<lang java5>import java.util.LinkedList; import java.util.Queue; public class MovingAverage {
private final Queue<Double> window = new LinkedList<Double>(); private final int period; private double sum;
public MovingAverage(int period) { assert period > 0 : "Period must be a positive integer"; this.period = period; }
public void newNum(double num) { sum += num; window.add(num); if (window.size() > period) { sum -= window.remove(); } }
public double getAvg() { if (window.isEmpty()) return 0; // technically the average is undefined return sum / window.size(); }
public static void main(String[] args) { double[] testData = {1,2,3,4,5,5,4,3,2,1}; int[] windowSizes = {3,5}; for (int windSize : windowSizes) { MovingAverage ma = new MovingAverage(windSize); for (double x : testData) { ma.newNum(x); System.out.println("Next number = " + x + ", SMA = " + ma.getAvg()); } System.out.println(); } }
}</lang> Output:
Next number = 1.0, SMA = 1.0 Next number = 2.0, SMA = 1.5 Next number = 3.0, SMA = 2.0 Next number = 4.0, SMA = 3.0 Next number = 5.0, SMA = 4.0 Next number = 5.0, SMA = 4.666666666666667 Next number = 4.0, SMA = 4.666666666666667 Next number = 3.0, SMA = 4.0 Next number = 2.0, SMA = 3.0 Next number = 1.0, SMA = 2.0 Next number = 1.0, SMA = 1.0 Next number = 2.0, SMA = 1.5 Next number = 3.0, SMA = 2.0 Next number = 4.0, SMA = 2.5 Next number = 5.0, SMA = 3.0 Next number = 5.0, SMA = 3.8 Next number = 4.0, SMA = 4.2 Next number = 3.0, SMA = 4.2 Next number = 2.0, SMA = 3.8 Next number = 1.0, SMA = 3.0
JavaScript
<lang javascript>function simple_moving_averager(period) {
var nums = []; return function(num) { nums.push(num); if (nums.length > period) nums.splice(0,1); // remove the first element of the array var sum = 0; for (var i in nums) sum += nums[i]; var n = period; if (nums.length < period) n = nums.length; return(sum/n); }
}
var sma3 = simple_moving_averager(3); var sma5 = simple_moving_averager(5); var data = [1,2,3,4,5,5,4,3,2,1]; for (var i in data) {
var n = data[i]; // using WSH WScript.Echo("Next number = " + n + ", SMA_3 = " + sma3(n) + ", SMA_5 = " + sma5(n));
}</lang> output:
Next number = 1, SMA_3 = 1, SMA_5 = 1 Next number = 2, SMA_3 = 1.5, SMA_5 = 1.5 Next number = 3, SMA_3 = 2, SMA_5 = 2 Next number = 4, SMA_3 = 3, SMA_5 = 2.5 Next number = 5, SMA_3 = 4, SMA_5 = 3 Next number = 5, SMA_3 = 4.666666666666667, SMA_5 = 3.8 Next number = 4, SMA_3 = 4.666666666666667, SMA_5 = 4.2 Next number = 3, SMA_3 = 4, SMA_5 = 4.2 Next number = 2, SMA_3 = 3, SMA_5 = 3.8 Next number = 1, SMA_3 = 2, SMA_5 = 3
Lua
<lang lua>do
local nums, av = 0, 0 function average(n) if nums == 10 then av, nums = 0, 0 end nums, av = nums + 1, av + n return av / nums end
end for i, v in ipairs{1,2,3,4,5,6,7,8,9} do print(average(v)) end</lang>
OCaml
<lang ocaml>let sma (n, s, q) x =
let l = Queue.length q and s = s +. x in Queue.push x q; if l < n then (n, s, q), s /. float (l + 1) else ( let s = s -. Queue.pop q in (n, s, q), s /. float l )
let _ =
let periodLst = [ 3; 5 ] in let series = [ 1.; 2.; 3.; 4.; 5.; 5.; 4.; 3.; 2.; 1. ] in List.iter (fun d -> Printf.printf "SIMPLE MOVING AVERAGE: PERIOD = %d\n" d; ignore ( List.fold_left (fun o x ->
let o, m = sma o x in Printf.printf "Next number = %-2g, SMA = %g\n" x m; o
) (d, 0., Queue.create ()) series; ); print_newline (); ) periodLst</lang>
Output:
SIMPLE MOVING AVERAGE: PERIOD = 3 Next number = 1 , SMA = 1 Next number = 2 , SMA = 1.5 Next number = 3 , SMA = 2 Next number = 4 , SMA = 3 Next number = 5 , SMA = 4 Next number = 5 , SMA = 4.66667 Next number = 4 , SMA = 4.66667 Next number = 3 , SMA = 4 Next number = 2 , SMA = 3 Next number = 1 , SMA = 2 SIMPLE MOVING AVERAGE: PERIOD = 5 Next number = 1 , SMA = 1 Next number = 2 , SMA = 1.5 Next number = 3 , SMA = 2 Next number = 4 , SMA = 2.5 Next number = 5 , SMA = 3 Next number = 5 , SMA = 3.8 Next number = 4 , SMA = 4.2 Next number = 3 , SMA = 4.2 Next number = 2 , SMA = 3.8 Next number = 1 , SMA = 3
More imperatively: <lang ocaml>let sma_create period =
let q = Queue.create () and sum = ref 0.0 in fun x -> sum := !sum +. x; Queue.push x q; if Queue.length q > period then sum := !sum -. Queue.pop q; !sum /. float (Queue.length q)
let () =
let periodLst = [ 3; 5 ] in let series = [ 1.; 2.; 3.; 4.; 5.; 5.; 4.; 3.; 2.; 1. ] in List.iter (fun d -> Printf.printf "SIMPLE MOVING AVERAGE: PERIOD = %d\n" d; let sma = sma_create d in List.iter (fun x -> Printf.printf "Next number = %-2g, SMA = %g\n" x (sma x); ) series; print_newline (); ) periodLst</lang>
Mathematica
This version uses a list entry so it can use the built-in function. <lang Mathematica>MA[x_List, r_] := Join[Table[Mean[x1;;y],{y,r-1}], MovingAverage[x,r]]</lang>
This version is stateful instead. <lang Mathematica>MAData = {{}, 0}; MAS[x_, t_: Null] :=
With[{r = If[t === Null, MAData2, t]}, Mean[MAData1 = If[Length[#] > (MAData2 = r), #-r ;; -1, #] &@ Append[MAData1, x]]]</lang>
Tests: <lang Mathematica> MA[{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}, 5] => {1, 3/2, 2, 5/2, 3, 19/5, 21/5, 21/5, 19/5, 3}
MAS[1, 5] => 1 MAS[2] => 3/2 MAS[3] => 2 MAS[4] => 5/2 MAS[5] => 3 MAS[5] => 19/5 MAS[4] => 21/5 MAS[3] => 21/5 MAS[2] => 19/5 MAS[1] => 3 </lang>
Oz
<lang oz>declare
fun {CreateSMA Period} Xs = {NewCell nil} in fun {$ X} Xs := {List.take X|@Xs Period} {FoldL @Xs Number.'+' 0.0} / {Int.toFloat {Min Period {Length @Xs}}} end end
in
for Period in [3 5] do SMA = {CreateSMA Period} in {System.showInfo "\nSTART PERIOD "#Period} for I in 1..5 do {System.showInfo " Number = "#I#" , SMA = "#{SMA {Int.toFloat I}}} end for I in 5..1;~1 do {System.showInfo " Number = "#I#" , SMA = "#{SMA {Int.toFloat I}}} end end</lang>
Perl
<lang perl>sub sma ($)
{my ($period, $sum, @a) = shift, 0; return sub {unshift @a, shift; $sum += $a[0]; @a > $period and $sum -= pop @a; return $sum / @a;}}</lang>
Perl 6
<lang perl6>sub sma (Int $period where (* > 0)) returns Sub {
my ($sum, @a) = 0; return sub ($x) { unshift @a, $x; $sum += $x; @a > $period and $sum -= pop @a; return $sum / @a; }
}</lang>
Python
<lang python>from collections import deque
def simplemovingaverage(period):
assert period == int(period) and period > 0, "Period must be an integer >0" summ = n = 0.0 values = deque([0.0] * period) # old value queue
def sma(x): nonlocal summ, n values.append(x) summ += x - values.popleft() n = min(n+1, period) return summ / n
return sma
if __name__ == '__main__':
for period in [3, 5]: print ("\nSIMPLE MOVING AVERAGE: PERIOD =", period) sma = simplemovingaverage(period) for i in range(1,6): print (" Next number = %-2g, SMA = %g " % (i, sma(i))) for i in range(5, 0, -1): print (" Next number = %-2g, SMA = %g " % (i, sma(i)))</lang>
Sample output
SIMPLE MOVING AVERAGE: PERIOD = 3 Next number = 1 , SMA = 1 Next number = 2 , SMA = 1.5 Next number = 3 , SMA = 2 Next number = 4 , SMA = 3 Next number = 5 , SMA = 4 Next number = 5 , SMA = 4.66667 Next number = 4 , SMA = 4.66667 Next number = 3 , SMA = 4 Next number = 2 , SMA = 3 Next number = 1 , SMA = 2 SIMPLE MOVING AVERAGE: PERIOD = 5 Next number = 1 , SMA = 1 Next number = 2 , SMA = 1.5 Next number = 3 , SMA = 2 Next number = 4 , SMA = 2.5 Next number = 5 , SMA = 3 Next number = 5 , SMA = 3.8 Next number = 4 , SMA = 4.2 Next number = 3 , SMA = 4.2 Next number = 2 , SMA = 3.8 Next number = 1 , SMA = 3
R
This is easiest done with two functions: one to handle the state (i.e. the numbers already entered), and one to calculate the average. <lang R>#concat concatenates the new values to the existing vector of values, then discards any values that are too old. lastvalues <- local( {
values <- c(); function(x, len) { values <<- c(values, x); lenv <- length(values); if(lenv > len) values <<- values[(len-lenv):-1] values }
})
- moving.average accepts a numeric scalars input (and optionally a length, i.e. the number of values to retain) and calculates the stateful moving average.
moving.average <- function(latestvalue, len=3) {
#Check that all inputs are numeric scalars is.numeric.scalar <- function(x) is.numeric(x) && length(x)==1L if(!is.numeric.scalar(latestvalue) || !is.numeric.scalar(len)) { stop("all arguments must be numeric scalars") } #Calculate mean of variables so far mean(lastvalues(latestvalue, len))
} moving.average(5) # 5 moving.average(1) # 3 moving.average(-3) # 1 moving.average(8) # 2 moving.average(7) # 4</lang>
Ruby
A closure: <lang ruby>def simple_moving_average(size)
nums = [] sum = 0.0 lambda do |hello| nums << hello goodbye = nums.length > size ? nums.shift : 0 sum += hello - goodbye sum / nums.length end
end
ma3 = simple_moving_average(3) ma5 = simple_moving_average(5)
(1.upto(5).to_a + 5.downto(1).to_a).each do |num|
printf "Next number = %d, SMA_3 = %.3f, SMA_5 = %.1f\n", num, ma3.call(num), ma5.call(num)
end</lang>
A class <lang ruby>class MovingAverager
def initialize(size) @size = size @nums = [] @sum = 0.0 end def <<(hello) @nums << hello goodbye = @nums.length > @size ? @nums.shift : 0 @sum += hello - goodbye self end def average @sum / @nums.length end alias to_f average def to_s average.to_s end
end
ma3 = MovingAverager.new(3) ma5 = MovingAverager.new(5)
(1.upto(5).to_a + 5.downto(1).to_a).each do |num|
printf "Next number = %d, SMA_3 = %.3f, SMA_5 = %.1f\n", num, ma3 << num, ma5 <<num
end</lang>
Scala
<lang scala>class MovingAverage(period: Int) {
private var queue = new scala.collection.mutable.Queue[Double]() def apply(n: Double) = { queue.enqueue(n) if (queue.size > period) queue.dequeue queue.sum / queue.size } override def toString = queue.mkString("(", ", ", ")")+", period "+period+", average "+(queue.sum / queue.size) def clear = queue.clear
}</lang>
scala> List(3,5) foreach { period => | println("SIMPLE MOVING AVERAGE: PERIOD = "+period) | val sma = new MovingAverage(period) | 1.0 to 5.0 by 1.0 foreach {i => println(" Next number = %-2g, SMA = %g " format (i, sma(i)))} | 5.0 to 1.0 by -1.0 foreach {i => println(" Next number = %-2g, SMA = %g " format (i, sma(i)))} | println(sma+"\n") | } SIMPLE MOVING AVERAGE: PERIOD = 3 Next number = 1.00000, SMA = 1.00000 Next number = 2.00000, SMA = 1.50000 Next number = 3.00000, SMA = 2.00000 Next number = 4.00000, SMA = 3.00000 Next number = 5.00000, SMA = 4.00000 Next number = 5.00000, SMA = 4.66667 Next number = 4.00000, SMA = 4.66667 Next number = 3.00000, SMA = 4.00000 Next number = 2.00000, SMA = 3.00000 Next number = 1.00000, SMA = 2.00000 (3.0, 2.0, 1.0), period 3, average 2.0 SIMPLE MOVING AVERAGE: PERIOD = 5 Next number = 1.00000, SMA = 1.00000 Next number = 2.00000, SMA = 1.50000 Next number = 3.00000, SMA = 2.00000 Next number = 4.00000, SMA = 2.50000 Next number = 5.00000, SMA = 3.00000 Next number = 5.00000, SMA = 3.80000 Next number = 4.00000, SMA = 4.20000 Next number = 3.00000, SMA = 4.20000 Next number = 2.00000, SMA = 3.80000 Next number = 1.00000, SMA = 3.00000 (5.0, 4.0, 3.0, 2.0, 1.0), period 5, average 3.0
Scheme
<lang scheme>(define ((simple-moving-averager size . nums) num)
(set! nums (cons num (if (= (length nums) size) (reverse (cdr (reverse nums))) nums))) (/ (apply + nums) (length nums)))
(define av (simple-moving-averager 3)) (map av '(1 2 3 4 5 5 4 3 2 1)) </lang> Output:
(1 3/2 2 3 4 14/3 14/3 4 3 2)
Smalltalk
<lang smalltalk>Object subclass: MovingAverage [
|valueCollection period collectedNumber sum| MovingAverage class >> newWithPeriod: thePeriod [
|r| r := super basicNew. ^ r initWithPeriod: thePeriod
] initWithPeriod: thePeriod [ valueCollection := OrderedCollection new: thePeriod.
period := thePeriod. collectedNumber := 0. sum := 0
] sma [ collectedNumber < period ifTrue: [ ^ sum / collectedNumber ] ifFalse: [ ^ sum / period ] ] add: value [ collectedNumber < period ifTrue: [
sum := sum + value. valueCollection add: value. collectedNumber := collectedNumber + 1. ] ifFalse: [ sum := sum - (valueCollection removeFirst). sum := sum + value. valueCollection add: value ]. ^ self sma
]
].</lang>
<lang smalltalk>|sma3 sma5|
sma3 := MovingAverage newWithPeriod: 3. sma5 := MovingAverage newWithPeriod: 5.
- ( 1 2 3 4 5 5 4 3 2 1 ) do: [ :v |
('Next number %1, SMA_3 = %2, SMA_5 = %3' % { v . (sma3 add: v) asFloat . (sma5 add: v) asFloat }) displayNl
]</lang>
Tcl
<lang tcl>oo::class create SimpleMovingAverage {
variable vals idx constructor Template:Period 3 { set idx end-[expr {$period-1}] set vals {} } method val x { set vals [lrange [list {*}$vals $x] $idx end] expr {[tcl::mathop::+ {*}$vals]/double([llength $vals])} }
}</lang> Demonstration: <lang tcl>SimpleMovingAverage create averager3 SimpleMovingAverage create averager5 5 foreach n {1 2 3 4 5 5 4 3 2 1} {
puts "Next number = $n, SMA_3 = [averager3 val $n], SMA_5 = [averager5 val $n]"
}</lang> Output:
Next number = 1, SMA_3 = 1.0, SMA_5 = 1.0 Next number = 2, SMA_3 = 1.5, SMA_5 = 1.5 Next number = 3, SMA_3 = 2.0, SMA_5 = 2.0 Next number = 4, SMA_3 = 3.0, SMA_5 = 2.5 Next number = 5, SMA_3 = 4.0, SMA_5 = 3.0 Next number = 5, SMA_3 = 4.666666666666667, SMA_5 = 3.8 Next number = 4, SMA_3 = 4.666666666666667, SMA_5 = 4.2 Next number = 3, SMA_3 = 4.0, SMA_5 = 4.2 Next number = 2, SMA_3 = 3.0, SMA_5 = 3.8 Next number = 1, SMA_3 = 2.0, SMA_5 = 3.0
TI-83 BASIC
Prompts for a source list A and the length K of the moving average. The 'L' in "LB" and "LB" is found in "List"/"OPS". <lang ti83b>:Prompt LA,K
- For(I,1,dim(LA))
- 0→S
- For(J,I-K+1,I)
- If J≥1
- S+LA(J)→S
- End
- S/K→LB(I)
- End</lang>
TI-89 BASIC
<lang ti89b>movinavg(la,k) Func
Local lb,s,i,j For i,1,dim(la) 0→s For j,i-k+1,i If j≥1 Then s+la[j]→s EndIf EndFor s/k→lb[i] EndFor Return lb</lang> EndFunc
- Programming Tasks
- Probability and statistics
- AutoHotkey
- ALGOL 68
- C
- Common Lisp
- D
- E
- Forth
- Fortran
- Groovy
- Haskell
- J
- Java
- JavaScript
- Lua
- Lua examples needing attention
- Examples needing attention
- OCaml
- Mathematica
- Oz
- Perl
- Perl 6
- Python
- R
- Ruby
- Scala
- Scheme
- Smalltalk
- Tcl
- TI-83 BASIC
- TI-83 BASIC examples needing attention
- TI-89 BASIC
- TI-89 BASIC examples needing attention