breakout-detection 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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