stl-rb 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a71b48c0e3da1cc0b03002261e90a7ecb4001dc54fba6592a3639c97544e49fd
4
- data.tar.gz: b0c1a624f4ec360bae0a0ad9e22d78adb28d448553198fae905b9954fdc279ff
3
+ metadata.gz: 2aa2dc89bcf68293681a8fc62b7806ae94ba76561350e26679cbc776093dd2c6
4
+ data.tar.gz: 7c5e650e295730e29f344108bfdb802210c9db3b20b742adb7d64028c95e6c59
5
5
  SHA512:
6
- metadata.gz: 0e20f51dbe1ee17114708c8ced912c8cc3bdbae731ac3f92fb27bbcc13bae0fa1a8c7b4848d06b79f89210084d08b01e8293639dedc4582795e6455d856a3413
7
- data.tar.gz: 37ac79cd61e39b5b26c174057230e19c18bfb4d022ea288a4d1c8d540974c479290c35981339b8f42bfe766dd858f1aec4daa2a38b654c1bfb72f1ec29b61400
6
+ metadata.gz: 608fac4b9b1f16372f32e664a0887d2bb36e9842acbc51e6f524ec09f1cbcdbb680b1033235943e1c70ad0d8d093df76236005f064addddcc35d6d58eb233bf7
7
+ data.tar.gz: b931fd2c683e457c8845f824e3a5915775b1f671ac23ab4e70471f7424c45dc92bf58fb00e114d884b5d9ce859fbe5aac6314e5f11d8debfbfd82a923eb1ef6d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.2.0 (2023-02-01)
2
+
3
+ - Raise error when `period` is less than 2
4
+ - Dropped support for Ruby < 2.7
5
+
1
6
  ## 0.1.3 (2021-12-16)
2
7
 
3
8
  - Fixed installation error on macOS 12
data/README.md CHANGED
@@ -9,7 +9,7 @@ Seasonal-trend decomposition for Ruby
9
9
  Add this line to your application’s Gemfile:
10
10
 
11
11
  ```ruby
12
- gem 'stl-rb'
12
+ gem "stl-rb"
13
13
  ```
14
14
 
15
15
  ## Getting Started
