bandit 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +674 -0
  3. data/README.rdoc +70 -2
  4. data/Rakefile +32 -0
  5. data/bandit.gemspec +2 -0
  6. data/lib/bandit.rb +52 -1
  7. data/lib/bandit/config.rb +33 -0
  8. data/lib/bandit/date_hour.rb +82 -0
  9. data/lib/bandit/exceptions.rb +10 -0
  10. data/lib/bandit/experiment.rb +63 -0
  11. data/lib/bandit/extensions/controller_concerns.rb +24 -0
  12. data/lib/bandit/extensions/string.rb +5 -0
  13. data/lib/bandit/extensions/time.rb +5 -0
  14. data/lib/bandit/extensions/view_concerns.rb +24 -0
  15. data/lib/bandit/memoizable.rb +15 -0
  16. data/lib/bandit/players/base.rb +37 -0
  17. data/lib/bandit/players/epsilon_greedy.rb +32 -0
  18. data/lib/bandit/players/round_robin.rb +7 -0
  19. data/lib/bandit/storage/base.rb +124 -0
  20. data/lib/bandit/storage/memcache.rb +34 -0
  21. data/lib/bandit/storage/memory.rb +31 -0
  22. data/lib/bandit/storage/redis.rb +37 -0
  23. data/lib/bandit/version.rb +1 -1
  24. data/lib/generators/bandit/USAGE +3 -0
  25. data/lib/generators/bandit/dashboard_generator.rb +31 -0
  26. data/lib/generators/bandit/install_generator.rb +22 -0
  27. data/lib/generators/bandit/templates/bandit.rake +20 -0
  28. data/lib/generators/bandit/templates/bandit.rb +18 -0
  29. data/lib/generators/bandit/templates/bandit.yml +16 -0
  30. data/lib/generators/bandit/templates/bandit_controller.rb +28 -0
  31. data/lib/generators/bandit/templates/dashboard/bandit.html.erb +43 -0
  32. data/lib/generators/bandit/templates/dashboard/css/application.css +7 -0
  33. data/lib/generators/bandit/templates/dashboard/css/base.css +22 -0
  34. data/lib/generators/bandit/templates/dashboard/css/toupee/buttons.css +101 -0
  35. data/lib/generators/bandit/templates/dashboard/css/toupee/forms.css +89 -0
  36. data/lib/generators/bandit/templates/dashboard/css/toupee/modules.css +30 -0
  37. data/lib/generators/bandit/templates/dashboard/css/toupee/reset.css +42 -0
  38. data/lib/generators/bandit/templates/dashboard/css/toupee/structure.css +124 -0
  39. data/lib/generators/bandit/templates/dashboard/css/toupee/typography.css +103 -0
  40. data/lib/generators/bandit/templates/dashboard/helpers/bandit_helper.rb +19 -0
  41. data/lib/generators/bandit/templates/dashboard/js/bandit.js +34 -0
  42. data/lib/generators/bandit/templates/dashboard/js/highstock.js +215 -0
  43. data/lib/generators/bandit/templates/dashboard/js/jquery.min.js +154 -0
  44. data/lib/generators/bandit/templates/dashboard/view/index.html.erb +21 -0
  45. data/lib/generators/bandit/templates/dashboard/view/show.html.erb +24 -0
  46. data/players.rdoc +22 -0
  47. data/test/config.yml +7 -0
  48. data/test/helper.rb +18 -0
  49. data/test/memcache_storage_test.rb +17 -0
  50. data/test/memory_storage_test.rb +16 -0
  51. data/test/redis_storage_test.rb +17 -0
  52. data/test/storage_test_base.rb +54 -0
  53. metadata +88 -8
