stl-rb 0.1.0 → 0.1.1
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 -1
- data/lib/stl/version.rb +1 -1
- data/lib/stl.rb +95 -31
- 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: 8c050d95d79bc0c5d644257c5cb5130d595dd7fe18c691b0e71eed7ecfef7a0d
|
4
|
+
data.tar.gz: 7c644155be7b848965299b62423e03ffa05bf17143fd76ca804bc5e4b36c03b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47a0248efdeb721218a9b5e1642e3e0b28735cb2692085c9ebc8f879d1cbc26bf2eb615611627d4a3873d9efda63da81fe214853eaae9901f74bddfc4bfac3e1
|
7
|
+
data.tar.gz: ef7bbb4ba3039a1f2703875165047dc87e4ad3bdfad5f3050f7faa3478560ca022735f226f171355394ef7e87edfd545dfa79f437026b4db336286e0592e6d47
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -34,7 +34,7 @@ series = User.group_by_day(:created_at).count
|
|
34
34
|
Stl.decompose(series, period: 7)
|
35
35
|
```
|
36
36
|
|
37
|
-
Series can also be an array without times
|
37
|
+
Series can also be an array without times
|
38
38
|
|
39
39
|
```ruby
|
40
40
|
series = [100, 150, 136, ...]
|
@@ -70,6 +70,20 @@ Stl.decompose(
|
|
70
70
|
)
|
71
71
|
```
|
72
72
|
|
73
|
+
## Plotting
|
74
|
+
|
75
|
+
Add [Vega](https://github.com/ankane/vega) to your application’s Gemfile:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
gem 'vega'
|
79
|
+
```
|
80
|
+
|
81
|
+
And use:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
Stl.plot(series, decompose_result)
|
85
|
+
```
|
86
|
+
|
73
87
|
## Credits
|
74
88
|
|
75
89
|
This library was ported from the [Fortran implementation](https://www.netlib.org/a/stl).
|
data/lib/stl/version.rb
CHANGED
data/lib/stl.rb
CHANGED
@@ -5,38 +5,102 @@ require "stl/ext"
|
|
5
5
|
require "stl/version"
|
6
6
|
|
7
7
|
module Stl
|
8
|
-
|
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
|
-
|
8
|
+
class << self
|
9
|
+
def decompose(
|
10
|
+
series, period:,
|
11
|
+
seasonal_length: nil, trend_length: nil, low_pass_length: nil,
|
12
|
+
seasonal_degree: nil, trend_degree: nil, low_pass_degree: nil,
|
13
|
+
seasonal_jump: nil, trend_jump: nil, low_pass_jump: nil,
|
14
|
+
inner_loops: nil, outer_loops: nil, robust: false
|
15
|
+
)
|
16
|
+
params = StlParams.new
|
17
|
+
|
18
|
+
params.seasonal_length(seasonal_length) unless seasonal_length.nil?
|
19
|
+
params.trend_length(trend_length) unless trend_length.nil?
|
20
|
+
params.low_pass_length(low_pass_length) unless low_pass_length.nil?
|
21
|
+
|
22
|
+
params.seasonal_degree(seasonal_degree) unless seasonal_degree.nil?
|
23
|
+
params.trend_degree(trend_degree) unless trend_degree.nil?
|
24
|
+
params.low_pass_degree(low_pass_degree) unless low_pass_degree.nil?
|
25
|
+
|
26
|
+
params.seasonal_jump(seasonal_jump) unless seasonal_jump.nil?
|
27
|
+
params.trend_jump(trend_jump) unless trend_jump.nil?
|
28
|
+
params.low_pass_jump(low_pass_jump) unless low_pass_jump.nil?
|
29
|
+
|
30
|
+
params.inner_loops(inner_loops) unless inner_loops.nil?
|
31
|
+
params.outer_loops(outer_loops) unless outer_loops.nil?
|
32
|
+
params.robust(robust) unless robust.nil?
|
33
|
+
|
34
|
+
if series.is_a?(Hash)
|
35
|
+
sorted = series.sort_by { |k, _| k }
|
36
|
+
y = sorted.map(&:last)
|
37
|
+
else
|
38
|
+
y = series
|
39
|
+
end
|
40
|
+
|
41
|
+
params.fit(y, period, outer_loops.nil? ? robust : outer_loops > 0)
|
42
|
+
end
|
43
|
+
|
44
|
+
def plot(series, result)
|
45
|
+
require "vega"
|
46
|
+
|
47
|
+
data =
|
48
|
+
if series.is_a?(Hash)
|
49
|
+
series.sort_by { |k, _| k }.map.with_index do |s, i|
|
50
|
+
{
|
51
|
+
x: iso8601(s[0]),
|
52
|
+
series: s[1],
|
53
|
+
seasonal: result[:seasonal][i],
|
54
|
+
trend: result[:trend][i],
|
55
|
+
remainder: result[:remainder][i]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
else
|
59
|
+
series.map.with_index do |v, i|
|
60
|
+
{
|
61
|
+
x: i,
|
62
|
+
series: v,
|
63
|
+
seasonal: result[:seasonal][i],
|
64
|
+
trend: result[:trend][i],
|
65
|
+
remainder: result[:remainder][i]
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if series.is_a?(Hash)
|
71
|
+
x = {field: "x", type: "temporal"}
|
72
|
+
x["scale"] = {type: "utc"} if series.keys.first.is_a?(Date)
|
73
|
+
else
|
74
|
+
x = {field: "x", type: "quantitative"}
|
75
|
+
end
|
76
|
+
x[:axis] = {title: nil, labelFontSize: 12}
|
77
|
+
|
78
|
+
charts =
|
79
|
+
["series", "seasonal", "trend", "remainder"].map do |field|
|
80
|
+
{
|
81
|
+
mark: {type: "line"},
|
82
|
+
encoding: {
|
83
|
+
x: x,
|
84
|
+
y: {field: field, type: "quantitative", scale: {zero: false}, axis: {labelFontSize: 12}}
|
85
|
+
},
|
86
|
+
width: "container",
|
87
|
+
height: 100
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
Vega.lite
|
92
|
+
.data(data)
|
93
|
+
.vconcat(charts)
|
38
94
|
end
|
39
95
|
|
40
|
-
|
96
|
+
private
|
97
|
+
|
98
|
+
def iso8601(v)
|
99
|
+
if v.is_a?(Date)
|
100
|
+
v.strftime("%Y-%m-%d")
|
101
|
+
else
|
102
|
+
v.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
|
103
|
+
end
|
104
|
+
end
|
41
105
|
end
|
42
106
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stl-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rice
|