breakout-detection 0.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f81b5a2a9781787903f293322eef1ff6d08479cde2df25eea47c3023cd501bd
4
- data.tar.gz: 1839625455de87fca184eb2f1fdf8bc7a2e8809c8afd17b9fc9ec61d6d775b7a
3
+ metadata.gz: 7b665aa0eaa17e43b38e8a4375a1f09c5885d19f6f461cae910ff4b8a7468bcc
4
+ data.tar.gz: 68c844bcfd8085d10e2ff2e081364a57c17c54c107f22cff2f9b78049cdb19a1
5
5
  SHA512:
6
- metadata.gz: 1fa14ae2cc9db547bdeb13a3232d87c96e239aa6dde618ea6b7fa9c53aaca5596b45223c3e7eeceb9a8aa0428de77c54c4e8275adf57b9767c13a889538981bb
7
- data.tar.gz: 4ba5ee1decd2ab9084c3d377ec6d37a743c7f5ff47bb48eb55bdd5ecb9bce57df534522643c448b00a35cdd39d1a429ebc1c036440b079046c321bbef5e193a1
6
+ metadata.gz: 4b729d9d68272f9b9453af53a183fff49717bc5669ed3fbf2e0e57f41b20513fe2412a4fa8822591b764cb7b801c8f152e0d97aec231bb5730ec644f30fa7e8a
7
+ data.tar.gz: a847b56c452c8ac3874f3bdc0239d4f5755a33e6bf2c06a3162ac27eea8c435175bd72608b22fe617ea40a4242224869c005382cb2e51b906a86b1601d4240e0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.2.0 (2023-05-11)
2
+
3
+ - Dropped support for Ruby < 3
4
+
5
+ ## 0.1.1 (2021-10-20)
6
+
7
+ - Added `plot` method
8
+
1
9
  ## 0.1.0 (2021-09-02)
2
10
 
3
11
  - First release
data/README.md CHANGED
@@ -1,17 +1,17 @@
1
- # Breakout
1
+ # Breakout Ruby
2
2
 
