stl-rb 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +15 -4
- data/ext/stl/ext.cpp +24 -0
- data/lib/stl/version.rb +1 -1
- data/lib/stl.rb +39 -8
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c47963b84db78ea882aa50f4a6a18c18ee84e9acc802d0db125772f80531b179
|
|
4
|
+
data.tar.gz: 9c9e732f43bc586dbac041be2a3c4cc59d552681a59f8185cb873f6800bf9787
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f434fe76d8498d8eaf6560fc464f84e10ea1aa1addf32a5fbf432e1f1435ae31d5936b2b49b64a3220bdf298b379074d97b4f9863e6e6a4a10a800ac6caa2746
|
|
7
|
+
data.tar.gz: 4aa67ef012521f9a810f6aa15586e0f9010c4a87b0f7aacbf0fb8eef6aeb47af06b0707d5268b0b9bb5c2f57cd0d295de47930907e24658db9e6d5091ab16b01
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -18,9 +18,9 @@ Decompose a time series
|
|
|
18
18
|
|
|
19
19
|
```ruby
|
|
20
20
|
series = {
|
|
21
|
-
Date.parse("
|
|
22
|
-
Date.parse("
|
|
23
|
-
Date.parse("
|
|
21
|
+
Date.parse("2026-01-01") => 100,
|
|
22
|
+
Date.parse("2026-01-02") => 150,
|
|
23
|
+
Date.parse("2026-01-03") => 136,
|
|
24
24
|
# ...
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -47,6 +47,14 @@ Use robustness iterations
|
|
|
47
47
|
Stl.decompose(series, period: 7, robust: true)
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
## Multiple Seasonality
|
|
51
|
+
|
|
52
|
+
Specify multiple periods
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
Stl.decompose(series, period: [7, 365])
|
|
56
|
+
```
|
|
57
|
+
|
|
50
58
|
## Options
|
|
51
59
|
|
|
52
60
|
Pass options
|
|
@@ -66,7 +74,9 @@ Stl.decompose(
|
|
|
66
74
|
low_pass_jump: 1, # skipping value for low-pass smoothing
|
|
67
75
|
inner_loops: 2, # number of loops for updating the seasonal and trend components
|
|
68
76
|
outer_loops: 0, # number of iterations of robust fitting
|
|
69
|
-
robust: false
|
|
77
|
+
robust: false, # if robustness iterations are to be used
|
|
78
|
+
iterations: 2, # number of iterations for MSTL
|
|
79
|
+
lambda: 0.5 # lambda for Box-Cox transformation for MSTL
|
|
70
80
|
)
|
|
71
81
|
```
|
|
72
82
|
|
|
@@ -105,6 +115,7 @@ This library was ported from the [Fortran implementation](https://www.netlib.org
|
|
|
105
115
|
## References
|
|
106
116
|
|
|
107
117
|
- [STL: A Seasonal-Trend Decomposition Procedure Based on Loess](https://www.scb.se/contentassets/ca21efb41fee47d293bbee5bf7be7fb3/stl-a-seasonal-trend-decomposition-procedure-based-on-loess.pdf)
|
|
118
|
+
- [MSTL: A Seasonal-Trend Decomposition Algorithm for Time Series with Multiple Seasonal Patterns](https://arxiv.org/pdf/2107.13462.pdf)
|
|
108
119
|
- [Measuring strength of trend and seasonality](https://otexts.com/fpp2/seasonal-strength.html)
|
|
109
120
|
|
|
110
121
|
## History
|
data/ext/stl/ext.cpp
CHANGED
|
@@ -33,6 +33,13 @@ void Init_ext() {
|
|
|
33
33
|
.define_attr("outer_loops", &stl::StlParams::outer_loops)
|
|
34
34
|
.define_attr("robust", &stl::StlParams::robust);
|
|
35
35
|
|
|
36
|
+
Rice::define_class_under<stl::MstlParams>(rb_mStl, "MstlParams")
|
|
37
|
+
.define_constructor(Rice::Constructor<stl::MstlParams>())
|
|
38
|
+
.define_attr("iterations", &stl::MstlParams::iterations)
|
|
39
|
+
.define_attr("lambda", &stl::MstlParams::lambda)
|
|
40
|
+
.define_attr("seasonal_lengths", &stl::MstlParams::seasonal_lengths)
|
|
41
|
+
.define_attr("stl_params", &stl::MstlParams::stl_params);
|
|
42
|
+
|
|
36
43
|
rb_mStl
|
|
37
44
|
.define_singleton_function(
|
|
38
45
|
"_decompose",
|
|
@@ -48,5 +55,22 @@ void Init_ext() {
|
|
|
48
55
|
ret[Rice::Symbol("weights")] = to_a(fit.weights());
|
|
49
56
|
}
|
|
50
57
|
return ret;
|
|
58
|
+
})
|
|
59
|
+
.define_singleton_function(
|
|
60
|
+
"_decompose_mstl",
|
|
61
|
+
[](Rice::Array rb_series, const std::vector<size_t>& periods, const stl::MstlParams& params) {
|
|
62
|
+
std::vector<float> series = rb_series.to_vector<float>();
|
|
63
|
+
stl::Mstl fit{series, periods, params};
|
|
64
|
+
|
|
65
|
+
Rice::Array seasonal;
|
|
66
|
+
for (const auto& s : fit.seasonal()) {
|
|
67
|
+
seasonal.push(to_a(s), false);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Rice::Hash ret;
|
|
71
|
+
ret[Rice::Symbol("seasonal")] = seasonal;
|
|
72
|
+
ret[Rice::Symbol("trend")] = to_a(fit.trend());
|
|
73
|
+
ret[Rice::Symbol("remainder")] = to_a(fit.remainder());
|
|
74
|
+
return ret;
|
|
51
75
|
});
|
|
52
76
|
}
|
data/lib/stl/version.rb
CHANGED
data/lib/stl.rb
CHANGED
|
@@ -11,15 +11,18 @@ module Stl
|
|
|
11
11
|
seasonal_length: nil, trend_length: nil, low_pass_length: nil,
|
|
12
12
|
seasonal_degree: nil, trend_degree: nil, low_pass_degree: nil,
|
|
13
13
|
seasonal_jump: nil, trend_jump: nil, low_pass_jump: nil,
|
|
14
|
-
inner_loops: nil, outer_loops: nil, robust: false
|
|
14
|
+
inner_loops: nil, outer_loops: nil, robust: false,
|
|
15
|
+
iterations: nil, lambda: nil
|
|
15
16
|
)
|
|
16
|
-
|
|
17
|
+
mstl = period.is_a?(Array)
|
|
18
|
+
|
|
19
|
+
if !mstl && period < 2
|
|
17
20
|
raise ArgumentError, "period must be greater than 1"
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
params = StlParams.new
|
|
21
24
|
|
|
22
|
-
params.seasonal_length = seasonal_length
|
|
25
|
+
params.seasonal_length = seasonal_length if !seasonal_length.nil? && !(mstl && seasonal_length.is_a?(Array))
|
|
23
26
|
params.trend_length = trend_length unless trend_length.nil?
|
|
24
27
|
params.low_pass_length = low_pass_length unless low_pass_length.nil?
|
|
25
28
|
|
|
@@ -42,12 +45,27 @@ module Stl
|
|
|
42
45
|
y = series
|
|
43
46
|
end
|
|
44
47
|
|
|
45
|
-
|
|
48
|
+
if mstl
|
|
49
|
+
mstl_params = MstlParams.new
|
|
50
|
+
mstl_params.iterations = iterations unless iterations.nil?
|
|
51
|
+
mstl_params.lambda = lambda unless lambda.nil?
|
|
52
|
+
mstl_params.seasonal_lengths = seasonal_length if seasonal_length.is_a?(Array)
|
|
53
|
+
mstl_params.stl_params = params
|
|
54
|
+
_decompose_mstl(y, period, mstl_params)
|
|
55
|
+
else
|
|
56
|
+
raise ArgumentError, "iterations requires MSTL" unless iterations.nil?
|
|
57
|
+
raise ArgumentError, "lambda requires MSTL" unless lambda.nil?
|
|
58
|
+
_decompose(y, period, params, outer_loops.nil? ? robust : outer_loops > 0)
|
|
59
|
+
end
|
|
46
60
|
end
|
|
47
61
|
|
|
48
62
|
def plot(series, result)
|
|
49
63
|
require "vega"
|
|
50
64
|
|
|
65
|
+
if mstl?(result)
|
|
66
|
+
raise "not implemented yet"
|
|
67
|
+
end
|
|
68
|
+
|
|
51
69
|
data =
|
|
52
70
|
if series.is_a?(Hash)
|
|
53
71
|
series.sort_by { |k, _| k }.map.with_index do |s, i|
|
|
@@ -101,13 +119,17 @@ module Stl
|
|
|
101
119
|
end
|
|
102
120
|
|
|
103
121
|
def seasonal_strength(result)
|
|
104
|
-
|
|
105
|
-
|
|
122
|
+
if mstl?(result)
|
|
123
|
+
result[:seasonal].map do |s|
|
|
124
|
+
strength(s, result[:remainder])
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
strength(result[:seasonal], result[:remainder])
|
|
128
|
+
end
|
|
106
129
|
end
|
|
107
130
|
|
|
108
131
|
def trend_strength(result)
|
|
109
|
-
|
|
110
|
-
[0, 1 - var(result[:remainder]) / var(tr)].max
|
|
132
|
+
strength(result[:trend], result[:remainder])
|
|
111
133
|
end
|
|
112
134
|
|
|
113
135
|
private
|
|
@@ -120,9 +142,18 @@ module Stl
|
|
|
120
142
|
end
|
|
121
143
|
end
|
|
122
144
|
|
|
145
|
+
def strength(component, remainder)
|
|
146
|
+
sr = component.zip(remainder).map { |a, b| a + b }
|
|
147
|
+
[0, 1 - var(remainder) / var(sr)].max
|
|
148
|
+
end
|
|
149
|
+
|
|
123
150
|
def var(series)
|
|
124
151
|
mean = series.sum / series.size.to_f
|
|
125
152
|
series.sum { |v| (v - mean) ** 2 } / (series.size.to_f - 1)
|
|
126
153
|
end
|
|
154
|
+
|
|
155
|
+
def mstl?(result)
|
|
156
|
+
result[:seasonal][0].is_a?(Array)
|
|
157
|
+
end
|
|
127
158
|
end
|
|
128
159
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stl-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Kane
|
|
@@ -55,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
55
55
|
- !ruby/object:Gem::Version
|
|
56
56
|
version: '0'
|
|
57
57
|
requirements: []
|
|
58
|
-
rubygems_version: 4.0.
|
|
58
|
+
rubygems_version: 4.0.16
|
|
59
59
|
specification_version: 4
|
|
60
60
|
summary: Seasonal-trend decomposition for Ruby
|
|
61
61
|
test_files: []
|