@@ -75,7 +75,7 @@ Stl.decompose(
75
75
  Add [Vega](https://github.com/ankane/vega) to your application’s Gemfile:
76
76
 
77
77
  ```ruby
78
- gem 'vega'
78
+ gem "vega"
79
79
  ```
80
80
 
81
81
  And use:
data/ext/stl/stl.hpp CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * STL C++ v0.1.0
2
+ * STL C++ v0.1.2
3
3
  * https://github.com/ankane/stl-cpp
4
4
  * Unlicense OR MIT License
5
5
  *
@@ -14,6 +14,7 @@
14
14
 
15
15
  #include <algorithm>
16
16
  #include <cmath>
17
+ #include <numeric>
17
18
  #include <optional>
18
19
  #include <stdexcept>
19
20
  #include <vector>
@@ -91,14 +92,14 @@ void ess(const float* y, size_t n, size_t len, int ideg, size_t njump, bool user
91
92
  return;
92
93
  }
93
94
 
94
- auto nleft = 0;
95
- auto nright = 0;
95
+ size_t nleft = 0;
96
+ size_t nright = 0;
96
97
 
97
98
  auto newnj = std::min(njump, n - 1);
98
99
  if (len >= n) {
99
100
  nleft = 1;
100
101
  nright = n;
101
- for (auto i = 1; i <= n; i += newnj) {
102
+ for (size_t i = 1; i <= n; i += newnj) {
102
103
  auto ok = est(y, n, len, ideg, (float) i, &ys[i - 1], nleft, nright, res, userw, rw);
103
104
  if (!ok) {
104
105
  ys[i - 1] = y[i - 1];
@@ -108,7 +109,7 @@ void ess(const float* y, size_t n, size_t len, int ideg, size_t njump, bool user
108
109
  auto nsh = (len + 1) / 2;
109
110
  nleft = 1;
110
111
  nright = len;
111
- for (auto i = 1; i <= n; i++) { // fitted value at i
112
+ for (size_t i = 1; i <= n; i++) { // fitted value at i
112
113
  if (i > nsh && nright != n) {
113
114
  nleft += 1;
114
115
  nright += 1;
@@ -120,7 +121,7 @@ void ess(const float* y, size_t n, size_t len, int ideg, size_t njump, bool user
120
121
  }
121
122
  } else { // newnj greater than one, len less than n
122
123
  auto nsh = (len + 1) / 2;
123
- for (auto i = 1; i <= n; i += newnj) { // fitted value at i
124
+ for (size_t i = 1; i <= n; i += newnj) { // fitted value at i
124
125
  if (i < nsh) {
125
126
  nleft = 1;
126
127
  nright = len;
@@ -139,7 +140,7 @@ void ess(const float* y, size_t n, size_t len, int ideg, size_t njump, bool user
139
140
  }
140
141
 
141
142
  if (newnj != 1) {
142
- for (auto i = 1; i <= n - newnj; i += newnj) {
143
+ for (size_t i = 1; i <= n - newnj; i += newnj) {
143
144
  auto delta = (ys[i + newnj - 1] - ys[i - 1]) / ((float) newnj);
144
145
  for (auto j = i + 1; j <= i + newnj - 1; j++) {
145
146
  ys[j - 1] = ys[i - 1] + delta * ((float) (j - i));
@@ -167,7 +168,7 @@ void ma(const float* x, size_t n, size_t len, float* ave) {
167
168
  auto v = 0.0;
168
169
 
169
170
  // get the first average
170
- for (auto i = 0; i < len; i++) {
171
+ for (size_t i = 0; i < len; i++) {
171
172
  v += x[i];
172
173
  }
173
174
 
@@ -175,7 +176,7 @@ void ma(const float* x, size_t n, size_t len, float* ave) {
175
176
  if (newn > 1) {
176
177
  auto k = len;
177
178
  auto m = 0;
178
- for (auto j = 1; j < newn; j++) {
179
+ for (size_t j = 1; j < newn; j++) {
179
180
  // window down the array
180
181
  v = v - x[m] + x[k];
181
182
  ave[j] = v / flen;
@@ -192,7 +193,7 @@ void fts(const float* x, size_t n, size_t np, float* trend, float* work) {
192
193
  }
193
194
 
194
195
  void rwts(const float* y, size_t n, const float* fit, float* rw) {
195
- for (auto i = 0; i < n; i++) {
196
+ for (size_t i = 0; i < n; i++) {
196
197
  rw[i] = fabs(y[i] - fit[i]);
197
198
  }
198
199
 
@@ -206,7 +207,7 @@ void rwts(const float* y, size_t n, const float* fit, float* rw) {
206
207
  auto c9 = 0.999 * cmad;
207
208
  auto c1 = 0.001 * cmad;
208
209
 
209
- for (auto i = 0; i < n; i++) {
210
+ for (size_t i = 0; i < n; i++) {
210
211
  auto r = fabs(y[i] - fit[i]);
211
212
  if (r <= c1) {
212
213
  rw[i] = 1.0;
@@ -219,14 +220,14 @@ void rwts(const float* y, size_t n, const float* fit, float* rw) {
219
220
  }
220
221
 
221
222
  void ss(const float* y, size_t n, size_t np, size_t ns, int isdeg, size_t nsjump, bool userw, float* rw, float* season, float* work1, float* work2, float* work3, float* work4) {
222
- for (auto j = 1; j <= np; j++) {
223
- auto k = (n - j) / np + 1;
223
+ for (size_t j = 1; j <= np; j++) {
224
+ size_t k = (n - j) / np + 1;
224
225
 
225
- for (auto i = 1; i <= k; i++) {
226
+ for (size_t i = 1; i <= k; i++) {
226
227
  work1[i - 1] = y[(i - 1) * np + j - 1];
227
228
  }
228
229
  if (userw) {
229
- for (auto i = 1; i <= k; i++) {
230
+ for (size_t i = 1; i <= k; i++) {
230
231
  work3[i - 1] = rw[(i - 1) * np + j - 1];
231
232
  }
232
233
  }
@@ -243,25 +244,25 @@ void ss(const float* y, size_t n, size_t np, size_t ns, int isdeg, size_t nsjump
243
244
  if (!ok) {
244
245
  work2[k + 1] = work2[k];
245
246
  }
246
- for (auto m = 1; m <= k + 2; m++) {
247
+ for (size_t m = 1; m <= k + 2; m++) {
247
248
  season[(m - 1) * np + j - 1] = work2[m - 1];
248
249
  }
249
250
  }
250
251
  }
251
252
 
252
253
  void onestp(const float* y, size_t n, size_t np, size_t ns, size_t nt, size_t nl, int isdeg, int itdeg, int ildeg, size_t nsjump, size_t ntjump, size_t nljump, size_t ni, bool userw, float* rw, float* season, float* trend, float* work1, float* work2, float* work3, float* work4, float* work5) {
253
- for (auto j = 0; j < ni; j++) {
254
- for (auto i = 0; i < n; i++) {
254
+ for (size_t j = 0; j < ni; j++) {
255
+ for (size_t i = 0; i < n; i++) {
255
256
  work1[i] = y[i] - trend[i];
256
257
  }
257
258
 
258
259
  ss(work1, n, np, ns, isdeg, nsjump, userw, rw, work2, work3, work4, work5, season);
259
260
  fts(work2, n + 2 * np, np, work3, work1);
260
261
  ess(work3, n, nl, ildeg, nljump, false, work4, work1, work5);
261
- for (auto i = 0; i < n; i++) {
262
+ for (size_t i = 0; i < n; i++) {
262
263
  season[i] = work2[np + i] - work1[i];
263
264
  }
264
- for (auto i = 0; i < n; i++) {
265
+ for (size_t i = 0; i < n; i++) {
265
266
  work1[i] = y[i] - season[i];
266
267
  }
267
268
  ess(work1, n, nt, itdeg, ntjump, userw, rw, trend, work3);
@@ -269,15 +270,6 @@ void onestp(const float* y, size_t n, size_t np, size_t ns, size_t nt, size_t nl
269
270
  }
270
271
 
271
272
  void stl(const float* y, size_t n, size_t np, size_t ns, size_t nt, size_t nl, int isdeg, int itdeg, int ildeg, size_t nsjump, size_t ntjump, size_t nljump, size_t ni, size_t no, float* rw, float* season, float* trend) {
272
- auto work1 = std::vector<float>(n + 2 * np);
273
- auto work2 = std::vector<float>(n + 2 * np);
274
- auto work3 = std::vector<float>(n + 2 * np);
275
- auto work4 = std::vector<float>(n + 2 * np);
276
- auto work5 = std::vector<float>(n + 2 * np);
277
-
278
- auto userw = false;
279
- auto k = 0;
280
-
281
273
  if (ns < 3) {
282
274
  throw std::invalid_argument("seasonal_length must be at least 3");
283
275
  }
@@ -311,13 +303,22 @@ void stl(const float* y, size_t n, size_t np, size_t ns, size_t nt, size_t nl, i
311
303
  throw std::invalid_argument("low_pass_length must be odd");
312
304
  }
313
305
 
306
+ auto work1 = std::vector<float>(n + 2 * np);
307
+ auto work2 = std::vector<float>(n + 2 * np);
308
+ auto work3 = std::vector<float>(n + 2 * np);
309
+ auto work4 = std::vector<float>(n + 2 * np);
310
+ auto work5 = std::vector<float>(n + 2 * np);
311
+
312
+ auto userw = false;
313
+ size_t k = 0;
314
+
314
315
  while (true) {
315
316
  onestp(y, n, np, ns, nt, nl, isdeg, itdeg, ildeg, nsjump, ntjump, nljump, ni, userw, rw, season, trend, work1.data(), work2.data(), work3.data(), work4.data(), work5.data());
316
317
  k += 1;
317
318
  if (k > no) {
318
319
  break;
319
320
  }
320
- for (auto i = 0; i < n; i++) {
321
+ for (size_t i = 0; i < n; i++) {
321
322
  work1[i] = trend[i] + season[i];
322
323
  }
323
324
  rwts(y, n, work1.data(), rw);
@@ -325,18 +326,46 @@ void stl(const float* y, size_t n, size_t np, size_t ns, size_t nt, size_t nl, i
325
326
  }
326
327
 
327
328
  if (no <= 0) {
328
- for (auto i = 0; i < n; i++) {
329
+ for (size_t i = 0; i < n; i++) {
329
330
  rw[i] = 1.0;
330
331
  }
331
332
  }
332
333
  }
333
334
 
335
+ float var(const std::vector<float>& series) {
336
+ auto mean = std::accumulate(series.begin(), series.end(), 0.0) / series.size();
337
+ std::vector<float> tmp;
338
+ tmp.reserve(series.size());
339
+ for (auto v : series) {
340
+ tmp.push_back(pow(v - mean, 2));
341
+ }
342
+ return std::accumulate(tmp.begin(), tmp.end(), 0.0) / (series.size() - 1);
343
+ }
344
+
334
345
  class StlResult {
335
346
  public:
336
347
  std::vector<float> seasonal;
337
348
  std::vector<float> trend;
338
349
  std::vector<float> remainder;
339
350
  std::vector<float> weights;
351
+
352
+ inline float seasonal_strength() {
353
+ std::vector<float> sr;
354
+ sr.reserve(remainder.size());
355
+ for (size_t i = 0; i < remainder.size(); i++) {
356
+ sr.push_back(seasonal[i] + remainder[i]);
357
+ }
358
+ return std::max(0.0, 1.0 - var(remainder) / var(sr));
359
+ }
360
+
361
+ inline float trend_strength() {
362
+ std::vector<float> tr;
363
+ tr.reserve(remainder.size());
364
+ for (size_t i = 0; i < remainder.size(); i++) {
365
+ tr.push_back(trend[i] + remainder[i]);
366
+ }
367
+ return std::max(0.0, 1.0 - var(remainder) / var(tr));
368
+ }
340
369
  };
341
370
 
342
371
  class StlParams {
@@ -357,62 +386,62 @@ public:
357
386
  inline StlParams seasonal_length(size_t ns) {
358
387
  this->ns_ = ns;
359
388
  return *this;
360
- };
389
+ }
361
390
 
362
391
  inline StlParams trend_length(size_t nt) {
363
392
  this->nt_ = nt;
364
393
  return *this;
365
- };
394
+ }
366
395
 
367
396
  inline StlParams low_pass_length(size_t nl) {
368
397
  this->nl_ = nl;
369
398
  return *this;
370
- };
399
+ }
371
400
 
372
401
  inline StlParams seasonal_degree(int isdeg) {
373
402
  this->isdeg_ = isdeg;
374
403
  return *this;
375
- };
404
+ }
376
405
 
377
406
  inline StlParams trend_degree(int itdeg) {
378
407
  this->itdeg_ = itdeg;
379
408
  return *this;
380
- };
409
+ }
381
410
 
382
411
  inline StlParams low_pass_degree(int ildeg) {
383
412
  this->ildeg_ = ildeg;
384
413
  return *this;
385
- };
414
+ }
386
415
 
387
416
  inline StlParams seasonal_jump(size_t nsjump) {
388
417
  this->nsjump_ = nsjump;
389
418
  return *this;
390
- };
419
+ }
391
420
 
392
421
  inline StlParams trend_jump(size_t ntjump) {
393
422
  this->ntjump_ = ntjump;
394
423
  return *this;
395
- };
424
+ }
396
425
 
397
426
  inline StlParams low_pass_jump(size_t nljump) {
398
427
  this->nljump_ = nljump;
399
428
  return *this;
400
- };
429
+ }
401
430
 
402
431
  inline StlParams inner_loops(bool ni) {
403
432
  this->ni_ = ni;
404
433
  return *this;
405
- };
434
+ }
406
435
 
407
436
  inline StlParams outer_loops(bool no) {
408
437
  this->no_ = no;
409
438
  return *this;
410
- };
439
+ }
411
440
 
412
441
  inline StlParams robust(bool robust) {
413
442
  this->robust_ = robust;
414
443
  return *this;
415
- };
444
+ }
416
445
 
417
446
  StlResult fit(const float* y, size_t n, size_t np);
418
447
  StlResult fit(const std::vector<float>& y, size_t np);
@@ -468,7 +497,7 @@ StlResult StlParams::fit(const float* y, size_t n, size_t np) {
468
497
  stl(y, n, newnp, newns, nt, nl, isdeg, itdeg, ildeg, nsjump, ntjump, nljump, ni, no, res.weights.data(), res.seasonal.data(), res.trend.data());
469
498
 
470
499
  res.remainder.reserve(n);
471
- for (auto i = 0; i < n; i++) {
500
+ for (size_t i = 0; i < n; i++) {
472
501
  res.remainder.push_back(y[i] - res.seasonal[i] - res.trend[i]);
473
502
  }
474
503
 
data/lib/stl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Stl
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/stl-rb.rb CHANGED
@@ -1 +1 @@
1
- require "stl"
1
+ require_relative "stl"
data/lib/stl.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # ext
2
- require "stl/ext"
2
+ require_relative "stl/ext"
3
3
 
4
4
  # modules
5
- require "stl/version"
5
+ require_relative "stl/version"
6
6
 
7
7
  module Stl
8
8
  class << self
@@ -13,6 +13,10 @@ module Stl
13
13
  seasonal_jump: nil, trend_jump: nil, low_pass_jump: nil,
14
14
  inner_loops: nil, outer_loops: nil, robust: false
15
15
  )
16
+ if period < 2
17
+ raise ArgumentError, "period must be greater than 1"
18
+ end
19
+
16
20
  params = StlParams.new
17
21
 
18
22
  params.seasonal_length(seasonal_length) unless seasonal_length.nil?
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.3
4
+ version: 0.2.0
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-12-17 00:00:00.000000000 Z
11
+ date: 2023-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rice
@@ -51,14 +51,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '2.6'
54
+ version: '2.7'
55
55
  required_rubygems_version: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
59
  version: '0'
60
60
  requirements: []
61
- rubygems_version: 3.2.32
61
+ rubygems_version: 3.4.1
62
62
  signing_key:
63
63
  specification_version: 4
64
64
  summary: Seasonal-trend decomposition for Ruby