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.
-
#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
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'app/models/split_cat/experiment.rb', line 145 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
83 84 85 86 87 88 |
# File 'app/models/split_cat/experiment.rb', line 83 def choose_hypothesis total = 0 roll = rand( total_weight ) + 1 hypotheses.each { |h| return h if roll <= ( total += h.weight ) } return hypotheses.first 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.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'app/models/split_cat/experiment.rb', line 66 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 26 27 28 29 |
# File 'app/models/split_cat/experiment.rb', line 23 def goal_hash unless @goal_hash @goal_hash = HashWithIndifferentAccess.new goals.map { |goal| @goal_hash[ goal.name ] = goal } end return @goal_hash end |
#hypothesis_counts ⇒ Object
Returns a memoized array of hypothesis name => subject counts
33 34 35 |
# File 'app/models/split_cat/experiment.rb', line 33 def hypothesis_counts @hypothesis_counts ||= HypothesisSubject.subject_counts( self ) end |
#hypothesis_hash ⇒ Object
Return a memoized hash of hypothesis name => hypotheses
39 40 41 42 43 44 45 |
# File 'app/models/split_cat/experiment.rb', line 39 def hypothesis_hash unless @hypothesis_hash @hypothesis_hash = HashWithIndifferentAccess.new hypotheses.map { |hypothesis| @hypothesis_hash[ hypothesis.name ] = hypothesis } end return @hypothesis_hash 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.
98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'app/models/split_cat/experiment.rb', line 98 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
113 114 115 116 117 118 |
# File 'app/models/split_cat/experiment.rb', line 113 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
128 129 130 131 132 133 134 135 136 137 |
# File 'app/models/split_cat/experiment.rb', line 128 def to_csv CSV.generate do |csv| csv << [ nil ] + hypotheses.map { |h| h.name } csv << [ 'total' ] + hypotheses.map { |h| hypothesis_counts[ h.name ] || 0 } goals.each do |g| csv << [ g.name ] + hypotheses.map { |h| goal_counts[ g.name ][ h.name ] || 0 } end end end |
#total_subjects ⇒ Object
47 48 49 |
# File 'app/models/split_cat/experiment.rb', line 47 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
53 54 55 |
# File 'app/models/split_cat/experiment.rb', line 53 def total_weight @total_weight ||= hypotheses.inject( 0 ) { |sum,h| sum + h.weight } end |