abba 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 88201a4cd5a8b7ea2eb7a28e31b5a5cd7ac86fc9
4
+ data.tar.gz: c1f24a23ff75f646ee43f7da4368e807c40f7a73
5
+ SHA512:
6
+ metadata.gz: 76adccef44a5bb5697166d449d2a8bdd285c3bbada18dce1b884d61b84841ab30a3e22e9f11d0ec47338709ac804972277468f54f8e048ca64fd8455c1f0390d
7
+ data.tar.gz: b8922e2fa9e9678062e8d709b7171a482bd9e225381fec156a720fc9ab19ed64b01b17c971b8b9a8fa44dbf182ac2d96bbde28c27515489d0a55c8c911e054da
@@ -0,0 +1,6 @@
1
+ module ABBA
2
+ end
3
+
4
+ require 'abba/bayes'
5
+ require 'abba/math'
6
+ require 'abba/ab_test'
@@ -0,0 +1,84 @@
1
+ module ABBA
2
+ class ABTest
3
+ def initialize(variant_a:, variant_b:, test: Bayes::BinaryOutcomeTest)
4
+ @variant_a = ABTest::Variant.new(variant_a)
5
+ @variant_b = ABTest::Variant.new(variant_b)
6
+ @test = test
7
+ end
8
+
9
+ def choose_a?
10
+ @test.new(@variant_a, @variant_b).call
11
+ end
12
+
13
+ def choose_b?
14
+ @test.new(@variant_b, @variant_a).call
15
+ end
16
+
17
+ def probabilities
18
+ p = choose_a?
19
+ [p, 1-p]
20
+ end
21
+
22
+ class Variant
23
+ def initialize(args={})
24
+ @successes = args[:successes]
25
+ @failures = args[:failures]
26
+ @size = args[:size]
27
+ raise ArgumentError, "Invalid ABTest variant, you must pass 2 or more of :successes, :failures, :size and successes + failures = size" unless validate
28
+ end
29
+
30
+ def successes
31
+ @successes ||= calculate_successes
32
+ end
33
+
34
+ def failures
35
+ @failures ||= calculate_failures
36
+ end
37
+
38
+ def size
39
+ @size ||= calculate_size
40
+ end
41
+
42
+ def success_rate
43
+ successes/size.to_f
44
+ end
45
+
46
+ def failure_rate
47
+ failures/size.to_f
48
+ end
49
+
50
+ def ==(other)
51
+ size == other.size && successes == other.successes && failures == other.failures
52
+ end
53
+
54
+ private
55
+ def has_successes?
56
+ !!successes
57
+ end
58
+
59
+ def has_failures?
60
+ !!failures
61
+ end
62
+
63
+ def has_size?
64
+ !!size
65
+ end
66
+
67
+ def calculate_successes
68
+ @size - @failures if @size && @failures
69
+ end
70
+
71
+ def calculate_failures
72
+ @size - @successes if @size && @successes
73
+ end
74
+
75
+ def calculate_size
76
+ @successes + @failures if @successes && @failures
77
+ end
78
+
79
+ def validate
80
+ has_successes? && has_failures? && has_size? && (successes + failures == size)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,38 @@
1
+ module ABBA
2
+ module Bayes
3
+ class BinaryOutcomeTest
4
+ def initialize(variant_a, variant_b)
5
+ @variant_a = variant_a
6
+ @variant_b = variant_b
7
+ end
8
+
9
+ def call
10
+ total = 0.0
11
+
12
+ (0..(alpha_a-1)).each do |i|
13
+ log_contribution = Math.lbeta(alpha_b+i, beta_a+beta_b) - Math.log(beta_a+i) - Math.lbeta(1+i, beta_a) - Math.lbeta(alpha_b, beta_b)
14
+ total += Math.exp(log_contribution)
15
+ end
16
+
17
+ total
18
+ end
19
+
20
+ private
21
+ def alpha_a
22
+ @variant_a.successes + 1
23
+ end
24
+
25
+ def beta_a
26
+ @variant_a.failures + 1
27
+ end
28
+
29
+ def alpha_b
30
+ @variant_b.successes + 1
31
+ end
32
+
33
+ def beta_b
34
+ @variant_b.failures + 1
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ require 'singleton'
2
+
3
+ module ABBA
4
+ module Math
5
+
6
+ class << self
7
+ def exp(x)
8
+ ::Math.exp(x)
9
+ end
10
+
11
+ def log(x)
12
+ ::Math.log(x)
13
+ end
14
+
15
+ def gamma(x)
16
+ ::Math.gamma(x)
17
+ end
18
+
19
+ def lgamma(x)
20
+ ::Math.lgamma(x)
21
+ end
22
+
23
+ def beta(x,y)
24
+ (gamma(x)*gamma(y))/(gamma(x+y))
25
+ end
26
+
27
+ def lbeta(x,y)
28
+ lgamma_x, sign_gamma_x = lgamma(x)
29
+ lgamma_y, sign_gamma_y = lgamma(y)
30
+ lgamma_xy, sign_gamma_xy = lgamma(x+y)
31
+
32
+ if (sign_gamma_x * sign_gamma_y * sign_gamma_xy) == 1
33
+ lgamma_x + lgamma_y - lgamma_xy
34
+ else
35
+ raise ::Math::DomainError, 'Numerical argument is out of domain - "lbeta"'
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module ABBA
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: abba
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kyle Tate
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email: kbt.tate@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/abba.rb
48
+ - lib/abba/ab_test.rb
49
+ - lib/abba/bayes.rb
50
+ - lib/abba/math.rb
51
+ - lib/abba/version.rb
52
+ homepage: http://github.com/infiton/abba
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 2.4.5.1
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Bayesian A/B Testing
76
+ test_files: []