split 3.1.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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