3
3
  :fire: [BreakoutDetection](https://github.com/twitter/BreakoutDetection) for Ruby
4
4
 
5
- Learn more about [how it works](https://blog.twitter.com/engineering/en_us/a/2014/breakout-detection-in-the-wild)
5
+ Learn [how it works](https://blog.twitter.com/engineering/en_us/a/2014/breakout-detection-in-the-wild)
6
6
 
7
- [![Build Status](https://github.com/ankane/breakout/workflows/build/badge.svg?branch=master)](https://github.com/ankane/breakout/actions)
7
+ [![Build Status](https://github.com/ankane/breakout-ruby/workflows/build/badge.svg?branch=master)](https://github.com/ankane/breakout-ruby/actions)
8
8
 
9
9
  ## Installation
10
10
 
11
11
  Add this line to your application’s Gemfile:
12
12
 
13
13
  ```ruby
14
- gem 'breakout-detection'
14
+ gem "breakout-detection"
15
15
  ```
16
16
 
17
17
  ## Getting Started
@@ -60,6 +60,20 @@ Breakout.detect(
60
60
  )
61
61
  ```
62
62
 
63
+ ## Plotting
64
+
65
+ Add [Vega](https://github.com/ankane/vega) to your application’s Gemfile:
66
+
67
+ ```ruby
68
+ gem "vega"
69
+ ```
70
+
71
+ And use:
72
+
73
+ ```ruby
74
+ Breakout.plot(series, breakouts)
75
+ ```
76
+
63
77
  ## Credits
64
78
 
65
79
  This library uses the C++ code from the [BreakoutDetection](https://github.com/twitter/BreakoutDetection) R package and is available under the same license.
@@ -70,22 +84,22 @@ This library uses the C++ code from the [BreakoutDetection](https://github.com/t
70
84
 
71
85
  ## History
72
86
 
73
- View the [changelog](https://github.com/ankane/breakout/blob/master/CHANGELOG.md)
87
+ View the [changelog](https://github.com/ankane/breakout-ruby/blob/master/CHANGELOG.md)
74
88
 
75
89
  ## Contributing
76
90
 
77
91
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
78
92
 
79
- - [Report bugs](https://github.com/ankane/breakout/issues)
80
- - Fix bugs and [submit pull requests](https://github.com/ankane/breakout/pulls)
93
+ - [Report bugs](https://github.com/ankane/breakout-ruby/issues)
94
+ - Fix bugs and [submit pull requests](https://github.com/ankane/breakout-ruby/pulls)
81
95
  - Write, clarify, or fix documentation
82
96
  - Suggest or add new features
83
97
 
84
98
  To get started with development:
85
99
 
86
100
  ```sh
87
- git clone https://github.com/ankane/breakout.git
88
- cd breakout
101
+ git clone https://github.com/ankane/breakout-ruby.git
102
+ cd breakout-ruby
89
103
  bundle install
90
104
  bundle exec rake compile
91
105
  bundle exec rake test
@@ -50,7 +50,7 @@ std::vector<int> EDM_multi(const std::vector<double>& Z, int min_size = 24, doub
50
50
  for (int i = min_size - 1; i < s; ++i)
51
51
  insert_element(right_min, right_max, Z[i]);
52
52
 
53
- // Iterate over possible locations for the penultiamte change
53
+ // Iterate over possible locations for the penultimate change
54
54
  for (int t = min_size; t < s - min_size + 1; ++t) { // modify limits to deal with min_size
55
55
  insert_element(left_min, left_max, Z[t - 1]); // insert element into left tree
56
56
  remove_element(right_min, right_max, Z[t - 1]); // remove element from right tree
@@ -1,7 +1,7 @@
1
1
  /*
2
- Penalizes based on percent chagne in the statistic value.
3
- Linear penalty means that each new breakout must result in an at least X% increast
4
- Quadratic penalty means that each new brekaout must result in at least an (X*k)% increase for k breakouts
2
+ Penalizes based on percent change in the statistic value.
3
+ Linear penalty means that each new breakout must result in an at least X% increase
4
+ Quadratic penalty means that each new breakout must result in at least an (X*k)% increase for k breakouts
5
5
  */
6
6
 
7
7
  #include <algorithm>
@@ -355,7 +355,6 @@ void BackwardUpdate(std::vector<double>& Z, Information& info, int& tau1, double
355
355
  index /= 2;
356
356
  }
357
357
  }
358
- double qb = std::pow(GetQuantile(info.B, quant), alpha);
359
358
  // Move tau2 from the end of the time series to the front.
360
359
  // Update the statistic value along the way
361
360
  tau2 = N;
@@ -366,7 +365,7 @@ void BackwardUpdate(std::vector<double>& Z, Information& info, int& tau1, double
366
365
  --info.B[index];
367
366
  index /= 2;
368
367
  }
369
- qb = std::pow(GetQuantile(info.B, quant), alpha);
368
+ double qb = std::pow(GetQuantile(info.B, quant), alpha);
370
369
 
371
370
  double stat = 2 * qc - qa - qb;
372
371
  stat *= (double)(tau2 - tau1) * tau1 / tau2;
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  Robust estimation of 2[mean(X)-mean(Y)]^2 time normalization factor
3
3
  This is the E-Divisive E-statistic when alpha = 2
4
- Instead of calculating mean(X) we calculate median(X), and similarly for Y
4
+ Instead of calculating mean(X), we calculate median(X), and similarly for Y
5
5
  */
6
6
 
7
7
  #include <algorithm>
@@ -1,3 +1,3 @@
1
1
  module Breakout
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/breakout.rb CHANGED
@@ -5,24 +5,78 @@ require "breakout/ext"
5
5
  require "breakout/version"
6
6
 
7
7
  module Breakout
8
- def self.detect(series, min_size: 30, method: "multi", alpha: 2, beta: nil, degree: 1, percent: nil, exact: true)
9
- raise ArgumentError, "min_size must be at least 2" if min_size < 2
10
- raise ArgumentError, "beta and percent cannot be passed together" unless beta.nil? || percent.nil?
11
- raise ArgumentError, "alpha must be between 0 and 2" if alpha < 0 || alpha > 2
12
- raise ArgumentError, "degree must be 0, 1, or 2" unless [0, 1, 2].include?(degree)
13
- raise ArgumentError, "Bad method" unless ["amoc", "multi"].include?(method)
14
-
15
- return [] if series.size < min_size
16
-
17
- if series.is_a?(Hash)
18
- sorted = series.sort_by { |k, _| k }
19
- z = sorted.map(&:last)
20
- else
21
- z = series
8
+ class << self
9
+ def detect(series, min_size: 30, method: "multi", alpha: 2, beta: nil, degree: 1, percent: nil, exact: true)
10
+ raise ArgumentError, "min_size must be at least 2" if min_size < 2
11
+ raise ArgumentError, "beta and percent cannot be passed together" unless beta.nil? || percent.nil?
12
+ raise ArgumentError, "alpha must be between 0 and 2" if alpha < 0 || alpha > 2
13
+ raise ArgumentError, "degree must be 0, 1, or 2" unless [0, 1, 2].include?(degree)
14
+ raise ArgumentError, "method must be amoc or multi" unless ["amoc", "multi"].include?(method)
15
+
16
+ return [] if series.size < min_size
17
+
18
+ if series.is_a?(Hash)
19
+ sorted = series.sort_by { |k, _| k }
20
+ z = sorted.map(&:last)
21
+ else
22
+ z = series
23
+ end
24
+
25
+ res = _detect(z, min_size, method, alpha, beta, degree, percent, exact)
26
+ res.map! { |i| sorted[i][0] } if series.is_a?(Hash)
27
+ res
28
+ end
29
+
30
+ def plot(series, breakouts)
31
+ require "vega"
32
+
33
+ data =
34
+ if series.is_a?(Hash)
35
+ series.map { |k, v| {x: iso8601(k), y: v, breakout: breakouts.include?(k)} }
36
+ else
37
+ series.map.with_index { |v, i| {x: i, y: v, breakout: breakouts.include?(i)} }
38
+ end
39
+
40
+ if series.is_a?(Hash)
41
+ x = {field: "x", type: "temporal"}
42
+ x["scale"] = {type: "utc"} if series.keys.first.is_a?(Date)
43
+ else
44
+ x = {field: "x", type: "quantitative"}
45
+ end
46
+
47
+ Vega.lite
48
+ .data(data)
49
+ .layer([
50
+ {
51
+ mark: {type: "line"},
52
+ encoding: {
53
+ x: x,
54
+ y: {field: "y", type: "quantitative", scale: {zero: false}},
55
+ color: {value: "#fa9088"}
56
+ }
57
+ },
58
+ {
59
+ transform: [{"filter": "datum.breakout == true"}],
60
+ mark: {type: "rule"},
61
+ encoding: {
62
+ x: x,
63
+ color: {value: "#19c7ca"},
64
+ strokeWidth: {value: 2},
65
+ strokeDash: {value: [6, 6]}
66
+ }
67
+ }
68
+ ])
69
+ .config(axis: {title: nil, labelFontSize: 12})
22
70
  end
23
71
 
24
- res = _detect(z, min_size, method, alpha, beta, degree, percent, exact)
25
- res.map! { |i| sorted[i][0] } if series.is_a?(Hash)
26
- res
72
+ private
73
+
74
+ def iso8601(v)
75
+ if v.is_a?(Date)
76
+ v.strftime("%Y-%m-%d")
77
+ else
78
+ v.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
79
+ end
80
+ end
27
81
  end
28
82
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: breakout-detection
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
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-09-03 00:00:00.000000000 Z
11
+ date: 2023-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rice
@@ -45,7 +45,7 @@ files:
45
45
  - lib/breakout-detection.rb
46
46
  - lib/breakout.rb
47
47
  - lib/breakout/version.rb
48
- homepage: https://github.com/ankane/breakout
48
+ homepage: https://github.com/ankane/breakout-ruby
49
49
  licenses:
50
50
  - GPL-2.0-or-later
51
51
  metadata: {}
@@ -57,14 +57,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: '2.6'
60
+ version: '3'
61
61
  required_rubygems_version: !ruby/object:Gem::Requirement
62
62
  requirements:
63
63
  - - ">="
64
64
  - !ruby/object:Gem::Version
65
65
  version: '0'
66
66
  requirements: []
67
- rubygems_version: 3.2.22
67
+ rubygems_version: 3.4.10
68
68
  signing_key:
69
69
  specification_version: 4
70
70
  summary: Breakout detection for Ruby