anomaly 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc0bbbf742e985e545dd4864ebccae5e864dd3a98af7c8c62a06a732a4477a0f
4
- data.tar.gz: 7d2ec359331e275f3b87cda1db48d309770e9445f2e7d0843b13a6c4502c8fd0
3
+ metadata.gz: 1159161d09e4681534d64580d0de094e0b6e9b9d393a60054e3201d69e668b37
4
+ data.tar.gz: 2332587591c8fec6cee8446e9c505e5c444d730643fbd8f4dab68e2df8218db1
5
5
  SHA512:
6
- metadata.gz: eabd1811a81ce6ba97523bb7dfcbbcafeef5c819cee17119c59a5c52bac54fcad3ac1e9397bc68be74e100053c405f1219f768bfa5df92049145b879c34439d3
7
- data.tar.gz: 7160ff0d78883138e1c6230624840726ab0a555f073108728bc490a358503fd1471f1f8337afba137c3c3cb6e0c8d934561ecb60f339129d7249c9f0f1fbc9ae
6
+ metadata.gz: d3250db6d40d85ee4f63e3563367e9da2b14a27e65bb2d50c5cad75784476b368ec135a6958efd21fa0c7d51c02136e317b5f634cd515c653cd16e9ff2033b70
7
+ data.tar.gz: 5c23dff5e507185e6ca904f88bcaf4e8091a650bcf404f4ced7350f2ad1645e4132249567485a76aac49b1a0b38a34c8d180fe363481ca5d1709d7316511a3ff
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
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
+
1
9
  ## 0.4.0 (2024-10-23)
2
10
 
3
11
  - Dropped support for Ruby < 3.1
@@ -19,4 +27,16 @@
19
27
 
20
28
  ## 0.1.0 (2011-12-18)
21
29
 
22
- - Started changelog
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011-2024 Andrew Kane
1
+ Copyright (c) 2011-2026 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -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
- bin = Marshal.dump(detector)
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
- bin = File.binread("detector.bin")
70
- detector = Marshal.load(bin)
68
+ json = File.read("detector.json")
69
+ detector = Anomaly::Detector.load_json(json)
71
70
  ```
72
71
 
73
72
  ## Related Projects
@@ -103,5 +102,5 @@ To get started with development:
103
102
  git clone https://github.com/ankane/anomaly.git
104
103
  cd anomaly
105
104
  bundle install
106
- bundle exec rake spec
105
+ bundle exec rake test
107
106
  ```
@@ -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, **opts)
5
+ def initialize(examples = nil, eps: nil)
7
6
  @m = 0
8
- train(examples, **opts) if examples
7
+ @eps = eps || 0
8
+ train(examples) if examples
9
9
  end
10
10
 
11
- def train(examples, eps: 0)
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)
@@ -1,3 +1,3 @@
1
1
  module Anomaly
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/anomaly.rb CHANGED
@@ -1,2 +1,7 @@
1
- require_relative "anomaly/version"
1
+ # modules
2
2
  require_relative "anomaly/detector"
3
+ require_relative "anomaly/version"
4
+
5
+ module Anomaly
6
+ class Error < StandardError; end
7
+ end
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.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: 2024-10-23 00:00:00.000000000 Z
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: '3.1'
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: 3.5.16
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: []