Module: Stl

Defined in:
lib/stl.rb,
lib/stl/version.rb

Constant Summary collapse

VERSION =
"0.4.2"

Class Method Summary collapse

Class Method Details

.decompose(series, period:, seasonal_length: nil, trend_length: nil, low_pass_length: nil, seasonal_degree: nil, trend_degree: nil, low_pass_degree: nil, seasonal_jump: nil, trend_jump: nil, low_pass_jump: nil, inner_loops: nil, outer_loops: nil, robust: false, iterations: nil, lambda: nil) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/stl.rb', line 9

def decompose(
  series, period:,
  seasonal_length: nil, trend_length: nil, low_pass_length: nil,
  seasonal_degree: nil, trend_degree: nil, low_pass_degree: nil,
  seasonal_jump: nil, trend_jump: nil, low_pass_jump: nil,
  inner_loops: nil, outer_loops: nil, robust: false,
  iterations: nil, lambda: nil
)
  mstl = period.is_a?(Array)

  if !mstl && period < 2
    raise ArgumentError, "period must be greater than 1"
  end

  params = StlParams.new

  params.seasonal_length = seasonal_length if !seasonal_length.nil? && !(mstl && seasonal_length.is_a?(Array))
  params.trend_length = trend_length unless trend_length.nil?
  params.low_pass_length = low_pass_length unless low_pass_length.nil?

  params.seasonal_degree = seasonal_degree unless seasonal_degree.nil?
  params.trend_degree = trend_degree unless trend_degree.nil?
  params.low_pass_degree = low_pass_degree unless low_pass_degree.nil?

  params.seasonal_jump = seasonal_jump unless seasonal_jump.nil?
  params.trend_jump = trend_jump unless trend_jump.nil?
  params.low_pass_jump = low_pass_jump unless low_pass_jump.nil?

  params.inner_loops = inner_loops unless inner_loops.nil?
  params.outer_loops = outer_loops unless outer_loops.nil?
  params.robust = robust unless robust.nil?

  if series.is_a?(Hash)
    sorted = series.sort_by { |k, _| k }
    y = sorted.map(&:last)
  else
    y = series
  end

  if mstl
    mstl_params = MstlParams.new
    mstl_params.iterations = iterations unless iterations.nil?
    mstl_params.lambda = lambda unless lambda.nil?
    mstl_params.seasonal_lengths = seasonal_length if seasonal_length.is_a?(Array)
    mstl_params.stl_params = params
    _decompose_mstl(y, period, mstl_params)
  else
    raise ArgumentError, "iterations requires MSTL" unless iterations.nil?
    raise ArgumentError, "lambda requires MSTL" unless lambda.nil?
    _decompose(y, period, params, outer_loops.nil? ? robust : outer_loops > 0)
  end
end

.plot(series, result) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/stl.rb', line 62

def plot(series, result)
  require "vega"

  if mstl?(result)
    raise "not implemented yet"
  end

  data =
    if series.is_a?(Hash)
      series.sort_by { |k, _| k }.map.with_index do |s, i|
        {
          x: iso8601(s[0]),
          series: s[1],
          seasonal: result[:seasonal][i],
          trend: result[:trend][i],
          remainder: result[:remainder][i]
        }
      end
    else
      series.map.with_index do |v, i|
        {
          x: i,
          series: v,
          seasonal: result[:seasonal][i],
          trend: result[:trend][i],
          remainder: result[:remainder][i]
        }
      end
    end

  if series.is_a?(Hash)
    x = {field: "x", type: "temporal"}
    x["scale"] = {type: "utc"} if series.keys.first.is_a?(Date)
  else
    x = {field: "x", type: "quantitative"}
  end
  x[:axis] = {title: nil, labelFontSize: 12}

  charts =
    ["series", "seasonal", "trend", "remainder"].map do |field|
      {
        mark: {type: "line"},
        encoding: {
          x: x,
          y: {field: field, type: "quantitative", scale: {zero: false}, axis: {labelFontSize: 12}}
        },
        width: "container",
        height: 100
      }
    end

  Vega.lite
    .data(data)
    .vconcat(charts)
    .config(autosize: {type: "fit-x", contains: "padding"})
    .width(nil) # prevents warning
    .height(nil) # prevents warning and sets div height to auto
end

.seasonal_strength(result) ⇒ Object



121
122
123
124
125
126
127
128
129
# File 'lib/stl.rb', line 121

def seasonal_strength(result)
  if mstl?(result)
    result[:seasonal].map do |s|
      strength(s, result[:remainder])
    end
  else
    strength(result[:seasonal], result[:remainder])
  end
end

.trend_strength(result) ⇒ Object



131
132
133
# File 'lib/stl.rb', line 131

def trend_strength(result)
  strength(result[:trend], result[:remainder])
end