@@ -0,0 +1,21 @@
1
+ <section class='span-20 colborder'>
2
+ <div class='title'><h1>Status</h1></div>
3
+ <p>
4
+ Using <%= storage_name %> storage <%= storage_config %> and a <%= player_name %> player <%= player_config %> for <%= Bandit.experiments.length %> active experiment(s).
5
+ </p>
6
+ <div class='title'><h1>Running Experiments</h1></div>
7
+ <% @experiments.each { |experiment| %>
8
+ <h2><%= link_to experiment.name.to_s.titleize, url_for(:action => 'show', :id => experiment.name) %></h2>
9
+ <p>
10
+ <%= experiment.description %>
11
+ </p>
12
+ <% experiment.alternatives.each { |alt| %>
13
+ <h3>Alternative: <%= alt %></h3>
14
+ <ul>
15
+ <li>Participants: <%= experiment.participant_count(alt) %></li>
16
+ <li>Conversions: <%= experiment.conversion_count(alt) %></li>
17
+ <li>Conversion rate: <%= experiment.conversion_rate(alt) %> %</li>
18
+ </ul>
19
+ <% } %>
20
+ <% } %>
21
+ </section>
@@ -0,0 +1,24 @@
1
+ <section class='span-20 colborder'>
2
+ <div class='title'><h1><%= @experiment.name.to_s.titleize %></h1></div>
3
+ <p>
4
+ <%= @experiment.description %>
5
+ </p>
6
+
7
+ <div id="gcontainer"></div>
8
+
9
+ <% @experiment.alternatives.each { |alt| %>
10
+ <h2>Alternative: <%= alt %></h2>
11
+ <ul>
12
+ <li>Participants: <%= @experiment.participant_count(alt) %></li>
13
+ <li>Conversions: <%= @experiment.conversion_count(alt) %></li>
14
+ <li>Conversion rate: <%= @experiment.conversion_rate(alt) %> %</li>
15
+ </ul>
16
+ <% } %>
17
+
18
+ </section>
19
+
20
+ <script type="text/javascript">
21
+ var url = "<%= url_for(:controller => 'bandit', :action => 'show', :id => @experiment.name, :format => 'csv') %>";
22
+ var title = "<%= @experiment.name.to_s.titleize %>";
23
+ show_chart(title, url);
24
+ </script>
@@ -0,0 +1,22 @@
1
+ = Bandit Players
2
+ There are a number of different possible players that each seek to explore/exploit using different methods.
3
+
4
+ == Epsilon Greedy
5
+ The epsilon greedy player selects the best alternative with a probability of 1 - epsilon, and selects uniformly among the other alternatives with a probability of epsilon. You can set the value of epsilon in the config file like this:
6
+
7
+ production:
8
+ ...
9
+ player: epsilon_greedy
10
+ player_config:
11
+ epsilon: 0.1
12
+ ...
13
+
14
+
15
+ == Round Robin
16
+ This is included for testing purposes only. It will not optimize. There are no config options.
17
+
18
+
19
+ production:
20
+ ...
21
+ player: round_robin
22
+ ...
@@ -0,0 +1,7 @@
1
+ redis_storage_config:
2
+ host: localhost
3
+ port: 6379
4
+
5
+ memcache_storage_config:
6
+ host: localhost
7
+
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'yaml'
4
+
5
+ $:.unshift(File.join File.dirname(__FILE__), '..', 'lib')
6
+ require 'bandit'
7
+
8
+ CONFIG = YAML.load_file File.join(File.dirname(__FILE__), "config.yml")
9
+
10
+ module SetupHelper
11
+ def new_experiment
12
+ Bandit::Experiment.create(:exp_test) { |exp|
13
+ exp.alternatives = [10,20,30,40]
14
+ exp.title = "A Test Exp"
15
+ exp.description = "Desc"
16
+ }
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ require File.join File.dirname(__FILE__), 'helper'
2
+ require File.join File.dirname(__FILE__), 'storage_test_base'
3
+
4
+ class MemCacheStorageTest < Test::Unit::TestCase
5
+ include StorageTestBase
6
+ include SetupHelper
7
+
8
+ def setup
9
+ Bandit.setup do |config|
10
+ config.player = "round_robin"
11
+ config.storage = 'memcache'
12
+ config.storage_config = CONFIG['memcache_storage_config']
13
+ end
14
+
15
+ @storage = Bandit.storage
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require File.join File.dirname(__FILE__), 'helper'
2
+ require File.join File.dirname(__FILE__), 'storage_test_base'
3
+
4
+ class MemoryStorageTest < Test::Unit::TestCase
5
+ include StorageTestBase
6
+ include SetupHelper
7
+
8
+ def setup
9
+ Bandit.setup do |config|
10
+ config.player = "round_robin"
11
+ config.storage = 'memory'
12
+ end
13
+
14
+ @storage = Bandit.storage
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require File.join File.dirname(__FILE__), 'helper'
2
+ require File.join File.dirname(__FILE__), 'storage_test_base'
3
+
4
+ class MemCacheStorageTest < Test::Unit::TestCase
5
+ include StorageTestBase
6
+ include SetupHelper
7
+
8
+ def setup
9
+ Bandit.setup do |config|
10
+ config.player = "round_robin"
11
+ config.storage = 'redis'
12
+ config.storage_config = CONFIG['redis_storage_config']
13
+ end
14
+
15
+ @storage = Bandit.storage
16
+ end
17
+ end
@@ -0,0 +1,54 @@
1
+ require 'date'
2
+
3
+ module StorageTestBase
4
+ def teardown
5
+ @storage.clear!
6
+ end
7
+
8
+ def test_participant_storage
9
+ exp = new_experiment
10
+ alt = exp.alternatives.first
11
+
12
+ start = Bandit::DateHour.now
13
+ @storage.incr_participants(exp, alt)
14
+ assert_equal @storage.participant_count(exp, alt), 1
15
+ @storage.incr_participants(exp, alt, 2)
16
+ assert_equal @storage.participant_count(exp, alt), 3
17
+ assert_equal @storage.participant_count(exp, alt, Bandit::DateHour.now), 3
18
+
19
+ assert_equal @storage.alternative_start_time(exp, alt), start
20
+
21
+ future = Bandit::DateHour.new(Date.today, Time.now.hour+3)
22
+ @storage.incr_participants(exp, alt, 123, future)
23
+ assert_equal @storage.participant_count(exp, alt, future), 123
24
+
25
+ assert_equal @storage.participant_count(exp, "nonexistant"), 0
26
+ end
27
+
28
+ def test_conversion_storage
29
+ exp = new_experiment
30
+ alt = exp.alternatives.first
31
+
32
+ @storage.incr_conversions(exp, alt)
33
+ assert_equal @storage.conversion_count(exp, alt), 1
34
+ @storage.incr_conversions(exp, alt, 2)
35
+ assert_equal @storage.conversion_count(exp, alt), 3
36
+ assert_equal @storage.conversion_count(exp, alt, Bandit::DateHour.now), 3
37
+
38
+ future = Bandit::DateHour.new(Date.today, Time.now.hour+3)
39
+ @storage.incr_conversions(exp, alt, 123, future)
40
+ assert_equal @storage.conversion_count(exp, alt, future), 123
41
+
42
+ assert_equal @storage.conversion_count(exp, "nonexistant"), 0
43
+ end
44
+
45
+ def test_player_storage
46
+ player = Bandit.player
47
+ exp = new_experiment
48
+
49
+ @storage.player_state_set(exp, player, "something", 123)
50
+ assert_equal @storage.player_state_get(exp, player, "something"), 123
51
+
52
+ assert_equal @storage.player_state_get(exp, player, "nonexistant"), nil
53
+ end
54
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bandit
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brian Muller
@@ -15,10 +15,39 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-05 00:00:00 -04:00
18
+ date: 2011-08-13 00:00:00 -04:00
19
19
  default_executable:
