Class: SplitCat::Experiment

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/split_cat/experiment.rb

Constant Summary collapse

@@cache =
HashWithIndifferentAccess.new

Class Method Summary collapse

Instance Method Summary collapse

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_hypothesisObject

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_countsObject

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_hashObject

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_countsObject

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_hashObject

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

Returns:

  • (Boolean)


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_csvObject

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_subjectsObject



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_weightObject

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