Class: SplitCat::Experiment
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- SplitCat::Experiment
- Defined in:
- app/models/split_cat/experiment.rb
Constant Summary collapse
- @@cache =
HashWithIndifferentAccess.new
Class Method Summary collapse
Instance Method Summary collapse
-
#choose_hypothesis ⇒ Object
Returns a random hypothesis with weighted probability.
-
#get_hypothesis(token) ⇒ Object
Return the winner if one has been chosen.
-
#goal_counts ⇒ Object
Returns a memoized array of goal name => hypothesis_name => subject counts.
-
#goal_hash ⇒ Object
Return a memoized hash of goal name => goals.
- #hash_name_to_item(items) ⇒ Object
-
#hypothesis_counts ⇒ Object
Returns a memoized array of hypothesis name => subject counts.
-
#hypothesis_hash ⇒ Object
Return a memoized hash of hypothesis name => hypotheses.
-
#record_goal(goal, token) ⇒ Object
Return true immediately if a winner has already been chosen.
-
#same_structure?(experiment) ⇒ Boolean
Returns true if the experiment has the same name, goals, and hypotheses as this one.
-
#to_csv ⇒ Object
Generates a CSV representing the experiment results * header row of hypothesis names * row of total subject count per hypothesis * goal rows of subject count per hypothesis.
- #total_subjects ⇒ Object
-
#total_weight ⇒ Object
Returns a memoized sum of hypothesis weights.
Class Method Details
.factory(name) ⇒ Object
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'app/models/split_cat/experiment.rb', line 153 def self.factory( name ) if experiment = @@cache[ name ] return experiment end template = SplitCat.config.template( name ) if experiment = Experiment.includes( :goals, :hypotheses ).find_by_name( name ) return nil if template && !experiment.same_structure?( template ) else experiment = template if template && template.save end @@cache[ name.to_sym ] = experiment return experiment end |
Instance Method Details
#choose_hypothesis ⇒ Object
Returns a random hypothesis with weighted probability
81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'app/models/split_cat/experiment.rb', line 81 def choose_hypothesis total = 0 roll = Kernel.rand( total_weight ) + 1 hypothesis = nil hypotheses.each do |h| if roll <= ( total += h.weight ) hypothesis ||= h end end hypothesis ||= hypotheses.first return hypothesis end |
#get_hypothesis(token) ⇒ Object
Return the winner if one has been chosen. Return nil if the token can’t be found. Return the previously assigned hypothesis (or nil on error). Choose a hypothesis randomly. Record the hypothesis assignment and return it.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'app/models/split_cat/experiment.rb', line 64 def get_hypothesis( token ) return winner if winner.present? return nil unless subject = Subject.find_by_token( token ) if join = HypothesisSubject.find_by_experiment_id_and_subject_id( id, subject.id ) hypotheses.each { |h| return h if h.id == join.hypothesis_id } return nil end hypothesis = choose_hypothesis HypothesisSubject.create( :hypothesis_id => hypothesis.id, :subject_id => subject.id, :experiment_id => id ) return hypothesis end |
#goal_counts ⇒ Object
Returns a memoized array of goal name => hypothesis_name => subject counts
17 18 19 |
# File 'app/models/split_cat/experiment.rb', line 17 def goal_counts @goal_counts ||= GoalSubject.subject_counts( self ) end |
#goal_hash ⇒ Object
Return a memoized hash of goal name => goals
23 24 25 |
# File 'app/models/split_cat/experiment.rb', line 23 def goal_hash return @goal_hash ||= hash_name_to_item( goals ) end |
#hash_name_to_item(items) ⇒ Object
27 28 29 30 31 |
# File 'app/models/split_cat/experiment.rb', line 27 def hash_name_to_item( items ) hash = HashWithIndifferentAccess.new items.map { |i| hash[ i.name ] = i } return hash end |
#hypothesis_counts ⇒ Object
Returns a memoized array of hypothesis name => subject counts
35 36 37 |
# File 'app/models/split_cat/experiment.rb', line 35 def hypothesis_counts @hypothesis_counts ||= HypothesisSubject.subject_counts( self ) end |
#hypothesis_hash ⇒ Object
Return a memoized hash of hypothesis name => hypotheses
41 42 43 |
# File 'app/models/split_cat/experiment.rb', line 41 def hypothesis_hash return @hypothesis_hash ||= hash_name_to_item( hypotheses ) end |
#record_goal(goal, token) ⇒ Object
Return true immediately if a winner has already been chosen. Return false if the goal or token can’t be found. Return true if the user isn’t in the experiment or has already recorded this goal. Record the goal and return true.
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'app/models/split_cat/experiment.rb', line 102 def record_goal( goal, token ) return true if winner_id return false unless goal = goal_hash[ goal ] return false unless subject = Subject.find_by_token( token ) return true unless join = HypothesisSubject.find_by_experiment_id_and_subject_id( id, subject.id ) return true if GoalSubject.find_by_goal_id_and_subject_id( goal.id, subject.id ) GoalSubject.create( :goal_id => goal.id, :subject_id => subject.id, :experiment_id => id, :hypothesis_id => join.hypothesis_id) return true end |
#same_structure?(experiment) ⇒ Boolean
Returns true if the experiment has the same name, goals, and hypotheses as this one
117 118 119 120 121 122 |
# File 'app/models/split_cat/experiment.rb', line 117 def same_structure?( experiment ) return nil if name.to_sym != experiment.name.to_sym return nil if goal_hash.keys != experiment.goal_hash.keys return nil if hypothesis_hash.keys != experiment.hypothesis_hash.keys return experiment end |
#to_csv ⇒ Object
Generates a CSV representing the experiment results
* header row of hypothesis names
* row of total subject count per hypothesis
* goal rows of subject count per hypothesis
132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'app/models/split_cat/experiment.rb', line 132 def to_csv CSV.generate do |csv| hypothesis_names = hypotheses.map { |h| h.name } hypothesis_totals = hypotheses.map { |h| hypothesis_counts[ h.name ] || 0 } csv << [ nil ] + hypothesis_names csv << [ 'total' ] + hypothesis_totals goals.each do |g| goal_totals = hypotheses.map { |h| goal_counts[ g.name ][ h.name ] || 0 } csv << [ g.name ] + goal_totals end end end |
#total_subjects ⇒ Object
45 46 47 |
# File 'app/models/split_cat/experiment.rb', line 45 def total_subjects @total_subjects ||= hypothesis_counts.values.inject( 0 ) { |sum,count| sum + ( count || 0 ) } end |
#total_weight ⇒ Object
Returns a memoized sum of hypothesis weights
51 52 53 |
# File 'app/models/split_cat/experiment.rb', line 51 def total_weight @total_weight ||= hypotheses.inject( 0 ) { |sum,h| sum + h.weight } end |