class TeaLeaves::ExponentialSmoothingForecast

Attributes

alpha[R]
beta[R]
gamma[R]
model_parameters[R]
seasonality[R]
trend[R]

Public Class Methods

new(time_series, period, opts={}) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 73
def initialize(time_series, period, opts={})
  @time_series = time_series
  @period = period
  @alpha = opts[:alpha]
  @beta  = opts[:beta]
  @gamma = opts[:gamma]
  @trend = opts[:trend]
  @seasonality = opts[:seasonality]
  @seasonality_strategy = case @seasonality
                          when :none
                            NoSeasonalityStrategy.new(@period, opts[:gamma])
                          when :additive
                            AdditiveSeasonalityStrategy.new(@period, opts[:gamma])
                          when :multiplicative
                            MultiplicativeSeasonalityStrategy.new(@period, opts[:gamma])
                          end

  calculate_one_step_ahead_forecasts
end

Public Instance Methods

improve(opts) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 93
def improve(opts)
  new_opts = {:alpha => @alpha, :beta => @beta, :gamma => @gamma, :trend => @trend, :seasonality => @seasonality}.merge(opts)
  self.class.new(@time_series, @period, new_opts)
end
initial_level() click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 100
def initial_level
  @initial_level ||= @time_series.take(@period).inject(&:+).to_f / @period
end
initial_parameters() click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 114
def initial_parameters
  { :level => initial_level,
    :trend => initial_trend,
    :seasonality => initial_seasonal_indices,
    :index => @seasonality_strategy.start_index
  }
end
initial_seasonal_indices() click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 109
def initial_seasonal_indices
  operation = @seasonality == :multiplicative ? :/ : :-
  @time_series.take(@period).map {|v| v.to_f.send(operation, initial_level) }
end
initial_trend() click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 104
def initial_trend
  period_1, period_2 = @time_series.each_slice(@period).take(2)
  period_1.zip(period_2).map {|(a,b)| (b - a) / @period.to_f }.inject(&:+) / @period
end
mean_squared_error() click to toggle source

Returns the mean squared error of the forecast.

# File lib/tealeaves/exponential_smoothing_forecast.rb, line 131
def mean_squared_error
  return @mean_squared_error if @mean_squared_error

  numerator = errors.drop(@seasonality_strategy.start_index).map {|i| i ** 2 }.inject(&:+)
  @mean_squared_error = numerator / (errors.size - @seasonality_strategy.start_index).to_f
end
predict(n=nil) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 122
def predict(n=nil)
  if n.nil?
    forecast(@model_parameters).first
  else
    (1..n).map {|i| forecast(@model_parameters, i).first }
  end
end

Private Instance Methods

calculate_one_step_ahead_forecasts() click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 140
def calculate_one_step_ahead_forecasts
  forecasts = [nil] * @seasonality_strategy.start_index
  parameters = initial_parameters
  (@seasonality_strategy.start_index...@time_series.size).each do |i|
    forecast, parameters = forecast(parameters)
    forecasts << forecast
  end
  parameters[:index] -= 1
  @model_parameters = parameters
  @one_step_ahead_forecasts = forecasts
end
forecast(parameters, n=1) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 152
def forecast(parameters, n=1)
  new_params = {}
  new_params[:level] = new_level(parameters)
  new_params[:trend] = new_trend(parameters, new_params[:level])
  new_params[:seasonality] = new_seasonality(parameters, new_params[:level])
  new_params[:index] = parameters[:index] + 1
  
  pre_forecast = case @trend
                 when :none
                   parameters[:level]
                 when :additive
                   parameters[:level] + (n * parameters[:trend])
                 when :multiplicative
                   parameters[:level] * (parameters[:trend] ** n)
                 end

  forecast = @seasonality_strategy.apply(pre_forecast, parameters, n)
  [forecast, new_params]
end
new_level(parameters) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 172
def new_level(parameters)
  @alpha * p(parameters) + (1 - @alpha) * q(parameters)
end
new_seasonality(parameters, new_level) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 182
def new_seasonality(parameters, new_level)
  @seasonality_strategy.new_values(@time_series[parameters[:index]],
                                   parameters,
                                   new_level)
end
new_trend(parameters, new_level) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 176
def new_trend(parameters, new_level)
  unless @trend == :none
    @beta * r(parameters, new_level) + (1 - @beta) * parameters[:trend]
  end
end
p(params) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 188
def p(params)
  @seasonality_strategy.p(@time_series[params[:index]], params)
end
q(params) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 192
def q(params)
  case @trend
  when :none
    params[:level]
  when :additive
    params[:level] + params[:trend]
  when :multiplicative
    params[:level] * params[:trend]
  end
end
r(params, new_level) click to toggle source
# File lib/tealeaves/exponential_smoothing_forecast.rb, line 203
def r(params, new_level)
  case @trend
  when :additive
    new_level - params[:level]
  when :multiplicative
    new_level / params[:level]
  end
end