anomaly_detection 0.1.3 → 0.2.0
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 +10 -0
- data/NOTICE.txt +1 -1
- data/README.md +2 -2
- data/ext/anomaly_detection/anomaly_detection.hpp +200 -2
- data/ext/anomaly_detection/dist.h +105 -49
- data/ext/anomaly_detection/ext.cpp +9 -3
- data/ext/anomaly_detection/stl.hpp +103 -50
- data/lib/anomaly_detection/version.rb +1 -1
- data/lib/anomaly_detection.rb +57 -2
- data/licenses/LICENSE-AnomalyDetection-cpp.txt +675 -0
- data/licenses/NOTICE-AnomalyDetection-cpp.txt +15 -0
- metadata +6 -5
- data/ext/anomaly_detection/anomaly_detection.cpp +0 -139
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da5eb71023f77a4c05e6322c020ef602e8e22b7b5ba516fce99679af702c881d
|
4
|
+
data.tar.gz: 26560c8dd893c491bd3094202ff82ae33eefdcdba74fe4386b006f7f522906df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec2e1459ca2410ee6ab1bce3fe9c528d6419b75e10c6448f1fe5b3030a2e3d8de320a23a9bded17702a01fd23d112007b909c8611e2da6c1ff4f8521352c89ac
|
7
|
+
data.tar.gz: ad150705d6e32a111c3bc044ef7f99910beebe572e07799719e72118422b7a9e6439943cac8b86d613537d7d0fb52cba86a668faf78d135abc888ce3737f8104
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 0.2.0 (2023-01-31)
|
2
|
+
|
3
|
+
- Added experimental support for auto-detecting period
|
4
|
+
- Fixed result when no seasonality (period is less than 2)
|
5
|
+
- Dropped support for Ruby < 2.7
|
6
|
+
|
7
|
+
## 0.1.4 (2022-03-19)
|
8
|
+
|
9
|
+
- Fixed initial median calculation
|
10
|
+
|
1
11
|
## 0.1.3 (2022-01-03)
|
2
12
|
|
3
13
|
- Switched to dist.h
|
data/NOTICE.txt
CHANGED
data/README.md
CHANGED
@@ -11,7 +11,7 @@ Learn [how it works](https://blog.twitter.com/engineering/en_us/a/2015/introduci
|
|
11
11
|
Add this line to your application’s Gemfile:
|
12
12
|
|
13
13
|
```ruby
|
14
|
-
gem
|
14
|
+
gem "anomaly_detection"
|
15
15
|
```
|
16
16
|
|
17
17
|
## Getting Started
|
@@ -63,7 +63,7 @@ AnomalyDetection.detect(
|
|
63
63
|
Add [Vega](https://github.com/ankane/vega) to your application’s Gemfile:
|
64
64
|
|
65
65
|
```ruby
|
66
|
-
gem
|
66
|
+
gem "vega"
|
67
67
|
```
|
68
68
|
|
69
69
|
And use:
|
@@ -1,12 +1,210 @@
|
|
1
|
+
/*!
|
2
|
+
* AnomalyDetection.cpp v0.1.3
|
3
|
+
* https://github.com/ankane/AnomalyDetection.cpp
|
4
|
+
* GPL-3.0-or-later License
|
5
|
+
*/
|
6
|
+
|
1
7
|
#pragma once
|
2
8
|
|
3
|
-
#include <
|
9
|
+
#include <functional>
|
10
|
+
#include <iostream>
|
11
|
+
#include <iterator>
|
12
|
+
#include <numeric>
|
4
13
|
#include <vector>
|
5
14
|
|
15
|
+
#include "dist.h"
|
16
|
+
#include "stl.hpp"
|
17
|
+
|
6
18
|
namespace anomaly_detection {
|
7
19
|
|
8
20
|
enum Direction { Positive, Negative, Both };
|
9
21
|
|
10
|
-
|
22
|
+
float median_sorted(const std::vector<float>& sorted) {
|
23
|
+
return (sorted[(sorted.size() - 1) / 2] + sorted[sorted.size() / 2]) / 2.0;
|
24
|
+
}
|
25
|
+
|
26
|
+
float median(const std::vector<float>& data) {
|
27
|
+
std::vector<float> sorted(data);
|
28
|
+
std::sort(sorted.begin(), sorted.end());
|
29
|
+
return median_sorted(sorted);
|
30
|
+
}
|
31
|
+
|
32
|
+
float mad(const std::vector<float>& data, float med) {
|
33
|
+
std::vector<float> res;
|
34
|
+
res.reserve(data.size());
|
35
|
+
for (auto v : data) {
|
36
|
+
res.push_back(fabs(v - med));
|
37
|
+
}
|
38
|
+
std::sort(res.begin(), res.end());
|
39
|
+
return 1.4826 * median_sorted(res);
|
40
|
+
}
|
41
|
+
|
42
|
+
std::vector<size_t> detect_anoms(const std::vector<float>& data, size_t num_obs_per_period, float k, float alpha, bool one_tail, bool upper_tail, bool verbose, std::function<void()> callback) {
|
43
|
+
auto n = data.size();
|
44
|
+
|
45
|
+
// Check to make sure we have at least two periods worth of data for anomaly context
|
46
|
+
if (n < num_obs_per_period * 2) {
|
47
|
+
throw std::invalid_argument("series must contain at least 2 periods");
|
48
|
+
}
|
49
|
+
|
50
|
+
// Handle NANs
|
51
|
+
auto nan = std::count_if(data.begin(), data.end(), [](const auto& value) { return std::isnan(value); });
|
52
|
+
if (nan > 0) {
|
53
|
+
throw std::invalid_argument("series contains NANs");
|
54
|
+
}
|
55
|
+
|
56
|
+
std::vector<float> data2;
|
57
|
+
data2.reserve(n);
|
58
|
+
auto med = median(data);
|
59
|
+
|
60
|
+
if (num_obs_per_period > 1) {
|
61
|
+
// Decompose data. This returns a univarite remainder which will be used for anomaly detection. Optionally, we might NOT decompose.
|
62
|
+
auto data_decomp = stl::params().robust(true).seasonal_length(data.size() * 10 + 1).fit(data, num_obs_per_period);
|
63
|
+
auto seasonal = data_decomp.seasonal;
|
64
|
+
|
65
|
+
for (size_t i = 0; i < n; i++) {
|
66
|
+
data2.push_back(data[i] - seasonal[i] - med);
|
67
|
+
}
|
68
|
+
} else {
|
69
|
+
for (size_t i = 0; i < n; i++) {
|
70
|
+
data2.push_back(data[i] - med);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
auto num_anoms = 0;
|
75
|
+
auto max_outliers = (size_t) n * k;
|
76
|
+
std::vector<size_t> anomalies;
|
77
|
+
anomalies.reserve(max_outliers);
|
78
|
+
|
79
|
+
// Sort data for fast median
|
80
|
+
// Use stable sort for indexes for deterministic results
|
81
|
+
std::vector<size_t> indexes(n);
|
82
|
+
std::iota(indexes.begin(), indexes.end(), 0);
|
83
|
+
std::stable_sort(indexes.begin(), indexes.end(), [&data2](size_t a, size_t b) { return data2[a] < data2[b]; });
|
84
|
+
std::sort(data2.begin(), data2.end());
|
85
|
+
|
86
|
+
// Compute test statistic until r=max_outliers values have been removed from the sample
|
87
|
+
for (auto i = 1; i <= max_outliers; i++) {
|
88
|
+
if (verbose) {
|
89
|
+
std::cout << i << " / " << max_outliers << " completed" << std::endl;
|
90
|
+
}
|
91
|
+
|
92
|
+
// TODO Improve performance between loop iterations
|
93
|
+
auto ma = median_sorted(data2);
|
94
|
+
std::vector<float> ares;
|
95
|
+
ares.reserve(data2.size());
|
96
|
+
if (one_tail) {
|
97
|
+
if (upper_tail) {
|
98
|
+
for (auto v : data2) {
|
99
|
+
ares.push_back(v - ma);
|
100
|
+
}
|
101
|
+
} else {
|
102
|
+
for (auto v : data2) {
|
103
|
+
ares.push_back(ma - v);
|
104
|
+
}
|
105
|
+
}
|
106
|
+
} else {
|
107
|
+
for (auto v : data2) {
|
108
|
+
ares.push_back(fabs(v - ma));
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
// Protect against constant time series
|
113
|
+
auto data_sigma = mad(data2, ma);
|
114
|
+
if (data_sigma == 0.0) {
|
115
|
+
break;
|
116
|
+
}
|
117
|
+
|
118
|
+
auto iter = std::max_element(ares.begin(), ares.end());
|
119
|
+
auto r_idx_i = std::distance(ares.begin(), iter);
|
120
|
+
|
121
|
+
// Only need to take sigma of r for performance
|
122
|
+
auto r = ares[r_idx_i] / data_sigma;
|
123
|
+
|
124
|
+
anomalies.push_back(indexes[r_idx_i]);
|
125
|
+
data2.erase(data2.begin() + r_idx_i);
|
126
|
+
indexes.erase(indexes.begin() + r_idx_i);
|
127
|
+
|
128
|
+
// Compute critical value
|
129
|
+
float p;
|
130
|
+
if (one_tail) {
|
131
|
+
p = 1.0 - alpha / (n - i + 1);
|
132
|
+
} else {
|
133
|
+
p = 1.0 - alpha / (2.0 * (n - i + 1));
|
134
|
+
}
|
135
|
+
|
136
|
+
auto t = students_t_ppf(p, n - i - 1);
|
137
|
+
auto lam = t * (n - i) / sqrt(((n - i - 1) + t * t) * (n - i + 1));
|
138
|
+
|
139
|
+
if (r > lam) {
|
140
|
+
num_anoms = i;
|
141
|
+
}
|
142
|
+
|
143
|
+
if (callback != nullptr) {
|
144
|
+
callback();
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
anomalies.resize(num_anoms);
|
149
|
+
|
150
|
+
// Sort like R version
|
151
|
+
std::sort(anomalies.begin(), anomalies.end());
|
152
|
+
|
153
|
+
return anomalies;
|
154
|
+
}
|
155
|
+
|
156
|
+
class AnomalyDetectionResult {
|
157
|
+
public:
|
158
|
+
std::vector<size_t> anomalies;
|
159
|
+
};
|
160
|
+
|
161
|
+
class AnomalyDetectionParams {
|
162
|
+
float alpha_ = 0.05;
|
163
|
+
float max_anoms_ = 0.1;
|
164
|
+
Direction direction_ = Direction::Both;
|
165
|
+
bool verbose_ = false;
|
166
|
+
std::function<void()> callback_ = nullptr;
|
167
|
+
|
168
|
+
public:
|
169
|
+
inline AnomalyDetectionParams alpha(float alpha) {
|
170
|
+
this->alpha_ = alpha;
|
171
|
+
return *this;
|
172
|
+
};
|
173
|
+
|
174
|
+
inline AnomalyDetectionParams max_anoms(float max_anoms) {
|
175
|
+
this->max_anoms_ = max_anoms;
|
176
|
+
return *this;
|
177
|
+
};
|
178
|
+
|
179
|
+
inline AnomalyDetectionParams direction(Direction direction) {
|
180
|
+
this->direction_ = direction;
|
181
|
+
return *this;
|
182
|
+
};
|
183
|
+
|
184
|
+
inline AnomalyDetectionParams verbose(bool verbose) {
|
185
|
+
this->verbose_ = verbose;
|
186
|
+
return *this;
|
187
|
+
};
|
188
|
+
|
189
|
+
inline AnomalyDetectionParams callback(std::function<void()> callback) {
|
190
|
+
this->callback_ = callback;
|
191
|
+
return *this;
|
192
|
+
};
|
193
|
+
|
194
|
+
AnomalyDetectionResult fit(const std::vector<float>& series, size_t period);
|
195
|
+
};
|
196
|
+
|
197
|
+
AnomalyDetectionParams params() {
|
198
|
+
return AnomalyDetectionParams();
|
199
|
+
}
|
200
|
+
|
201
|
+
AnomalyDetectionResult AnomalyDetectionParams::fit(const std::vector<float>& series, size_t period) {
|
202
|
+
bool one_tail = this->direction_ != Direction::Both;
|
203
|
+
bool upper_tail = this->direction_ == Direction::Positive;
|
204
|
+
|
205
|
+
auto res = AnomalyDetectionResult();
|
206
|
+
res.anomalies = detect_anoms(series, period, this->max_anoms_, this->alpha_, one_tail, upper_tail, this->verbose_, this->callback_);
|
207
|
+
return res;
|
208
|
+
}
|
11
209
|
|
12
210
|
}
|
@@ -1,72 +1,119 @@
|
|
1
1
|
/*!
|
2
|
-
* dist.h v0.
|
2
|
+
* dist.h v0.3.0
|
3
3
|
* https://github.com/ankane/dist.h
|
4
4
|
* Unlicense OR MIT License
|
5
5
|
*/
|
6
6
|
|
7
7
|
#pragma once
|
8
8
|
|
9
|
-
#define _USE_MATH_DEFINES
|
10
|
-
|
11
|
-
#include <assert.h>
|
12
9
|
#include <math.h>
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
double sign = x < 0 ? -1.0 : 1.0;
|
20
|
-
x = x < 0 ? -x : x;
|
21
|
-
|
22
|
-
double a = 0.14;
|
23
|
-
double x2 = x * x;
|
24
|
-
return sign * sqrt(1.0 - exp(-x2 * (4.0 / M_PI + a * x2) / (1.0 + a * x2)));
|
25
|
-
}
|
11
|
+
#ifdef M_E
|
12
|
+
#define DIST_E M_E
|
13
|
+
#else
|
14
|
+
#define DIST_E 2.71828182845904523536
|
15
|
+
#endif
|
26
16
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
double f2 = ln / 2.0;
|
39
|
-
double f3 = f1 + f2;
|
40
|
-
double f4 = 1.0 / a * ln;
|
41
|
-
return sign * sqrt(-f1 - f2 + sqrt(f3 * f3 - f4));
|
42
|
-
}
|
17
|
+
#ifdef M_PI
|
18
|
+
#define DIST_PI M_PI
|
19
|
+
#else
|
20
|
+
#define DIST_PI 3.14159265358979323846
|
21
|
+
#endif
|
22
|
+
|
23
|
+
#ifdef M_SQRT2
|
24
|
+
#define DIST_SQRT2 M_SQRT2
|
25
|
+
#else
|
26
|
+
#define DIST_SQRT2 1.41421356237309504880
|
27
|
+
#endif
|
43
28
|
|
44
29
|
double normal_pdf(double x, double mean, double std_dev) {
|
45
|
-
|
46
|
-
|
30
|
+
if (std_dev <= 0) {
|
31
|
+
return NAN;
|
32
|
+
}
|
33
|
+
|
34
|
+
double n = (x - mean) / std_dev;
|
35
|
+
return (1.0 / (std_dev * sqrt(2.0 * DIST_PI))) * pow(DIST_E, -0.5 * n * n);
|
47
36
|
}
|
48
37
|
|
49
38
|
double normal_cdf(double x, double mean, double std_dev) {
|
50
|
-
|
39
|
+
if (std_dev <= 0) {
|
40
|
+
return NAN;
|
41
|
+
}
|
42
|
+
|
43
|
+
return 0.5 * (1.0 + erf((x - mean) / (std_dev * DIST_SQRT2)));
|
51
44
|
}
|
52
45
|
|
46
|
+
// Wichura, M. J. (1988).
|
47
|
+
// Algorithm AS 241: The Percentage Points of the Normal Distribution.
|
48
|
+
// Journal of the Royal Statistical Society. Series C (Applied Statistics), 37(3), 477-484.
|
53
49
|
double normal_ppf(double p, double mean, double std_dev) {
|
54
|
-
|
50
|
+
if (p < 0 || p > 1 || std_dev <= 0 || isnan(mean) || isnan(std_dev)) {
|
51
|
+
return NAN;
|
52
|
+
}
|
53
|
+
|
54
|
+
if (p == 0) {
|
55
|
+
return -INFINITY;
|
56
|
+
}
|
55
57
|
|
56
|
-
|
58
|
+
if (p == 1) {
|
59
|
+
return INFINITY;
|
60
|
+
}
|
61
|
+
|
62
|
+
double q = p - 0.5;
|
63
|
+
if (fabs(q) < 0.425) {
|
64
|
+
double r = 0.180625 - q * q;
|
65
|
+
return mean + std_dev * q *
|
66
|
+
(((((((2.5090809287301226727e3 * r + 3.3430575583588128105e4) * r + 6.7265770927008700853e4) * r + 4.5921953931549871457e4) * r + 1.3731693765509461125e4) * r + 1.9715909503065514427e3) * r + 1.3314166789178437745e2) * r + 3.3871328727963666080e0) /
|
67
|
+
(((((((5.2264952788528545610e3 * r + 2.8729085735721942674e4) * r + 3.9307895800092710610e4) * r + 2.1213794301586595867e4) * r + 5.3941960214247511077e3) * r + 6.8718700749205790830e2) * r + 4.2313330701600911252e1) * r + 1);
|
68
|
+
} else {
|
69
|
+
double r = q < 0 ? p : 1 - p;
|
70
|
+
r = sqrt(-log(r));
|
71
|
+
double sign = q < 0 ? -1 : 1;
|
72
|
+
if (r < 5) {
|
73
|
+
r -= 1.6;
|
74
|
+
return mean + std_dev * sign *
|
75
|
+
(((((((7.74545014278341407640e-4 * r + 2.27238449892691845833e-2) * r + 2.41780725177450611770e-1) * r + 1.27045825245236838258e0) * r + 3.64784832476320460504e0) * r + 5.76949722146069140550e0) * r + 4.63033784615654529590e0) * r + 1.42343711074968357734e0) /
|
76
|
+
(((((((1.05075007164441684324e-9 * r + 5.47593808499534494600e-4) * r + 1.51986665636164571966e-2) * r + 1.48103976427480074590e-1) * r + 6.89767334985100004550e-1) * r + 1.67638483018380384940e0) * r + 2.05319162663775882187e0) * r + 1);
|
77
|
+
} else {
|
78
|
+
r -= 5;
|
79
|
+
return mean + std_dev * sign *
|
80
|
+
(((((((2.01033439929228813265e-7 * r + 2.71155556874348757815e-5) * r + 1.24266094738807843860e-3) * r + 2.65321895265761230930e-2) * r + 2.96560571828504891230e-1) * r + 1.78482653991729133580e0) * r + 5.46378491116411436990e0) * r + 6.65790464350110377720e0) /
|
81
|
+
(((((((2.04426310338993978564e-15 * r + 1.42151175831644588870e-7) * r + 1.84631831751005468180e-5) * r + 7.86869131145613259100e-4) * r + 1.48753612908506148525e-2) * r + 1.36929880922735805310e-1) * r + 5.99832206555887937690e-1) * r + 1);
|
82
|
+
}
|
83
|
+
}
|
57
84
|
}
|
58
85
|
|
59
|
-
double students_t_pdf(double x,
|
60
|
-
|
86
|
+
double students_t_pdf(double x, double n) {
|
87
|
+
if (n <= 0) {
|
88
|
+
return NAN;
|
89
|
+
}
|
90
|
+
|
91
|
+
if (n == INFINITY) {
|
92
|
+
return normal_pdf(x, 0, 1);
|
93
|
+
}
|
61
94
|
|
62
|
-
return tgamma((n + 1.0) / 2.0) / (sqrt(n *
|
95
|
+
return tgamma((n + 1.0) / 2.0) / (sqrt(n * DIST_PI) * tgamma(n / 2.0)) * pow(1.0 + x * x / n, -(n + 1.0) / 2.0);
|
63
96
|
}
|
64
97
|
|
65
98
|
// Hill, G. W. (1970).
|
66
99
|
// Algorithm 395: Student's t-distribution.
|
67
100
|
// Communications of the ACM, 13(10), 617-619.
|
68
|
-
double students_t_cdf(double x,
|
69
|
-
|
101
|
+
double students_t_cdf(double x, double n) {
|
102
|
+
if (n < 1) {
|
103
|
+
return NAN;
|
104
|
+
}
|
105
|
+
|
106
|
+
if (isnan(x)) {
|
107
|
+
return NAN;
|
108
|
+
}
|
109
|
+
|
110
|
+
if (!isfinite(x)) {
|
111
|
+
return x < 0 ? 0 : 1;
|
112
|
+
}
|
113
|
+
|
114
|
+
if (n == INFINITY) {
|
115
|
+
return normal_cdf(x, 0, 1);
|
116
|
+
}
|
70
117
|
|
71
118
|
double start = x < 0 ? 0 : 1;
|
72
119
|
double sign = x < 0 ? 1 : -1;
|
@@ -76,7 +123,7 @@ double students_t_cdf(double x, unsigned int n) {
|
|
76
123
|
double y = t / n;
|
77
124
|
double b = 1.0 + y;
|
78
125
|
|
79
|
-
if ((n >= 20 && t < n) || n > 200) {
|
126
|
+
if (n > floor(n) || (n >= 20 && t < n) || n > 200) {
|
80
127
|
// asymptotic series for large or noninteger n
|
81
128
|
if (y > 10e-6) {
|
82
129
|
y = log(b);
|
@@ -88,6 +135,10 @@ double students_t_cdf(double x, unsigned int n) {
|
|
88
135
|
return start + sign * normal_cdf(-y, 0.0, 1.0);
|
89
136
|
}
|
90
137
|
|
138
|
+
// make n int
|
139
|
+
// n is int between 1 and 200 if made it here
|
140
|
+
n = (int) n;
|
141
|
+
|
91
142
|
if (n < 20 && t < 4.0) {
|
92
143
|
// nested summation of cosine series
|
93
144
|
y = sqrt(y);
|
@@ -104,7 +155,7 @@ double students_t_cdf(double x, unsigned int n) {
|
|
104
155
|
n -= 2;
|
105
156
|
}
|
106
157
|
}
|
107
|
-
a = n == 0 ? a / sqrt(b) : (atan(y) + a / b) * (2.0 /
|
158
|
+
a = n == 0 ? a / sqrt(b) : (atan(y) + a / b) * (2.0 / DIST_PI);
|
108
159
|
return start + sign * (z - a) / 2;
|
109
160
|
}
|
110
161
|
|
@@ -127,16 +178,21 @@ double students_t_cdf(double x, unsigned int n) {
|
|
127
178
|
a = (n - 1) / (b * n) * a + y;
|
128
179
|
n -= 2;
|
129
180
|
}
|
130
|
-
a = n == 0 ? a / sqrt(b) : (atan(y) + a / b) * (2.0 /
|
181
|
+
a = n == 0 ? a / sqrt(b) : (atan(y) + a / b) * (2.0 / DIST_PI);
|
131
182
|
return start + sign * (z - a) / 2;
|
132
183
|
}
|
133
184
|
|
134
185
|
// Hill, G. W. (1970).
|
135
186
|
// Algorithm 396: Student's t-quantiles.
|
136
187
|
// Communications of the ACM, 13(10), 619-620.
|
137
|
-
double students_t_ppf(double p,
|
138
|
-
|
139
|
-
|
188
|
+
double students_t_ppf(double p, double n) {
|
189
|
+
if (p < 0 || p > 1 || n < 1) {
|
190
|
+
return NAN;
|
191
|
+
}
|
192
|
+
|
193
|
+
if (n == INFINITY) {
|
194
|
+
return normal_ppf(p, 0, 1);
|
195
|
+
}
|
140
196
|
|
141
197
|
// distribution is symmetric
|
142
198
|
double sign = p < 0.5 ? -1 : 1;
|
@@ -149,7 +205,7 @@ double students_t_ppf(double p, unsigned int n) {
|
|
149
205
|
return sign * sqrt(2.0 / (p * (2.0 - p)) - 2.0);
|
150
206
|
}
|
151
207
|
|
152
|
-
double half_pi =
|
208
|
+
double half_pi = DIST_PI / 2.0;
|
153
209
|
|
154
210
|
if (n == 1) {
|
155
211
|
p = p * half_pi;
|
@@ -12,7 +12,7 @@ void Init_ext() {
|
|
12
12
|
rb_mAnomalyDetection
|
13
13
|
.define_singleton_function(
|
14
14
|
"_detect",
|
15
|
-
[](std::vector<float>
|
15
|
+
[](std::vector<float> series, int period, float k, float alpha, const std::string& direction, bool verbose) {
|
16
16
|
Direction dir;
|
17
17
|
if (direction == "pos") {
|
18
18
|
dir = Direction::Positive;
|
@@ -24,10 +24,16 @@ void Init_ext() {
|
|
24
24
|
throw std::invalid_argument("direction must be pos, neg, or both");
|
25
25
|
}
|
26
26
|
|
27
|
-
auto res = anomaly_detection::
|
27
|
+
auto res = anomaly_detection::params()
|
28
|
+
.max_anoms(k)
|
29
|
+
.alpha(alpha)
|
30
|
+
.direction(dir)
|
31
|
+
.verbose(verbose)
|
32
|
+
.callback(rb_thread_check_ints)
|
33
|
+
.fit(series, period);
|
28
34
|
|
29
35
|
auto a = Rice::Array();
|
30
|
-
for (auto v : res) {
|
36
|
+
for (auto v : res.anomalies) {
|
31
37
|
a.push(v);
|
32
38
|
}
|
33
39
|
return a;
|