anomaly 0.3.0 → 0.5.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 +25 -1
- data/LICENSE.txt +1 -1
- data/README.md +6 -6
- data/lib/anomaly/detector.rb +41 -15
- data/lib/anomaly/version.rb +1 -1
- data/lib/anomaly.rb +7 -2
- metadata +4 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1159161d09e4681534d64580d0de094e0b6e9b9d393a60054e3201d69e668b37
|
|
4
|
+
data.tar.gz: 2332587591c8fec6cee8446e9c505e5c444d730643fbd8f4dab68e2df8218db1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3250db6d40d85ee4f63e3563367e9da2b14a27e65bb2d50c5cad75784476b368ec135a6958efd21fa0c7d51c02136e317b5f634cd515c653cd16e9ff2033b70
|
|
7
|
+
data.tar.gz: 5c23dff5e507185e6ca904f88bcaf4e8091a650bcf404f4ced7350f2ad1645e4132249567485a76aac49b1a0b38a34c8d180fe363481ca5d1709d7316511a3ff
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 0.5.0 (2026-04-01)
|
|
2
|
+
|
|
3
|
+
- Added support for JSON serialization
|
|
4
|
+
- Improved error classes
|
|
5
|
+
- Removed `eps` option from `train` method (use initializer instead)
|
|
6
|
+
- Dropped support for `nmatrix`
|
|
7
|
+
- Dropped support for Ruby < 3.3
|
|
8
|
+
|
|
9
|
+
## 0.4.0 (2024-10-23)
|
|
10
|
+
|
|
11
|
+
- Dropped support for Ruby < 3.1
|
|
12
|
+
|
|
1
13
|
## 0.3.0 (2022-09-05)
|
|
2
14
|
|
|
3
15
|
- Dropped support for `narray` (use `numo-narray` instead)
|
|
@@ -15,4 +27,16 @@
|
|
|
15
27
|
|
|
16
28
|
## 0.1.0 (2011-12-18)
|
|
17
29
|
|
|
18
|
-
-
|
|
30
|
+
- Automatically find the best value for epsilon
|
|
31
|
+
|
|
32
|
+
## 0.0.3 (2011-12-11)
|
|
33
|
+
|
|
34
|
+
- Keep probabilities between 0 and 1
|
|
35
|
+
|
|
36
|
+
## 0.0.2 (2011-12-11)
|
|
37
|
+
|
|
38
|
+
- Handle very small standard deviations more gracefully
|
|
39
|
+
|
|
40
|
+
## 0.0.1 (2011-12-11)
|
|
41
|
+
|
|
42
|
+
- First release
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Easy-to-use anomaly detection for Ruby
|
|
4
4
|
|
|
5
|
-
[](https://github.com/ankane/anomaly/actions)
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -59,15 +59,14 @@ detector = Anomaly::Detector.new(weather_data, eps: 0.01)
|
|
|
59
59
|
You can easily persist the detector to a file or database - it’s very tiny.
|
|
60
60
|
|
|
61
61
|
```ruby
|
|
62
|
-
|
|
63
|
-
File.binwrite("detector.bin", bin)
|
|
62
|
+
File.write("detector.json", detector.to_json)
|
|
64
63
|
```
|
|
65
64
|
|
|
66
65
|
Then read it later:
|
|
67
66
|
|
|
68
67
|
```ruby
|
|
69
|
-
|
|
70
|
-
detector =
|
|
68
|
+
json = File.read("detector.json")
|
|
69
|
+
detector = Anomaly::Detector.load_json(json)
|
|
71
70
|
```
|
|
72
71
|
|
|
73
72
|
## Related Projects
|
|
@@ -77,6 +76,7 @@ detector = Marshal.load(bin)
|
|
|
77
76
|
- [IsoTree](https://github.com/ankane/isotree-ruby) - Outlier/anomaly detection for Ruby using Isolation Forest
|
|
78
77
|
- [OutlierTree](https://github.com/ankane/outliertree-ruby) - Explainable outlier/anomaly detection for Ruby
|
|
79
78
|
- [MIDAS](https://github.com/ankane/midas-ruby) - Edge stream anomaly detection for Ruby
|
|
79
|
+
- [Random Cut Forest](https://github.com/ankane/random-cut-forest-ruby) - Random Cut Forest anomaly detection for Ruby
|
|
80
80
|
- [Trend](https://github.com/ankane/trend-ruby) - Anomaly detection and forecasting for Ruby
|
|
81
81
|
|
|
82
82
|
## Credits
|
|
@@ -102,5 +102,5 @@ To get started with development:
|
|
|
102
102
|
git clone https://github.com/ankane/anomaly.git
|
|
103
103
|
cd anomaly
|
|
104
104
|
bundle install
|
|
105
|
-
bundle exec rake
|
|
105
|
+
bundle exec rake test
|
|
106
106
|
```
|
data/lib/anomaly/detector.rb
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
module Anomaly
|
|
2
2
|
class Detector
|
|
3
|
-
attr_reader :mean, :std
|
|
4
|
-
attr_accessor :eps
|
|
3
|
+
attr_reader :eps, :mean, :std
|
|
5
4
|
|
|
6
|
-
def initialize(examples = nil,
|
|
5
|
+
def initialize(examples = nil, eps: nil)
|
|
7
6
|
@m = 0
|
|
8
|
-
|
|
7
|
+
@eps = eps || 0
|
|
8
|
+
train(examples) if examples
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def train(examples
|
|
11
|
+
def train(examples)
|
|
12
12
|
# for Numo::NArray
|
|
13
13
|
# TODO make more efficient when possible
|
|
14
14
|
examples = examples.to_a
|
|
15
15
|
|
|
16
|
-
raise "No examples" if examples.empty?
|
|
17
|
-
raise "Must have at least two columns" if examples.first.size < 2
|
|
16
|
+
raise ArgumentError, "No examples" if examples.empty?
|
|
17
|
+
raise ArgumentError, "Must have at least two columns" if examples.first.size < 2
|
|
18
18
|
|
|
19
19
|
# Divide into groups since we only want to train with non-anomalies.
|
|
20
20
|
anomalies = []
|
|
@@ -27,9 +27,8 @@ module Anomaly
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
raise "Must have at least one non-anomaly" if non_anomalies.empty?
|
|
30
|
+
raise ArgumentError, "Must have at least one non-anomaly" if non_anomalies.empty?
|
|
31
31
|
|
|
32
|
-
@eps = eps
|
|
33
32
|
if @eps > 0
|
|
34
33
|
# Use all non-anomalies to train.
|
|
35
34
|
training_examples = non_anomalies
|
|
@@ -47,11 +46,6 @@ module Anomaly
|
|
|
47
46
|
# Convert these to an Array for Marshal.dump
|
|
48
47
|
@mean = training_examples.mean(0).to_a
|
|
49
48
|
@std = training_examples.stddev(0).to_a
|
|
50
|
-
elsif defined?(NMatrix)
|
|
51
|
-
training_examples = NMatrix.to_na(training_examples)
|
|
52
|
-
# Convert these to an Array for Marshal.dump
|
|
53
|
-
@mean = training_examples.mean(1).to_a
|
|
54
|
-
@std = training_examples.stddev(1).to_a
|
|
55
49
|
else
|
|
56
50
|
# Default to Array, since built-in Matrix does not give us a big performance advantage.
|
|
57
51
|
cols = @n.times.map { |i| training_examples.map { |r| r[i] } }
|
|
@@ -76,7 +70,7 @@ module Anomaly
|
|
|
76
70
|
# to keep probabilities at same scale.
|
|
77
71
|
# Use log to prevent underflow
|
|
78
72
|
def probability(x)
|
|
79
|
-
raise "Train me first" unless trained?
|
|
73
|
+
raise Error, "Train me first" unless trained?
|
|
80
74
|
|
|
81
75
|
singular = !x.first.is_a?(Array)
|
|
82
76
|
x = [x] if singular
|
|
@@ -106,8 +100,40 @@ module Anomaly
|
|
|
106
100
|
end
|
|
107
101
|
end
|
|
108
102
|
|
|
103
|
+
def to_json
|
|
104
|
+
require "json"
|
|
105
|
+
|
|
106
|
+
obj = {
|
|
107
|
+
m: @m,
|
|
108
|
+
n: @n,
|
|
109
|
+
eps: @eps,
|
|
110
|
+
mean: @mean,
|
|
111
|
+
std: @std
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
JSON.generate(obj)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.load_json(json)
|
|
118
|
+
require "json"
|
|
119
|
+
|
|
120
|
+
obj = JSON.parse(json)
|
|
121
|
+
|
|
122
|
+
detector = new
|
|
123
|
+
detector.send(:json_load, obj)
|
|
124
|
+
detector
|
|
125
|
+
end
|
|
126
|
+
|
|
109
127
|
protected
|
|
110
128
|
|
|
129
|
+
def json_load(obj)
|
|
130
|
+
@m = obj["m"].to_i
|
|
131
|
+
@n = obj["n"].to_i
|
|
132
|
+
@eps = obj["eps"].to_f
|
|
133
|
+
@mean = obj["mean"].map(&:to_f)
|
|
134
|
+
@std = obj["std"].map(&:to_f)
|
|
135
|
+
end
|
|
136
|
+
|
|
111
137
|
SQRT2PI = Math.sqrt(2 * Math::PI)
|
|
112
138
|
|
|
113
139
|
def normal_pdf(x, mean = 0, std = 1)
|
data/lib/anomaly/version.rb
CHANGED
data/lib/anomaly.rb
CHANGED
metadata
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: anomaly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Kane
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
|
-
description:
|
|
14
12
|
email: andrew@ankane.org
|
|
15
13
|
executables: []
|
|
16
14
|
extensions: []
|
|
@@ -26,7 +24,6 @@ homepage: https://github.com/ankane/anomaly
|
|
|
26
24
|
licenses:
|
|
27
25
|
- MIT
|
|
28
26
|
metadata: {}
|
|
29
|
-
post_install_message:
|
|
30
27
|
rdoc_options: []
|
|
31
28
|
require_paths:
|
|
32
29
|
- lib
|
|
@@ -34,15 +31,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
34
31
|
requirements:
|
|
35
32
|
- - ">="
|
|
36
33
|
- !ruby/object:Gem::Version
|
|
37
|
-
version: '
|
|
34
|
+
version: '3.3'
|
|
38
35
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
36
|
requirements:
|
|
40
37
|
- - ">="
|
|
41
38
|
- !ruby/object:Gem::Version
|
|
42
39
|
version: '0'
|
|
43
40
|
requirements: []
|
|
44
|
-
rubygems_version:
|
|
45
|
-
signing_key:
|
|
41
|
+
rubygems_version: 4.0.6
|
|
46
42
|
specification_version: 4
|
|
47
43
|
summary: Easy-to-use anomaly detection for Ruby
|
|
48
44
|
test_files: []
|