class TeaLeaves::MovingAverage

A calculator for simple & weighted moving averages.

Attributes

span[R]
weights[R]

Public Class Methods

multiple(m,n) click to toggle source

Returns a moving average of a moving average calculator.

For Example, for a 3x3 MA:

MovingAverage.multiple(3,3).weights #=> [1/9, 2/9, 1/3, 2/9, 1/9]
# File lib/tealeaves/moving_average.rb, line 41
def self.multiple(m,n)
  divisor = (m * n).to_f
  weights = (1..m).map {|i| i / divisor }
  num_of_center_weights = ((m + n) / 2) - m
  num_of_center_weights = 0 if num_of_center_weights < 0
  num_of_center_weights.times { weights << weights.last }
  
  new(expand_weights(weights.reverse))
end
new(weights) click to toggle source

Creates a new MovingAverage given a list of weights.

See also the class methods simple and weighted.

# File lib/tealeaves/moving_average.rb, line 54
def initialize(weights)
  @weights = weights
  @span = @weights.length
  check_weights
end
simple(n) click to toggle source

Returns a Simple Moving Average calculator, given a span n.

Examples:

MovingAverage.simple(5).weights # => [0.2, 0.2, 0.2, 0.2, 0.2]

@param [Integer] n the span or order of the moving average,

i.e. the number of terms to include in each average.
# File lib/tealeaves/moving_average.rb, line 28
def self.simple(n)
  raise ArgumentError.new("Number of terms must be positive") if n < 1
  weights = n.odd?() ? [1.0 / n] * n : expand_weights([1.0 / n] * (n / 2) + [1.0 / (2 * n)])
  new weights
end
weighted(weights) click to toggle source

Returns a Weighted Moving Average calculator, given a list of weights.

The list of weights should be an odd length, and should sum to 1.

Examples:

MovingAverage.weighted([0.15, 0.7, 0.15]) # => [0.2, 0.7, 0.1]
# File lib/tealeaves/moving_average.rb, line 16
def self.weighted(weights)
  new(weights)
end

Private Class Methods

expand_weights(weights) click to toggle source
# File lib/tealeaves/moving_average.rb, line 83
def self.expand_weights(weights)
  left_side_weights = weights.reverse
  left_side_weights.pop
  left_side_weights + weights
end

Public Instance Methods

calculate(array) click to toggle source

Calculates the moving average for the given array of numbers.

Moving averages won't include values for terms at the beginning or end of the array, so there will be fewer numbers than in the original.

# File lib/tealeaves/moving_average.rb, line 64
def calculate(array)
  return [] if @span > array.length
  
  array.each_cons(@span).map do |window|
    window.zip(weights).map {|(a,b)| a * b }.inject(&:+)
  end
end

Private Instance Methods

check_weights() click to toggle source

Error checking for weights

# File lib/tealeaves/moving_average.rb, line 75
def check_weights
  raise ArgumentError.new("Weights should be an odd list") unless @span.odd?
  sum = weights.inject(&:+)
  if sum < 0.999999 || sum > 1.000001
    raise ArgumentError.new("Weights must sum to 1")
  end
end