20
- dependencies: []
21
-
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 5
34
+ version: 3.0.5
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rdoc
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :runtime
50
+ version_requirements: *id002
22
51
  description: Bandit provides a way to do multi-armed bandit optimization of alternatives in a rails website
23
52
  email:
24
53
  - brian.muller@livingsocial.com
@@ -31,11 +60,57 @@ extra_rdoc_files: []
31
60
  files:
32
61
  - .gitignore
33
62
  - Gemfile
63
+ - LICENSE
34
64
  - README.rdoc
35
65
  - Rakefile
36
66
  - bandit.gemspec
37
67
  - lib/bandit.rb
68
+ - lib/bandit/config.rb
69
+ - lib/bandit/date_hour.rb
70
+ - lib/bandit/exceptions.rb
71
+ - lib/bandit/experiment.rb
72
+ - lib/bandit/extensions/controller_concerns.rb
73
+ - lib/bandit/extensions/string.rb
74
+ - lib/bandit/extensions/time.rb
75
+ - lib/bandit/extensions/view_concerns.rb
76
+ - lib/bandit/memoizable.rb
77
+ - lib/bandit/players/base.rb
78
+ - lib/bandit/players/epsilon_greedy.rb
79
+ - lib/bandit/players/round_robin.rb
80
+ - lib/bandit/storage/base.rb
81
+ - lib/bandit/storage/memcache.rb
82
+ - lib/bandit/storage/memory.rb
83
+ - lib/bandit/storage/redis.rb
38
84
  - lib/bandit/version.rb
