lib/split/experiment.rb in split-4.0.1 vs lib/split/experiment.rb in split-4.0.2
- old
+ new
@@ -1,9 +1,7 @@
# frozen_string_literal: true
-require 'rubystats'
-
module Split
class Experiment
attr_accessor :name
attr_accessor :goals
attr_accessor :alternative_probabilities
@@ -11,11 +9,11 @@
attr_reader :alternatives
attr_reader :resettable
DEFAULT_OPTIONS = {
- :resettable => true
+ resettable: true
}
def self.find(name)
Split.cache(:experiments, name) do
return unless Split.redis.exists?(name)
@@ -50,11 +48,11 @@
def extract_alternatives_from_options(options)
alts = options[:alternatives] || []
if alts.length == 1
if alts[0].is_a? Hash
- alts = alts[0].map{|k, v| {k => v} }
+ alts = alts[0].map { |k, v| { k => v } }
end
end
if alts.empty?
exp_config = Split.configuration.experiment_for(name)
@@ -85,20 +83,20 @@
elsif experiment_configuration_has_changed?
reset unless Split.configuration.reset_manually
persist_experiment_configuration
end
- redis.hmset(experiment_config_key, :resettable, resettable,
+ redis.hmset(experiment_config_key, :resettable, resettable.to_s,
:algorithm, algorithm.to_s)
self
end
def validate!
if @alternatives.empty? && Split.configuration.experiment_for(@name).nil?
raise ExperimentNotFound.new("Experiment #{@name} not found")
end
- @alternatives.each {|a| a.validate! }
+ @alternatives.each { |a| a.validate! }
goals_collection.validate!
end
def new_record?
ExperimentCatalog.find(name).nil?
@@ -107,11 +105,11 @@
def ==(obj)
self.name == obj.name
end
def [](name)
- alternatives.find{|a| a.name == name}
+ alternatives.find { |a| a.name == name }
end
def algorithm
@algorithm ||= Split.configuration.algorithm
end
@@ -119,11 +117,11 @@
def algorithm=(algorithm)
@algorithm = algorithm.is_a?(String) ? algorithm.constantize : algorithm
end
def resettable=(resettable)
- @resettable = resettable.is_a?(String) ? resettable == 'true' : resettable
+ @resettable = resettable.is_a?(String) ? resettable == "true" : resettable
end
def alternatives=(alts)
@alternatives = alts.map do |alternative|
if alternative.kind_of?(Split::Alternative)
@@ -155,11 +153,11 @@
@has_winner = true
Split.configuration.on_experiment_winner_choose.call(self)
end
def participant_count
- alternatives.inject(0){|sum, a| sum + a.participant_count}
+ alternatives.inject(0) { |sum, a| sum + a.participant_count }
end
def control
alternatives.first
end
@@ -260,12 +258,12 @@
def load_from_redis
exp_config = redis.hgetall(experiment_config_key)
options = {
- resettable: exp_config['resettable'],
- algorithm: exp_config['algorithm'],
+ resettable: exp_config["resettable"],
+ algorithm: exp_config["algorithm"],
alternatives: load_alternatives_from_redis,
goals: Split::GoalsCollection.new(@name).load_from_redis,
metadata: load_metadata_from_redis
}
@@ -326,24 +324,24 @@
def calc_alternative_probabilities(winning_counts, number_of_simulations)
alternative_probabilities = {}
winning_counts.each do |alternative, wins|
alternative_probabilities[alternative] = wins / number_of_simulations.to_f
end
- return alternative_probabilities
+ alternative_probabilities
end
def count_simulated_wins(winning_alternatives)
- # initialize a hash to keep track of winning alternative in simulations
+ # initialize a hash to keep track of winning alternative in simulations
winning_counts = {}
alternatives.each do |alternative|
winning_counts[alternative] = 0
end
# count number of times each alternative won, calculate probabilities, place in hash
winning_alternatives.each do |alternative|
winning_counts[alternative] += 1
end
- return winning_counts
+ winning_counts
end
def find_simulated_winner(simulated_cr_hash)
# figure out which alternative had the highest simulated conversion rate
winning_pair = ["", 0.0]
@@ -351,25 +349,25 @@
if rate > winning_pair[1]
winning_pair = [alternative, rate]
end
end
winner = winning_pair[0]
- return winner
+ winner
end
def calc_simulated_conversion_rates(beta_params)
simulated_cr_hash = {}
# create a hash which has the conversion rate pulled from each alternative's beta distribution
beta_params.each do |alternative, params|
alpha = params[0]
beta = params[1]
- simulated_conversion_rate = Rubystats::BetaDistribution.new(alpha, beta).rng
+ simulated_conversion_rate = Split::Algorithms.beta_distribution_rng(alpha, beta)
simulated_cr_hash[alternative] = simulated_conversion_rate
end
- return simulated_cr_hash
+ simulated_cr_hash
end
def calc_beta_params(goal = nil)
beta_params = {}
alternatives.each do |alternative|
@@ -379,11 +377,11 @@
params = [alpha, beta]
beta_params[alternative] = params
end
- return beta_params
+ beta_params
end
def calc_time=(time)
redis.hset(experiment_config_key, :calc_time, time)
end
@@ -392,15 +390,15 @@
redis.hget(experiment_config_key, :calc_time).to_i
end
def jstring(goal = nil)
js_id = if goal.nil?
- name
- else
- name + "-" + goal
- end
- js_id.gsub('/', '--')
+ name
+ else
+ name + "-" + goal
+ end
+ js_id.gsub("/", "--")
end
def cohorting_disabled?
@cohorting_disabled ||= begin
value = redis.hget(experiment_config_key, :cohorting)
@@ -408,97 +406,95 @@
end
end
def disable_cohorting
@cohorting_disabled = true
- redis.hset(experiment_config_key, :cohorting, true)
+ redis.hset(experiment_config_key, :cohorting, true.to_s)
end
def enable_cohorting
@cohorting_disabled = false
- redis.hset(experiment_config_key, :cohorting, false)
+ redis.hset(experiment_config_key, :cohorting, false.to_s)
end
protected
+ def experiment_config_key
+ "experiment_configurations/#{@name}"
+ end
- def experiment_config_key
- "experiment_configurations/#{@name}"
- end
+ def load_metadata_from_configuration
+ Split.configuration.experiment_for(@name)[:metadata]
+ end
- def load_metadata_from_configuration
- Split.configuration.experiment_for(@name)[:metadata]
- end
+ def load_metadata_from_redis
+ meta = redis.get(metadata_key)
+ JSON.parse(meta) unless meta.nil?
+ end
- def load_metadata_from_redis
- meta = redis.get(metadata_key)
- JSON.parse(meta) unless meta.nil?
- end
-
- def load_alternatives_from_configuration
- alts = Split.configuration.experiment_for(@name)[:alternatives]
- raise ArgumentError, "Experiment configuration is missing :alternatives array" unless alts
- if alts.is_a?(Hash)
- alts.keys
- else
- alts.flatten
+ def load_alternatives_from_configuration
+ alts = Split.configuration.experiment_for(@name)[:alternatives]
+ raise ArgumentError, "Experiment configuration is missing :alternatives array" unless alts
+ if alts.is_a?(Hash)
+ alts.keys
+ else
+ alts.flatten
+ end
end
- end
- def load_alternatives_from_redis
- alternatives = redis.lrange(@name, 0, -1)
- alternatives.map do |alt|
- alt = begin
- JSON.parse(alt)
- rescue
- alt
- end
- Split::Alternative.new(alt, @name)
+ def load_alternatives_from_redis
+ alternatives = redis.lrange(@name, 0, -1)
+ alternatives.map do |alt|
+ alt = begin
+ JSON.parse(alt)
+ rescue
+ alt
+ end
+ Split::Alternative.new(alt, @name)
+ end
end
- end
private
+ def redis
+ Split.redis
+ end
- def redis
- Split.redis
- end
+ def redis_interface
+ RedisInterface.new
+ end
- def redis_interface
- RedisInterface.new
- end
+ def persist_experiment_configuration
+ redis_interface.add_to_set(:experiments, name)
+ redis_interface.persist_list(name, @alternatives.map { |alt| { alt.name => alt.weight }.to_json })
+ goals_collection.save
- def persist_experiment_configuration
- redis_interface.add_to_set(:experiments, name)
- redis_interface.persist_list(name, @alternatives.map{|alt| {alt.name => alt.weight}.to_json})
- goals_collection.save
+ if @metadata
+ redis.set(metadata_key, @metadata.to_json)
+ else
+ delete_metadata
+ end
+ end
- if @metadata
- redis.set(metadata_key, @metadata.to_json)
- else
+ def remove_experiment_configuration
+ @alternatives.each(&:delete)
+ goals_collection.delete
delete_metadata
+ redis.del(@name)
end
- end
- def remove_experiment_configuration
- @alternatives.each(&:delete)
- goals_collection.delete
- delete_metadata
- redis.del(@name)
- end
+ def experiment_configuration_has_changed?
+ existing_experiment = Experiment.find(@name)
- def experiment_configuration_has_changed?
- existing_experiment = Experiment.find(@name)
+ existing_experiment.alternatives.map(&:to_s) != @alternatives.map(&:to_s) ||
+ existing_experiment.goals != @goals ||
+ existing_experiment.metadata != @metadata
+ end
- existing_experiment.alternatives.map(&:to_s) != @alternatives.map(&:to_s) ||
- existing_experiment.goals != @goals ||
- existing_experiment.metadata != @metadata
- end
+ def goals_collection
+ Split::GoalsCollection.new(@name, @goals)
+ end
- def goals_collection
- Split::GoalsCollection.new(@name, @goals)
- end
-
- def remove_experiment_cohorting
- @cohorting_disabled = false
- redis.hdel(experiment_config_key, :cohorting)
- end
+ def remove_experiment_cohorting
+ @cohorting_disabled = false
+ redis.hdel(experiment_config_key, :cohorting)
+ end
end
end