split 3.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c5fbcc8e3abf30d53af50aa09b77ceab245d58a1
4
- data.tar.gz: 0b576e850d6d066a06aaf599b0970622f22c1041
3
+ metadata.gz: 861fb73c59529de9a24f3a78ddd0b5cbe257eb8b
4
+ data.tar.gz: dd67eac616dd9435c3a0ca448de504b3824bccb8
5
5
  SHA512:
6
- metadata.gz: 9a05fd38b874ffa595a76445673c3916c9d00835b641b17416fc41e48eadb731c13377e886889dbecc90aa88c1f2a6264ae11cdb33d9a41043ffde999355b8b1
7
- data.tar.gz: 6491c8f886f1b90e90df207103c2fd52c545ae3f9219f1e43f10c67f2d4b15d57542eb7cb27ace064e58f9163f20938dbeb89d7a115b91ed205e28716f7e04dc
6
+ metadata.gz: d078b157629b473b654b751c18fe11ddae08486f38bce2fef2bdd71b7a8536ce048204adaf9b1e242e6cf76f1c33650db91994e34a23734ecaf4357663b750cf
7
+ data.tar.gz: aa994978184569f624b9d5bb3503b93b498ef4601bb1989fe2e16eabee07a0450adc9555c02c251d70d9a73dce65e40bac0e05bf071dc8d229e4ab87c0b817ec
@@ -716,7 +716,7 @@ Style/LineEndConcatenation:
716
716
  line end.
717
717
  Enabled: false
718
718
 
719
- Style/MethodCallParentheses:
719
+ Style/MethodCallWithoutArgsParentheses:
720
720
  Description: 'Do not use parentheses for method calls with no arguments.'
721
721
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens'
722
722
  Enabled: false
@@ -816,7 +816,7 @@ Style/OneLineConditional:
816
816
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator'
817
817
  Enabled: false
818
818
 
819
- Style/OpMethod:
819
+ Naming/BinaryOperatorParameter:
820
820
  Description: 'When defining binary operators, name the argument other.'
821
821
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg'
822
822
  Enabled: false
@@ -5,7 +5,7 @@ rvm:
5
5
  - 2.1
6
6
  - 2.2.0
7
7
  - 2.2.2
8
- - 2.4.1
8
+ - 2.4.2
9
9
 
10
10
  gemfile:
11
11
  - gemfiles/4.2.gemfile
