Skip to content

Commit f62b90c

Browse files
hulleywoodandrew
authored andcommitted
added block randomization algorithm and specs (#475)
* added block randomization algorithm and specs * added BlockRandomization explanation to README
1 parent 06107c4 commit f62b90c

3 files changed

Lines changed: 60 additions & 0 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,12 @@ It is possible to specify static weights to favor certain alternatives.
793793
This algorithm will automatically weight the alternatives based on their relative performance,
794794
choosing the better-performing ones more often as trials are completed.
795795

796+
`Split::Algorithms::BlockRandomization` is an algorithm that ensures equal
797+
participation across all alternatives. This algorithm will choose the alternative
798+
with the fewest participants. In the event of multiple minimum participant alternatives
799+
(i.e. starting a new "Block") the algorithm will choose a random alternative from
800+
those minimum participant alternatives.
801+
796802
Users may also write their own algorithms. The default algorithm may be specified globally in the configuration file, or on a per experiment basis using the experiments hash of the configuration file.
797803

798804
To change the algorithm globally for all experiments, use the following in your initializer:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Selects alternative with minimum count of participants
2+
# If all counts are even (i.e. all are minimum), samples from all possible alternatives
3+
4+
module Split
5+
module Algorithms
6+
module BlockRandomization
7+
class << self
8+
def choose_alternative(experiment)
9+
minimum_participant_alternatives(experiment.alternatives).sample
10+
end
11+
12+
private
13+
14+
def minimum_participant_alternatives(alternatives)
15+
alternatives_by_count = alternatives.group_by(&:participant_count)
16+
min_group = alternatives_by_count.min_by { |k, v| k }
17+
min_group.last
18+
end
19+
end
20+
end
21+
end
22+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require "spec_helper"
2+
3+
describe Split::Algorithms::BlockRandomization do
4+
5+
let(:experiment) { Split::Experiment.new 'experiment' }
6+
let(:alternative_A) { Split::Alternative.new 'A', 'experiment' }
7+
let(:alternative_B) { Split::Alternative.new 'B', 'experiment' }
8+
let(:alternative_C) { Split::Alternative.new 'C', 'experiment' }
9+
10+
before :each do
11+
allow(experiment).to receive(:alternatives) { [alternative_A, alternative_B, alternative_C] }
12+
end
13+
14+
it "should return an alternative" do
15+
expect(Split::Algorithms::BlockRandomization.choose_alternative(experiment).class).to eq(Split::Alternative)
16+
end
17+
18+
it "should always return the minimum participation option" do
19+
allow(alternative_A).to receive(:participant_count) { 1 }
20+
allow(alternative_B).to receive(:participant_count) { 1 }
21+
allow(alternative_C).to receive(:participant_count) { 0 }
22+
expect(Split::Algorithms::BlockRandomization.choose_alternative(experiment)).to eq(alternative_C)
23+
end
24+
25+
it "should return one of the minimum participation options when multiple" do
26+
allow(alternative_A).to receive(:participant_count) { 0 }
27+
allow(alternative_B).to receive(:participant_count) { 0 }
28+
allow(alternative_C).to receive(:participant_count) { 0 }
29+
alternative = Split::Algorithms::BlockRandomization.choose_alternative(experiment)
30+
expect([alternative_A, alternative_B, alternative_C].include?(alternative)).to be(true)
31+
end
32+
end

0 commit comments

Comments
 (0)