Averages/Simple moving average: Difference between revisions
(Forth) |
Underscore (talk | contribs) (Added Common Lisp.) |
||
Line 3: | Line 3: | ||
Create a [[wp:Stateful|stateful]] function/class/instance that takes a number as argument and returns a simple moving average of its arguments so far. |
Create a [[wp:Stateful|stateful]] function/class/instance that takes a number as argument and returns a simple moving average of its arguments so far. |
||
=={{header|Common Lisp}}== |
|||
<lang lisp>(defun simple-moving-average (period &aux |
|||
(sum 0) (qtty 0) (values (make-list period)) (pointer values)) |
|||
(rplacd (last values) values) |
|||
(lambda (n) |
|||
(when (car pointer) |
|||
(decf sum (car pointer))) |
|||
(incf sum n) |
|||
(incf qtty) |
|||
(rplaca pointer n) |
|||
(setf pointer (cdr pointer)) |
|||
(/ sum (min qtty period))))</lang> |
|||
=={{header|Forth}}== |
=={{header|Forth}}== |
Revision as of 13:42, 20 June 2009
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.
Common Lisp
<lang lisp>(defun simple-moving-average (period &aux
(sum 0) (qtty 0) (values (make-list period)) (pointer values)) (rplacd (last values) values) (lambda (n) (when (car pointer) (decf sum (car pointer))) (incf sum n) (incf qtty) (rplaca pointer n) (setf pointer (cdr pointer)) (/ sum (min qtty period))))</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>
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>
Python
<lang python>def simplemovingaverage(period):
assert period == int(period) and period > 0, "Period must be an integer >0" summ = n = 0.0 values = [0.0] * period # old value queue
def sma(x): nonlocal summ, n, values n += 1 values.insert(0, x) summ += x - values.pop() n = n if n <= period else 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
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>
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