anomaly_detection 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +675 -0
- data/NOTICE.txt +15 -0
- data/README.md +89 -0
- data/ext/anomaly_detection/anomaly_detection.cpp +156 -0
- data/ext/anomaly_detection/cdflib.cpp +12126 -0
- data/ext/anomaly_detection/cdflib.hpp +123 -0
- data/ext/anomaly_detection/ext.cpp +23 -0
- data/ext/anomaly_detection/extconf.rb +5 -0
- data/ext/anomaly_detection/stl.hpp +458 -0
- data/lib/anomaly_detection/version.rb +3 -0
- data/lib/anomaly_detection.rb +22 -0
- data/licenses/LICENSE-MIT-stl-cpp.txt +21 -0
- data/licenses/LICENSE-cdflib.txt +165 -0
- data/licenses/UNLICENSE-stl-cpp.txt +24 -0
- metadata +72 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <string>
|
4
|
+
|
5
|
+
using std::string;
|
6
|
+
|
7
|
+
double algdiv ( double *a, double *b );
|
8
|
+
double alnrel ( double *a );
|
9
|
+
double apser ( double *a, double *b, double *x, double *eps );
|
10
|
+
double bcorr ( double *a0, double *b0 );
|
11
|
+
double beta ( double a, double b );
|
12
|
+
double beta_asym ( double *a, double *b, double *lambda, double *eps );
|
13
|
+
double beta_frac ( double *a, double *b, double *x, double *y, double *lambda,
|
14
|
+
double *eps );
|
15
|
+
void beta_grat ( double *a, double *b, double *x, double *y, double *w,
|
16
|
+
double *eps,int *ierr );
|
17
|
+
void beta_inc ( double *a, double *b, double *x, double *y, double *w,
|
18
|
+
double *w1, int *ierr );
|
19
|
+
void beta_inc_values ( int *n_data, double *a, double *b, double *x, double *fx );
|
20
|
+
double beta_log ( double *a0, double *b0 );
|
21
|
+
double beta_pser ( double *a, double *b, double *x, double *eps );
|
22
|
+
double beta_rcomp ( double *a, double *b, double *x, double *y );
|
23
|
+
double beta_rcomp1 ( int *mu, double *a, double *b, double *x, double *y );
|
24
|
+
double beta_up ( double *a, double *b, double *x, double *y, int *n, double *eps );
|
25
|
+
void binomial_cdf_values ( int *n_data, int *a, double *b, int *x, double *fx );
|
26
|
+
void cdfbet ( int *which, double *p, double *q, double *x, double *y,
|
27
|
+
double *a, double *b, int *status, double *bound );
|
28
|
+
void cdfbin ( int *which, double *p, double *q, double *s, double *xn,
|
29
|
+
double *pr, double *ompr, int *status, double *bound );
|
30
|
+
void cdfchi ( int *which, double *p, double *q, double *x, double *df,
|
31
|
+
int *status, double *bound );
|
32
|
+
void cdfchn ( int *which, double *p, double *q, double *x, double *df,
|
33
|
+
double *pnonc, int *status, double *bound );
|
34
|
+
void cdff ( int *which, double *p, double *q, double *f, double *dfn,
|
35
|
+
double *dfd, int *status, double *bound );
|
36
|
+
void cdffnc ( int *which, double *p, double *q, double *f, double *dfn,
|
37
|
+
double *dfd, double *phonc, int *status, double *bound );
|
38
|
+
void cdfgam ( int *which, double *p, double *q, double *x, double *shape,
|
39
|
+
double *scale, int *status, double *bound );
|
40
|
+
void cdfnbn ( int *which, double *p, double *q, double *s, double *xn,
|
41
|
+
double *pr, double *ompr, int *status, double *bound );
|
42
|
+
void cdfnor ( int *which, double *p, double *q, double *x, double *mean,
|
43
|
+
double *sd, int *status, double *bound );
|
44
|
+
void cdfpoi ( int *which, double *p, double *q, double *s, double *xlam,
|
45
|
+
int *status, double *bound );
|
46
|
+
void cdft ( int *which, double *p, double *q, double *t, double *df,
|
47
|
+
int *status, double *bound );
|
48
|
+
void chi_noncentral_cdf_values ( int *n_data, double *x, double *lambda,
|
49
|
+
int *df, double *cdf );
|
50
|
+
void chi_square_cdf_values ( int *n_data, int *a, double *x, double *fx );
|
51
|
+
void cumbet ( double *x, double *y, double *a, double *b, double *cum,
|
52
|
+
double *ccum );
|
53
|
+
void cumbin ( double *s, double *xn, double *pr, double *ompr,
|
54
|
+
double *cum, double *ccum );
|
55
|
+
void cumchi ( double *x, double *df, double *cum, double *ccum );
|
56
|
+
void cumchn ( double *x, double *df, double *pnonc, double *cum,
|
57
|
+
double *ccum );
|
58
|
+
void cumf ( double *f, double *dfn, double *dfd, double *cum, double *ccum );
|
59
|
+
void cumfnc ( double *f, double *dfn, double *dfd, double *pnonc,
|
60
|
+
double *cum, double *ccum );
|
61
|
+
void cumgam ( double *x, double *a, double *cum, double *ccum );
|
62
|
+
void cumnbn ( double *s, double *xn, double *pr, double *ompr,
|
63
|
+
double *cum, double *ccum );
|
64
|
+
void cumnor ( double *arg, double *result, double *ccum );
|
65
|
+
void cumpoi ( double *s, double *xlam, double *cum, double *ccum );
|
66
|
+
void cumt ( double *t, double *df, double *cum, double *ccum );
|
67
|
+
double dbetrm ( double *a, double *b );
|
68
|
+
double dexpm1 ( double *x );
|
69
|
+
double dinvnr ( double *p, double *q );
|
70
|
+
void dinvr ( int *status, double *x, double *fx,
|
71
|
+
unsigned long *qleft, unsigned long *qhi );
|
72
|
+
double dlanor ( double *x );
|
73
|
+
double dpmpar ( int *i );
|
74
|
+
void dstinv ( double *zsmall, double *zbig, double *zabsst,
|
75
|
+
double *zrelst, double *zstpmu, double *zabsto, double *zrelto );
|
76
|
+
double dstrem ( double *z );
|
77
|
+
void dstzr ( double *zxlo, double *zxhi, double *zabstl, double *zreltl );
|
78
|
+
double dt1 ( double *p, double *q, double *df );
|
79
|
+
void dzror ( int *status, double *x, double *fx, double *xlo,
|
80
|
+
double *xhi, unsigned long *qleft, unsigned long *qhi );
|
81
|
+
void erf_values ( int *n_data, double *x, double *fx );
|
82
|
+
double error_f ( double *x );
|
83
|
+
double error_fc ( int *ind, double *x );
|
84
|
+
double esum ( int *mu, double *x );
|
85
|
+
double eval_pol ( double a[], int *n, double *x );
|
86
|
+
double exparg ( int *l );
|
87
|
+
void f_cdf_values ( int *n_data, int *a, int *b, double *x, double *fx );
|
88
|
+
void f_noncentral_cdf_values ( int *n_data, int *a, int *b, double *lambda,
|
89
|
+
double *x, double *fx );
|
90
|
+
double fifdint ( double a );
|
91
|
+
double fifdmax1 ( double a, double b );
|
92
|
+
double fifdmin1 ( double a, double b );
|
93
|
+
double fifdsign ( double mag, double sign );
|
94
|
+
long fifidint ( double a );
|
95
|
+
long fifmod ( long a, long b );
|
96
|
+
double fpser ( double *a, double *b, double *x, double *eps );
|
97
|
+
void ftnstop ( string msg );
|
98
|
+
double gam1 ( double *a );
|
99
|
+
void gamma_inc ( double *a, double *x, double *ans, double *qans, int *ind );
|
100
|
+
void gamma_inc_inv ( double *a, double *x, double *x0, double *p, double *q,
|
101
|
+
int *ierr );
|
102
|
+
void gamma_inc_values ( int *n_data, double *a, double *x, double *fx );
|
103
|
+
double gamma_ln1 ( double *a );
|
104
|
+
double gamma_log ( double *a );
|
105
|
+
void gamma_rat1 ( double *a, double *x, double *r, double *p, double *q,
|
106
|
+
double *eps );
|
107
|
+
void gamma_values ( int *n_data, double *x, double *fx );
|
108
|
+
double gamma_x ( double *a );
|
109
|
+
double gsumln ( double *a, double *b );
|
110
|
+
int ipmpar ( int *i );
|
111
|
+
void negative_binomial_cdf_values ( int *n_data, int *f, int *s, double *p,
|
112
|
+
double *cdf );
|
113
|
+
void normal_cdf_values ( int *n_data, double *x, double *fx );
|
114
|
+
void poisson_cdf_values ( int *n_data, double *a, int *x, double *fx );
|
115
|
+
double psi ( double *xx );
|
116
|
+
void psi_values ( int *n_data, double *x, double *fx );
|
117
|
+
double rcomp ( double *a, double *x );
|
118
|
+
double rexp ( double *x );
|
119
|
+
double rlog ( double *x );
|
120
|
+
double rlog1 ( double *x );
|
121
|
+
void student_cdf_values ( int *n_data, int *a, double *x, double *fx );
|
122
|
+
double stvaln ( double *p );
|
123
|
+
void timestamp ( void );
|
@@ -0,0 +1,23 @@
|
|
1
|
+
// rice
|
2
|
+
#include <rice/rice.hpp>
|
3
|
+
#include <rice/stl.hpp>
|
4
|
+
|
5
|
+
std::vector<size_t> anomalies(const std::vector<float>& x, int period, float k, float alpha, const std::string& direction);
|
6
|
+
|
7
|
+
extern "C"
|
8
|
+
void Init_ext() {
|
9
|
+
auto rb_mAnomalyDetection = Rice::define_module("AnomalyDetection");
|
10
|
+
|
11
|
+
rb_mAnomalyDetection
|
12
|
+
.define_singleton_function(
|
13
|
+
"_detect",
|
14
|
+
[](std::vector<float> x, int period, float k, float alpha, const std::string& direction) {
|
15
|
+
auto res = anomalies(x, period, k, alpha, direction);
|
16
|
+
|
17
|
+
auto a = Rice::Array();
|
18
|
+
for (auto v : res) {
|
19
|
+
a.push(v);
|
20
|
+
}
|
21
|
+
return a;
|
22
|
+
});
|
23
|
+
}
|
@@ -0,0 +1,458 @@
|
|
1
|
+
/*!
|
2
|
+
* STL C++ v0.1.0
|
3
|
+
* https://github.com/ankane/stl-cpp
|
4
|
+
* Unlicense OR MIT License
|
5
|
+
*
|
6
|
+
* Ported from https://www.netlib.org/a/stl
|
7
|
+
*
|
8
|
+
* Cleveland, R. B., Cleveland, W. S., McRae, J. E., & Terpenning, I. (1990).
|
9
|
+
* STL: A Seasonal-Trend Decomposition Procedure Based on Loess.
|
10
|
+
* Journal of Official Statistics, 6(1), 3-33.
|
11
|
+
*/
|
12
|
+
|
13
|
+
#pragma once
|
14
|
+
|
15
|
+
#include <algorithm>
|
16
|
+
#include <cassert>
|
17
|
+
#include <cmath>
|
18
|
+
#include <optional>
|
19
|
+
#include <vector>
|
20
|
+
|
21
|
+
namespace stl {
|
22
|
+
|
23
|
+
bool est(const float* y, size_t n, size_t len, int ideg, float xs, float* ys, size_t nleft, size_t nright, float* w, bool userw, const float* rw) {
|
24
|
+
auto range = ((float) n) - 1.0;
|
25
|
+
auto h = std::max(xs - ((float) nleft), ((float) nright) - xs);
|
26
|
+
|
27
|
+
if (len > n) {
|
28
|
+
h += (float) ((len - n) / 2);
|
29
|
+
}
|
30
|
+
|
31
|
+
auto h9 = 0.999 * h;
|
32
|
+
auto h1 = 0.001 * h;
|
33
|
+
|
34
|
+
// compute weights
|
35
|
+
auto a = 0.0;
|
36
|
+
for (auto j = nleft; j <= nright; j++) {
|
37
|
+
w[j - 1] = 0.0;
|
38
|
+
auto r = fabs(((float) j) - xs);
|
39
|
+
if (r <= h9) {
|
40
|
+
if (r <= h1) {
|
41
|
+
w[j - 1] = 1.0;
|
42
|
+
} else {
|
43
|
+
w[j - 1] = pow(1.0 - pow(r / h, 3), 3);
|
44
|
+
}
|
45
|
+
if (userw) {
|
46
|
+
w[j - 1] *= rw[j - 1];
|
47
|
+
}
|
48
|
+
a += w[j - 1];
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
if (a <= 0.0) {
|
53
|
+
return false;
|
54
|
+
} else { // weighted least squares
|
55
|
+
for (auto j = nleft; j <= nright; j++) { // make sum of w(j) == 1
|
56
|
+
w[j - 1] /= a;
|
57
|
+
}
|
58
|
+
|
59
|
+
if (h > 0.0 && ideg > 0) { // use linear fit
|
60
|
+
auto a = 0.0;
|
61
|
+
for (auto j = nleft; j <= nright; j++) { // weighted center of x values
|
62
|
+
a += w[j - 1] * ((float) j);
|
63
|
+
}
|
64
|
+
auto b = xs - a;
|
65
|
+
auto c = 0.0;
|
66
|
+
for (auto j = nleft; j <= nright; j++) {
|
67
|
+
c += w[j - 1] * pow(((float) j) - a, 2);
|
68
|
+
}
|
69
|
+
if (sqrt(c) > 0.001 * range) {
|
70
|
+
b /= c;
|
71
|
+
|
72
|
+
// points are spread out enough to compute slope
|
73
|
+
for (auto j = nleft; j <= nright; j++) {
|
74
|
+
w[j - 1] *= b * (((float) j) - a) + 1.0;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
*ys = 0.0;
|
80
|
+
for (auto j = nleft; j <= nright; j++) {
|
81
|
+
*ys += w[j - 1] * y[j - 1];
|
82
|
+
}
|
83
|
+
|
84
|
+
return true;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
void ess(const float* y, size_t n, size_t len, int ideg, size_t njump, bool userw, const float* rw, float* ys, float* res) {
|
89
|
+
if (n < 2) {
|
90
|
+
ys[0] = y[0];
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
|
94
|
+
auto nleft = 0;
|
95
|
+
auto nright = 0;
|
96
|
+
|
97
|
+
auto newnj = std::min(njump, n - 1);
|
98
|
+
if (len >= n) {
|
99
|
+
nleft = 1;
|
100
|
+
nright = n;
|
101
|
+
for (auto i = 1; i <= n; i += newnj) {
|
102
|
+
auto ok = est(y, n, len, ideg, (float) i, &ys[i - 1], nleft, nright, res, userw, rw);
|
103
|
+
if (!ok) {
|
104
|
+
ys[i - 1] = y[i - 1];
|
105
|
+
}
|
106
|
+
}
|
107
|
+
} else if (newnj == 1) { // newnj equal to one, len less than n
|
108
|
+
auto nsh = (len + 1) / 2;
|
109
|
+
nleft = 1;
|
110
|
+
nright = len;
|
111
|
+
for (auto i = 1; i <= n; i++) { // fitted value at i
|
112
|
+
if (i > nsh && nright != n) {
|
113
|
+
nleft += 1;
|
114
|
+
nright += 1;
|
115
|
+
}
|
116
|
+
auto ok = est(y, n, len, ideg, (float) i, &ys[i - 1], nleft, nright, res, userw, rw);
|
117
|
+
if (!ok) {
|
118
|
+
ys[i - 1] = y[i - 1];
|
119
|
+
}
|
120
|
+
}
|
121
|
+
} else { // newnj greater than one, len less than n
|
122
|
+
auto nsh = (len + 1) / 2;
|
123
|
+
for (auto i = 1; i <= n; i += newnj) { // fitted value at i
|
124
|
+
if (i < nsh) {
|
125
|
+
nleft = 1;
|
126
|
+
nright = len;
|
127
|
+
} else if (i >= n - nsh + 1) {
|
128
|
+
nleft = n - len + 1;
|
129
|
+
nright = n;
|
130
|
+
} else {
|
131
|
+
nleft = i - nsh + 1;
|
132
|
+
nright = len + i - nsh;
|
133
|
+
}
|
134
|
+
auto ok = est(y, n, len, ideg, (float) i, &ys[i - 1], nleft, nright, res, userw, rw);
|
135
|
+
if (!ok) {
|
136
|
+
ys[i - 1] = y[i - 1];
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
if (newnj != 1) {
|
142
|
+
for (auto i = 1; i <= n - newnj; i += newnj) {
|
143
|
+
auto delta = (ys[i + newnj - 1] - ys[i - 1]) / ((float) newnj);
|
144
|
+
for (auto j = i + 1; j <= i + newnj - 1; j++) {
|
145
|
+
ys[j - 1] = ys[i - 1] + delta * ((float) (j - i));
|
146
|
+
}
|
147
|
+
}
|
148
|
+
auto k = ((n - 1) / newnj) * newnj + 1;
|
149
|
+
if (k != n) {
|
150
|
+
auto ok = est(y, n, len, ideg, (float) n, &ys[n - 1], nleft, nright, res, userw, rw);
|
151
|
+
if (!ok) {
|
152
|
+
ys[n - 1] = y[n - 1];
|
153
|
+
if (k != n - 1) {
|
154
|
+
auto delta = (ys[n - 1] - ys[k - 1]) / ((float) (n - k));
|
155
|
+
for (auto j = k + 1; j <= n - 1; j++) {
|
156
|
+
ys[j - 1] = ys[k - 1] + delta * ((float) (j - k));
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
void ma(const float* x, size_t n, size_t len, float* ave) {
|
165
|
+
auto newn = n - len + 1;
|
166
|
+
auto flen = (float) len;
|
167
|
+
auto v = 0.0;
|
168
|
+
|
169
|
+
// get the first average
|
170
|
+
for (auto i = 0; i < len; i++) {
|
171
|
+
v += x[i];
|
172
|
+
}
|
173
|
+
|
174
|
+
ave[0] = v / flen;
|
175
|
+
if (newn > 1) {
|
176
|
+
auto k = len;
|
177
|
+
auto m = 0;
|
178
|
+
for (auto j = 1; j < newn; j++) {
|
179
|
+
// window down the array
|
180
|
+
v = v - x[m] + x[k];
|
181
|
+
ave[j] = v / flen;
|
182
|
+
k += 1;
|
183
|
+
m += 1;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
void fts(const float* x, size_t n, size_t np, float* trend, float* work) {
|
189
|
+
ma(x, n, np, trend);
|
190
|
+
ma(trend, n - np + 1, np, work);
|
191
|
+
ma(work, n - 2 * np + 2, 3, trend);
|
192
|
+
}
|
193
|
+
|
194
|
+
void rwts(const float* y, size_t n, const float* fit, float* rw) {
|
195
|
+
for (auto i = 0; i < n; i++) {
|
196
|
+
rw[i] = fabs(y[i] - fit[i]);
|
197
|
+
}
|
198
|
+
|
199
|
+
auto mid1 = (n - 1) / 2;
|
200
|
+
auto mid2 = n / 2;
|
201
|
+
|
202
|
+
// sort
|
203
|
+
std::sort(rw, rw + n);
|
204
|
+
|
205
|
+
auto cmad = 3.0 * (rw[mid1] + rw[mid2]); // 6 * median abs resid
|
206
|
+
auto c9 = 0.999 * cmad;
|
207
|
+
auto c1 = 0.001 * cmad;
|
208
|
+
|
209
|
+
for (auto i = 0; i < n; i++) {
|
210
|
+
auto r = fabs(y[i] - fit[i]);
|
211
|
+
if (r <= c1) {
|
212
|
+
rw[i] = 1.0;
|
213
|
+
} else if (r <= c9) {
|
214
|
+
rw[i] = pow(1.0 - pow(r / cmad, 2), 2);
|
215
|
+
} else {
|
216
|
+
rw[i] = 0.0;
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
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;
|
224
|
+
|
225
|
+
for (auto i = 1; i <= k; i++) {
|
226
|
+
work1[i - 1] = y[(i - 1) * np + j - 1];
|
227
|
+
}
|
228
|
+
if (userw) {
|
229
|
+
for (auto i = 1; i <= k; i++) {
|
230
|
+
work3[i - 1] = rw[(i - 1) * np + j - 1];
|
231
|
+
}
|
232
|
+
}
|
233
|
+
ess(work1, k, ns, isdeg, nsjump, userw, work3, work2 + 1, work4);
|
234
|
+
auto xs = 0.0;
|
235
|
+
auto nright = std::min(ns, k);
|
236
|
+
auto ok = est(work1, k, ns, isdeg, xs, &work2[0], 1, nright, work4, userw, work3);
|
237
|
+
if (!ok) {
|
238
|
+
work2[0] = work2[1];
|
239
|
+
}
|
240
|
+
xs = k + 1;
|
241
|
+
size_t nleft = std::max(1, (int) k - (int) ns + 1);
|
242
|
+
ok = est(work1, k, ns, isdeg, xs, &work2[k + 1], nleft, k, work4, userw, work3);
|
243
|
+
if (!ok) {
|
244
|
+
work2[k + 1] = work2[k];
|
245
|
+
}
|
246
|
+
for (auto m = 1; m <= k + 2; m++) {
|
247
|
+
season[(m - 1) * np + j - 1] = work2[m - 1];
|
248
|
+
}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
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++) {
|
255
|
+
work1[i] = y[i] - trend[i];
|
256
|
+
}
|
257
|
+
|
258
|
+
ss(work1, n, np, ns, isdeg, nsjump, userw, rw, work2, work3, work4, work5, season);
|
259
|
+
fts(work2, n + 2 * np, np, work3, work1);
|
260
|
+
ess(work3, n, nl, ildeg, nljump, false, work4, work1, work5);
|
261
|
+
for (auto i = 0; i < n; i++) {
|
262
|
+
season[i] = work2[np + i] - work1[i];
|
263
|
+
}
|
264
|
+
for (auto i = 0; i < n; i++) {
|
265
|
+
work1[i] = y[i] - season[i];
|
266
|
+
}
|
267
|
+
ess(work1, n, nt, itdeg, ntjump, userw, rw, trend, work3);
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
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
|
+
assert(ns >= 3);
|
282
|
+
assert(nt >= 3);
|
283
|
+
assert(nl >= 3);
|
284
|
+
assert(np >= 2);
|
285
|
+
|
286
|
+
assert(isdeg == 0 || isdeg == 1);
|
287
|
+
assert(itdeg == 0 || itdeg == 1);
|
288
|
+
assert(ildeg == 0 || ildeg == 1);
|
289
|
+
|
290
|
+
assert(ns % 2 == 1);
|
291
|
+
assert(nt % 2 == 1);
|
292
|
+
assert(nl % 2 == 1);
|
293
|
+
|
294
|
+
while (true) {
|
295
|
+
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());
|
296
|
+
k += 1;
|
297
|
+
if (k > no) {
|
298
|
+
break;
|
299
|
+
}
|
300
|
+
for (auto i = 0; i < n; i++) {
|
301
|
+
work1[i] = trend[i] + season[i];
|
302
|
+
}
|
303
|
+
rwts(y, n, work1.data(), rw);
|
304
|
+
userw = true;
|
305
|
+
}
|
306
|
+
|
307
|
+
if (no <= 0) {
|
308
|
+
for (auto i = 0; i < n; i++) {
|
309
|
+
rw[i] = 1.0;
|
310
|
+
}
|
311
|
+
}
|
312
|
+
}
|
313
|
+
|
314
|
+
class StlResult {
|
315
|
+
public:
|
316
|
+
std::vector<float> seasonal;
|
317
|
+
std::vector<float> trend;
|
318
|
+
std::vector<float> remainder;
|
319
|
+
std::vector<float> weights;
|
320
|
+
};
|
321
|
+
|
322
|
+
class StlParams {
|
323
|
+
std::optional<size_t> ns_ = std::nullopt;
|
324
|
+
std::optional<size_t> nt_ = std::nullopt;
|
325
|
+
std::optional<size_t> nl_ = std::nullopt;
|
326
|
+
int isdeg_ = 0;
|
327
|
+
int itdeg_ = 1;
|
328
|
+
std::optional<int> ildeg_ = std::nullopt;
|
329
|
+
std::optional<size_t> nsjump_ = std::nullopt;
|
330
|
+
std::optional<size_t> ntjump_ = std::nullopt;
|
331
|
+
std::optional<size_t> nljump_ = std::nullopt;
|
332
|
+
std::optional<size_t> ni_ = std::nullopt;
|
333
|
+
std::optional<size_t> no_ = std::nullopt;
|
334
|
+
bool robust_ = false;
|
335
|
+
|
336
|
+
public:
|
337
|
+
inline StlParams seasonal_length(size_t ns) {
|
338
|
+
this->ns_ = ns;
|
339
|
+
return *this;
|
340
|
+
};
|
341
|
+
|
342
|
+
inline StlParams trend_length(size_t nt) {
|
343
|
+
this->nt_ = nt;
|
344
|
+
return *this;
|
345
|
+
};
|
346
|
+
|
347
|
+
inline StlParams low_pass_length(size_t nl) {
|
348
|
+
this->nl_ = nl;
|
349
|
+
return *this;
|
350
|
+
};
|
351
|
+
|
352
|
+
inline StlParams seasonal_degree(int isdeg) {
|
353
|
+
this->isdeg_ = isdeg;
|
354
|
+
return *this;
|
355
|
+
};
|
356
|
+
|
357
|
+
inline StlParams trend_degree(int itdeg) {
|
358
|
+
this->itdeg_ = itdeg;
|
359
|
+
return *this;
|
360
|
+
};
|
361
|
+
|
362
|
+
inline StlParams low_pass_degree(int ildeg) {
|
363
|
+
this->ildeg_ = ildeg;
|
364
|
+
return *this;
|
365
|
+
};
|
366
|
+
|
367
|
+
inline StlParams seasonal_jump(size_t nsjump) {
|
368
|
+
this->nsjump_ = nsjump;
|
369
|
+
return *this;
|
370
|
+
};
|
371
|
+
|
372
|
+
inline StlParams trend_jump(size_t ntjump) {
|
373
|
+
this->ntjump_ = ntjump;
|
374
|
+
return *this;
|
375
|
+
};
|
376
|
+
|
377
|
+
inline StlParams low_pass_jump(size_t nljump) {
|
378
|
+
this->nljump_ = nljump;
|
379
|
+
return *this;
|
380
|
+
};
|
381
|
+
|
382
|
+
inline StlParams inner_loops(bool ni) {
|
383
|
+
this->ni_ = ni;
|
384
|
+
return *this;
|
385
|
+
};
|
386
|
+
|
387
|
+
inline StlParams outer_loops(bool no) {
|
388
|
+
this->no_ = no;
|
389
|
+
return *this;
|
390
|
+
};
|
391
|
+
|
392
|
+
inline StlParams robust(bool robust) {
|
393
|
+
this->robust_ = robust;
|
394
|
+
return *this;
|
395
|
+
};
|
396
|
+
|
397
|
+
StlResult fit(const float* y, size_t n, size_t np);
|
398
|
+
StlResult fit(const std::vector<float>& y, size_t np);
|
399
|
+
};
|
400
|
+
|
401
|
+
StlParams params() {
|
402
|
+
return StlParams();
|
403
|
+
}
|
404
|
+
|
405
|
+
StlResult StlParams::fit(const float* y, size_t n, size_t np) {
|
406
|
+
auto ns = this->ns_.value_or(np);
|
407
|
+
|
408
|
+
auto isdeg = this->isdeg_;
|
409
|
+
auto itdeg = this->itdeg_;
|
410
|
+
|
411
|
+
auto res = StlResult {
|
412
|
+
std::vector<float>(n),
|
413
|
+
std::vector<float>(n),
|
414
|
+
std::vector<float>(),
|
415
|
+
std::vector<float>(n)
|
416
|
+
};
|
417
|
+
|
418
|
+
auto ildeg = this->ildeg_.value_or(itdeg);
|
419
|
+
auto newns = std::max(ns, (size_t) 3);
|
420
|
+
if (newns % 2 == 0) {
|
421
|
+
newns += 1;
|
422
|
+
}
|
423
|
+
|
424
|
+
auto newnp = std::max(np, (size_t) 2);
|
425
|
+
auto nt = (size_t) ceil((1.5 * newnp) / (1.0 - 1.5 / (float) newns));
|
426
|
+
nt = this->nt_.value_or(nt);
|
427
|
+
nt = std::max(nt, (size_t) 3);
|
428
|
+
if (nt % 2 == 0) {
|
429
|
+
nt += 1;
|
430
|
+
}
|
431
|
+
|
432
|
+
auto nl = this->nl_.value_or(newnp);
|
433
|
+
if (nl % 2 == 0 && !this->nl_.has_value()) {
|
434
|
+
nl += 1;
|
435
|
+
}
|
436
|
+
|
437
|
+
auto ni = this->ni_.value_or(this->robust_ ? 1 : 2);
|
438
|
+
auto no = this->no_.value_or(this->robust_ ? 15 : 0);
|
439
|
+
|
440
|
+
auto nsjump = this->nsjump_.value_or((size_t) ceil(((float) newns) / 10.0));
|
441
|
+
auto ntjump = this->ntjump_.value_or((size_t) ceil(((float) nt) / 10.0));
|
442
|
+
auto nljump = this->nljump_.value_or((size_t) ceil(((float) nl) / 10.0));
|
443
|
+
|
444
|
+
stl(y, n, newnp, newns, nt, nl, isdeg, itdeg, ildeg, nsjump, ntjump, nljump, ni, no, res.weights.data(), res.seasonal.data(), res.trend.data());
|
445
|
+
|
446
|
+
res.remainder.reserve(n);
|
447
|
+
for (auto i = 0; i < n; i++) {
|
448
|
+
res.remainder.push_back(y[i] - res.seasonal[i] - res.trend[i]);
|
449
|
+
}
|
450
|
+
|
451
|
+
return res;
|
452
|
+
}
|
453
|
+
|
454
|
+
StlResult StlParams::fit(const std::vector<float>& y, size_t np) {
|
455
|
+
return StlParams::fit(y.data(), y.size(), np);
|
456
|
+
}
|
457
|
+
|
458
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# extensions
|
2
|
+
require "anomaly_detection/ext"
|
3
|
+
|
4
|
+
# modules
|
5
|
+
require "anomaly_detection/version"
|
6
|
+
|
7
|
+
module AnomalyDetection
|
8
|
+
def self.detect(series, period:, max_anoms: 0.1, alpha: 0.05, direction: "both")
|
9
|
+
raise ArgumentError, "series must contain at least 2 periods" if series.size < period * 2
|
10
|
+
|
11
|
+
if series.is_a?(Hash)
|
12
|
+
sorted = series.sort_by { |k, _| k }
|
13
|
+
x = sorted.map(&:last)
|
14
|
+
else
|
15
|
+
x = series
|
16
|
+
end
|
17
|
+
|
18
|
+
res = _detect(x, period, max_anoms, alpha, direction)
|
19
|
+
res.map! { |i| sorted[i][0] } if series.is_a?(Hash)
|
20
|
+
res
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Contributors
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|