@@ -1,3 +1,14 @@
1
+ ## 3.2.0 (September 21st, 2017)
2
+
3
+ Features:
4
+
5
+ - Allow configuration of how often winning alternatives are recalculated (@patbl, #501)
6
+
7
+ Bugfixes:
8
+
9
+ - Avoid z_score numeric exception for conversion rates >1 (@cmantas, #503)
10
+ - Fix combined experiments (@semanticart, #502)
11
+
1
12
  ## 3.1.1 (August 30th, 2017)
2
13
 
3
14
  Bugfixes:
data/README.md CHANGED
@@ -115,6 +115,14 @@ As per this [blog post](http://www.evanmiller.org/how-not-to-run-an-ab-test.html
115
115
 
116
116
  The second option uses simulations from a beta distribution to determine the probability that the given alternative is the winner compared to all other alternatives. You can view these probabilities by clicking on the drop-down menu labeled "Confidence." This option should be used when the experiment has more than just 1 control and 1 alternative. It can also be used for a simple, 2-alternative A/B test.
117
117
 
118
+ Calculating the beta-distribution simulations for a large number of experiments can be slow, so the results are cached. You can specify how often they should be recalculated (the default is once per day).
119
+
120
+ ```ruby
121
+ Split.configure do |config|
122
+ config.winning_alternative_recalculation_interval = 3600 # 1 hour
123
+ end
124
+ ```
125
+
118
126
  ## Extras
119
127
 
120
128
  ### Weighted alternatives
@@ -119,6 +119,9 @@ module Split
119
119
  n_a = alternative.participant_count
120
120
  n_c = control.participant_count
121
121
 
122
+ # can't calculate zscore for P(x) > 1
123
+ return 'N/A' if p_a > 1 || p_c > 1
124
+
122
125
  z_score = Split::Zscore.calculate(p_a, n_a, p_c, n_c)
123
126
  end
124
127
 
@@ -6,6 +6,7 @@ module Split
6
6
  raise(Split::InvalidExperimentsFormatError, 'Unable to find experiment #{metric_descriptor} in configuration') if experiment[:combined_experiments].nil?
7
7
 
8
8
  alternative = nil
9
+ weighted_alternatives = nil
9
10
  experiment[:combined_experiments].each do |combined_experiment|
10
11
  if alternative.nil?
11
12
  if control
@@ -15,9 +16,15 @@ module Split
15
16
  alternative = ab_test(combined_experiment, normalized_alternatives[0], *normalized_alternatives[1])
16
17
  end
17
18
  else
18
- ab_test(combined_experiment, [{alternative => 1}])
19
+ weighted_alternatives ||= experiment[:alternatives].each_with_object({}) do |alt, memo|
20
+ alt = Alternative.new(alt, experiment[:name]).name
21
+ memo[alt] = (alt == alternative ? 1 : 0)
22
+ end
23
+
24
+ ab_test(combined_experiment, [weighted_alternatives])
19
25
  end
20
26
  end
27
+ alternative
21
28
  end
22
29
 
23
30
  def find_combined_experiment(metric_descriptor)
@@ -25,6 +25,7 @@ module Split
25
25
  attr_accessor :on_before_experiment_delete
26
26
  attr_accessor :include_rails_helper
27
27
  attr_accessor :beta_probability_simulations
28
+ attr_accessor :winning_alternative_recalculation_interval
28
29
  attr_accessor :redis
29
30
 
30
31
  attr_reader :experiments
@@ -217,6 +218,7 @@ module Split
217
218
  @algorithm = Split::Algorithms::WeightedSample
218
219
  @include_rails_helper = true
219
220
  @beta_probability_simulations = 10000
221
+ @winning_alternative_recalculation_interval = 60 * 60 * 24 # 1 day
220
222
  @redis = ENV.fetch(ENV.fetch('REDIS_PROVIDER', 'REDIS_URL'), 'redis://localhost:6379')
221
223
  end
222
224
 
@@ -262,10 +262,11 @@ module Split
262
262
  end
263
263
 
264
264
  def calc_winning_alternatives
265
- # Super simple cache so that we only recalculate winning alternatives once per day
266
- days_since_epoch = Time.now.utc.to_i / 86400
265
+ # Cache the winning alternatives so we recalculate them once per the specified interval.
266
+ intervals_since_epoch =
267
+ Time.now.utc.to_i / Split.configuration.winning_alternative_recalculation_interval
267
268
 
268
- if self.calc_time != days_since_epoch
269
+ if self.calc_time != intervals_since_epoch
269
270
  if goals.empty?
270
271
  self.estimate_winning_alternative
271
272
  else
@@ -274,7 +275,7 @@ module Split
274
275
  end
275
276
  end
276
277
 
277
- self.calc_time = days_since_epoch
278
+ self.calc_time = intervals_since_epoch
278
279
 
279
280
  self.save
280
281
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Split
3
3
  MAJOR = 3
4
- MINOR = 1
5
- PATCH = 1
4
+ MINOR = 2
5
+ PATCH = 0
6
6
  VERSION = [MAJOR, MINOR, PATCH].join('.')
7
7
  end
@@ -273,6 +273,18 @@ describe Split::Alternative do
273
273
  expect(control.z_score(goal1)).to eq('N/A')
274
274
  expect(control.z_score(goal2)).to eq('N/A')
275
275
  end
276
+
277
+ it "should not blow up for Conversion Rates > 1" do
278
+ control = experiment.control
279
+ control.participant_count = 3474
280
+ control.set_completed_count(4244)
281
+
282
+ alternative2.participant_count = 3434
283
+ alternative2.set_completed_count(4358)
284
+
285
+ expect { control.z_score }.not_to raise_error
286
+ expect { alternative2.z_score }.not_to raise_error
287
+ end
276
288
  end
277
289
 
278
290
  describe "extra_info" do
@@ -46,12 +46,12 @@ describe Split::CombinedExperimentsHelper do
46
46
  end
47
47
  end
48
48
 
49
- it "uses same alternatives for all sub experiments " do
49
+ it "uses same alternative for all sub experiments and returns the alternative" do
50
50
  allow(self).to receive(:get_alternative) { "test-alt" }
51
51
  expect(self).to receive(:ab_test).with(:exp_1_click, {"control"=>0.5}, {"test-alt"=>0.5}) { "test-alt" }
52
- expect(self).to receive(:ab_test).with(:exp_1_scroll, [{"test-alt" => 1}] )
52
+ expect(self).to receive(:ab_test).with(:exp_1_scroll, [{"control" => 0, "test-alt" => 1}])
53
53
 
54
- ab_combined_test('combined_exp_1')
54
+ expect(ab_combined_test('combined_exp_1')).to eq('test-alt')
55
55
  end
56
56
  end
57
57
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: split
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-30 00:00:00.000000000 Z
11
+ date: 2017-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -265,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
265
265
  version: 2.0.0
266
266
  requirements: []
267
267
  rubyforge_project: split
268
- rubygems_version: 2.6.11
268
+ rubygems_version: 2.6.4
269
269
  signing_key:
270
270
  specification_version: 4
271
271
  summary: Rack based split testing framework