85
+ - lib/generators/bandit/USAGE
86
+ - lib/generators/bandit/dashboard_generator.rb
87
+ - lib/generators/bandit/install_generator.rb
88
+ - lib/generators/bandit/templates/bandit.rake
89
+ - lib/generators/bandit/templates/bandit.rb
90
+ - lib/generators/bandit/templates/bandit.yml
91
+ - lib/generators/bandit/templates/bandit_controller.rb
92
+ - lib/generators/bandit/templates/dashboard/bandit.html.erb
93
+ - lib/generators/bandit/templates/dashboard/css/application.css
94
+ - lib/generators/bandit/templates/dashboard/css/base.css
95
+ - lib/generators/bandit/templates/dashboard/css/toupee/buttons.css
96
+ - lib/generators/bandit/templates/dashboard/css/toupee/forms.css
97
+ - lib/generators/bandit/templates/dashboard/css/toupee/modules.css
98
+ - lib/generators/bandit/templates/dashboard/css/toupee/reset.css
99
+ - lib/generators/bandit/templates/dashboard/css/toupee/structure.css
100
+ - lib/generators/bandit/templates/dashboard/css/toupee/typography.css
101
+ - lib/generators/bandit/templates/dashboard/helpers/bandit_helper.rb
102
+ - lib/generators/bandit/templates/dashboard/js/bandit.js
103
+ - lib/generators/bandit/templates/dashboard/js/highstock.js
104
+ - lib/generators/bandit/templates/dashboard/js/jquery.min.js
105
+ - lib/generators/bandit/templates/dashboard/view/index.html.erb
106
+ - lib/generators/bandit/templates/dashboard/view/show.html.erb
107
+ - players.rdoc
108
+ - test/config.yml
109
+ - test/helper.rb
110
+ - test/memcache_storage_test.rb
111
+ - test/memory_storage_test.rb
112
+ - test/redis_storage_test.rb
113
+ - test/storage_test_base.rb
39
114
  has_rdoc: true
40
115
  homepage: https://github.com/bmuller/bandit
41
116
  licenses: []
@@ -70,5 +145,10 @@ rubygems_version: 1.3.7
70
145
  signing_key:
71
146
  specification_version: 3
72
147
  summary: Multi-armed bandit testing in rails
73
- test_files: []
74
-
148
+ test_files:
149
+ - test/config.yml
150
+ - test/helper.rb
151
+ - test/memcache_storage_test.rb
152
+ - test/memory_storage_test.rb
153
+ - test/redis_storage_test.rb
154
+ - test/storage_test_base.rb