field_test 0.8.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/app/controllers/field_test/base_controller.rb +2 -2
- data/ext/field_test/bayestest.h +381 -0
- data/ext/field_test/ext.c +42 -0
- data/ext/field_test/extconf.rb +4 -2
- data/lib/field_test/controller.rb +1 -1
- data/lib/field_test/experiment.rb +3 -9
- data/lib/field_test/helpers.rb +1 -2
- data/lib/field_test/version.rb +1 -1
- metadata +9 -23
- data/ext/field_test/bayestest.hpp +0 -329
- data/ext/field_test/ext.cpp +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 613be253211f115808fccbc17aba8eae3a6bf2a713eaee82d3c0a8d27ad02330
|
|
4
|
+
data.tar.gz: 11f3a88aa737f8c69dde0760863a6ec2a25995172a478f32afeb262276dcdbe8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9daeb6b4dcff1b8deb97c94e3cab45c046137bf3c6665f1ba2833bcf43fd4f772b5f0c2f91e471824a394f3261baa9c712160f6fb521e3878716161f054fbb7
|
|
7
|
+
data.tar.gz: c3c77d85c8eb6eef81a7b8d545e51c086635b6ed509b354e7f8473d1b6064fadca790411668c783dd6044d45b8ab55d17ceeddaaf5bee4dabf9b0e3f810eb00e
|
data/CHANGELOG.md
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -189,8 +189,8 @@ Keep track of when experiments started and ended. Use any format `Time.parse` ac
|
|
|
189
189
|
```yml
|
|
190
190
|
experiments:
|
|
191
191
|
button_color:
|
|
192
|
-
started_at: Dec 1,
|
|
193
|
-
ended_at: Dec 8,
|
|
192
|
+
started_at: Dec 1, 2025 8 am PST
|
|
193
|
+
ended_at: Dec 8, 2025 2 pm PST
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
Add a friendlier name and description with:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module FieldTest
|
|
2
2
|
class BaseController < ActionController::Base
|
|
3
|
-
|
|
3
|
+
http_basic_authenticate_with name: ENV["FIELD_TEST_USERNAME"], password: ENV["FIELD_TEST_PASSWORD"] if ENV["FIELD_TEST_PASSWORD"]
|
|
4
4
|
|
|
5
5
|
protect_from_forgery with: :exception
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
layout "field_test/application"
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* BayesTest C v0.1.1
|
|
3
|
+
* https://github.com/ankane/bayestest-c
|
|
4
|
+
* MIT License
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#pragma once
|
|
8
|
+
|
|
9
|
+
#include <limits.h>
|
|
10
|
+
#include <math.h>
|
|
11
|
+
#include <stdlib.h>
|
|
12
|
+
|
|
13
|
+
/// @private
|
|
14
|
+
static inline double bayestest_logbeta(double a, double b) {
|
|
15
|
+
// TODO use lgamma_r when available
|
|
16
|
+
return lgamma(a) + lgamma(b) - lgamma(a + b);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// @private
|
|
20
|
+
static inline double bayestest_prob_b_beats_a(int alpha_a, int beta_a, int alpha_b, int beta_b) {
|
|
21
|
+
double total = 0.0;
|
|
22
|
+
double logbeta_aa_ba = bayestest_logbeta(alpha_a, beta_a);
|
|
23
|
+
double beta_ba = beta_b + beta_a;
|
|
24
|
+
|
|
25
|
+
for (int i = 0; i < alpha_b; i++) {
|
|
26
|
+
total += exp(
|
|
27
|
+
bayestest_logbeta(alpha_a + i, beta_ba) - log(beta_b + i)
|
|
28
|
+
- bayestest_logbeta(1 + i, beta_b) - logbeta_aa_ba
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return total;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// @private
|
|
36
|
+
static inline double bayestest_prob_c_beats_ab(
|
|
37
|
+
int alpha_a,
|
|
38
|
+
int beta_a,
|
|
39
|
+
int alpha_b,
|
|
40
|
+
int beta_b,
|
|
41
|
+
int alpha_c,
|
|
42
|
+
int beta_c
|
|
43
|
+
) {
|
|
44
|
+
double* log_bb_j_logbeta_j_bb = malloc(sizeof(double) * (unsigned int) alpha_b);
|
|
45
|
+
double* logbeta_ac_i_j = malloc(sizeof(double) * (unsigned int) (alpha_a + alpha_b));
|
|
46
|
+
|
|
47
|
+
if (log_bb_j_logbeta_j_bb == NULL || logbeta_ac_i_j == NULL) {
|
|
48
|
+
free(log_bb_j_logbeta_j_bb);
|
|
49
|
+
free(logbeta_ac_i_j);
|
|
50
|
+
return NAN;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
double total = 0.0;
|
|
54
|
+
double logbeta_ac_bc = bayestest_logbeta(alpha_c, beta_c);
|
|
55
|
+
|
|
56
|
+
for (int j = 0; j < alpha_b; j++) {
|
|
57
|
+
log_bb_j_logbeta_j_bb[j] = log(beta_b + j) + bayestest_logbeta(1 + j, beta_b);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
double abc = beta_a + beta_b + beta_c;
|
|
61
|
+
for (int i = 0; i < alpha_a + alpha_b; i++) {
|
|
62
|
+
logbeta_ac_i_j[i] = bayestest_logbeta(alpha_c + i, abc);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (int i = 0; i < alpha_a; i++) {
|
|
66
|
+
double sum_i = -log(beta_a + i) - bayestest_logbeta(1 + i, beta_a) - logbeta_ac_bc;
|
|
67
|
+
|
|
68
|
+
for (int j = 0; j < alpha_b; j++) {
|
|
69
|
+
total += exp(sum_i + logbeta_ac_i_j[i + j] - log_bb_j_logbeta_j_bb[j]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
free(log_bb_j_logbeta_j_bb);
|
|
74
|
+
free(logbeta_ac_i_j);
|
|
75
|
+
|
|
76
|
+
return 1 - bayestest_prob_b_beats_a(alpha_c, beta_c, alpha_a, beta_a)
|
|
77
|
+
- bayestest_prob_b_beats_a(alpha_c, beta_c, alpha_b, beta_b) + total;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// @private
|
|
81
|
+
static inline double bayestest_prob_d_beats_abc(
|
|
82
|
+
int alpha_a,
|
|
83
|
+
int beta_a,
|
|
84
|
+
int alpha_b,
|
|
85
|
+
int beta_b,
|
|
86
|
+
int alpha_c,
|
|
87
|
+
int beta_c,
|
|
88
|
+
int alpha_d,
|
|
89
|
+
int beta_d
|
|
90
|
+
) {
|
|
91
|
+
double* log_bb_j_logbeta_j_bb = malloc(sizeof(double) * (unsigned int) alpha_b);
|
|
92
|
+
double* log_bc_k_logbeta_k_bc = malloc(sizeof(double) * (unsigned int) alpha_c);
|
|
93
|
+
double* logbeta_bd_i_j_k = malloc(
|
|
94
|
+
sizeof(double) * (unsigned int) (alpha_a + alpha_b + alpha_c)
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (log_bb_j_logbeta_j_bb == NULL || log_bc_k_logbeta_k_bc == NULL
|
|
98
|
+
|| logbeta_bd_i_j_k == NULL) {
|
|
99
|
+
free(log_bb_j_logbeta_j_bb);
|
|
100
|
+
free(log_bc_k_logbeta_k_bc);
|
|
101
|
+
free(logbeta_bd_i_j_k);
|
|
102
|
+
return NAN;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
double total = 0.0;
|
|
106
|
+
double logbeta_ad_bd = bayestest_logbeta(alpha_d, beta_d);
|
|
107
|
+
|
|
108
|
+
for (int j = 0; j < alpha_b; j++) {
|
|
109
|
+
log_bb_j_logbeta_j_bb[j] = log(beta_b + j) + bayestest_logbeta(1 + j, beta_b);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (int k = 0; k < alpha_c; k++) {
|
|
113
|
+
log_bc_k_logbeta_k_bc[k] = log(beta_c + k) + bayestest_logbeta(1 + k, beta_c);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
double abcd = beta_a + beta_b + beta_c + beta_d;
|
|
117
|
+
for (int i = 0; i < alpha_a + alpha_b + alpha_c; i++) {
|
|
118
|
+
logbeta_bd_i_j_k[i] = bayestest_logbeta(alpha_d + i, abcd);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (int i = 0; i < alpha_a; i++) {
|
|
122
|
+
double sum_i = -log(beta_a + i) - bayestest_logbeta(1 + i, beta_a) - logbeta_ad_bd;
|
|
123
|
+
|
|
124
|
+
for (int j = 0; j < alpha_b; j++) {
|
|
125
|
+
double sum_j = sum_i - log_bb_j_logbeta_j_bb[j];
|
|
126
|
+
|
|
127
|
+
for (int k = 0; k < alpha_c; k++) {
|
|
128
|
+
total += exp(sum_j + logbeta_bd_i_j_k[i + j + k] - log_bc_k_logbeta_k_bc[k]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
free(log_bb_j_logbeta_j_bb);
|
|
134
|
+
free(log_bc_k_logbeta_k_bc);
|
|
135
|
+
free(logbeta_bd_i_j_k);
|
|
136
|
+
|
|
137
|
+
return 1 - bayestest_prob_b_beats_a(alpha_a, beta_a, alpha_d, beta_d)
|
|
138
|
+
- bayestest_prob_b_beats_a(alpha_b, beta_b, alpha_d, beta_d)
|
|
139
|
+
- bayestest_prob_b_beats_a(alpha_c, beta_c, alpha_d, beta_d)
|
|
140
|
+
+ bayestest_prob_c_beats_ab(alpha_a, beta_a, alpha_b, beta_b, alpha_d, beta_d)
|
|
141
|
+
+ bayestest_prob_c_beats_ab(alpha_a, beta_a, alpha_c, beta_c, alpha_d, beta_d)
|
|
142
|
+
+ bayestest_prob_c_beats_ab(alpha_b, beta_b, alpha_c, beta_c, alpha_d, beta_d) - total;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// Returns the winning probability of each variant for binary outcomes.
|
|
146
|
+
static inline int bayestest_binary(
|
|
147
|
+
int variants,
|
|
148
|
+
const int* participants,
|
|
149
|
+
const int* conversions,
|
|
150
|
+
double* probabilities
|
|
151
|
+
) {
|
|
152
|
+
if (variants < 0 || variants > 4) {
|
|
153
|
+
return -1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (int i = 0; i < variants; i++) {
|
|
157
|
+
if (participants[i] < 0) {
|
|
158
|
+
return -1;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (participants[i] > INT_MAX / (int) sizeof(double) / 4) {
|
|
162
|
+
return -1;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (conversions[i] < 0) {
|
|
166
|
+
return -1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (conversions[i] > INT_MAX / (int) sizeof(double) / 4) {
|
|
170
|
+
return -1;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (conversions[i] > participants[i]) {
|
|
174
|
+
return -1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
switch (variants) {
|
|
179
|
+
case 0: {
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case 1: {
|
|
183
|
+
probabilities[0] = 1;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case 2: {
|
|
187
|
+
int a = 1;
|
|
188
|
+
int b = 0;
|
|
189
|
+
double prob = bayestest_prob_b_beats_a(
|
|
190
|
+
1 + conversions[a],
|
|
191
|
+
1 + participants[a] - conversions[a],
|
|
192
|
+
1 + conversions[b],
|
|
193
|
+
1 + participants[b] - conversions[b]
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if (isnan(prob)) {
|
|
197
|
+
return -1;
|
|
198
|
+
}
|
|
199
|
+
probabilities[0] = prob;
|
|
200
|
+
probabilities[1] = 1 - prob;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case 3: {
|
|
204
|
+
double total = 0.0;
|
|
205
|
+
for (int i = 0; i < 2; i++) {
|
|
206
|
+
int c = i;
|
|
207
|
+
int b = (i + 1) % 3;
|
|
208
|
+
int a = (i + 2) % 3;
|
|
209
|
+
|
|
210
|
+
double prob = bayestest_prob_c_beats_ab(
|
|
211
|
+
1 + conversions[a],
|
|
212
|
+
1 + participants[a] - conversions[a],
|
|
213
|
+
1 + conversions[b],
|
|
214
|
+
1 + participants[b] - conversions[b],
|
|
215
|
+
1 + conversions[c],
|
|
216
|
+
1 + participants[c] - conversions[c]
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (isnan(prob)) {
|
|
220
|
+
return -1;
|
|
221
|
+
}
|
|
222
|
+
probabilities[i] = prob;
|
|
223
|
+
total += prob;
|
|
224
|
+
}
|
|
225
|
+
probabilities[2] = 1 - total;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case 4: {
|
|
229
|
+
double total = 0.0;
|
|
230
|
+
for (int i = 0; i < 3; i++) {
|
|
231
|
+
int d = i;
|
|
232
|
+
int c = (i + 1) % 4;
|
|
233
|
+
int b = (i + 2) % 4;
|
|
234
|
+
int a = (i + 3) % 4;
|
|
235
|
+
|
|
236
|
+
double prob = bayestest_prob_d_beats_abc(
|
|
237
|
+
1 + conversions[a],
|
|
238
|
+
1 + participants[a] - conversions[a],
|
|
239
|
+
1 + conversions[b],
|
|
240
|
+
1 + participants[b] - conversions[b],
|
|
241
|
+
1 + conversions[c],
|
|
242
|
+
1 + participants[c] - conversions[c],
|
|
243
|
+
1 + conversions[d],
|
|
244
|
+
1 + participants[d] - conversions[d]
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (isnan(prob)) {
|
|
248
|
+
return -1;
|
|
249
|
+
}
|
|
250
|
+
probabilities[i] = prob;
|
|
251
|
+
total += prob;
|
|
252
|
+
}
|
|
253
|
+
probabilities[3] = 1 - total;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
default: {
|
|
257
|
+
return -1;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/// @private
|
|
265
|
+
static inline double bayestest_prob_1_beats_2(int alpha_1, int beta_1, int alpha_2, int beta_2) {
|
|
266
|
+
double total = 0.0;
|
|
267
|
+
double log_b1 = log(beta_1);
|
|
268
|
+
double a2_log_b2 = alpha_2 * log(beta_2);
|
|
269
|
+
double log_b1_b2 = log(beta_1 + beta_2);
|
|
270
|
+
|
|
271
|
+
for (int k = 0; k < alpha_1; k++) {
|
|
272
|
+
total += exp(
|
|
273
|
+
k * log_b1 + a2_log_b2 - (k + alpha_2) * log_b1_b2 - log(k + alpha_2)
|
|
274
|
+
- bayestest_logbeta(k + 1, alpha_2)
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return total;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/// @private
|
|
282
|
+
static inline double bayestest_prob_1_beats_23(
|
|
283
|
+
int alpha_1,
|
|
284
|
+
int beta_1,
|
|
285
|
+
int alpha_2,
|
|
286
|
+
int beta_2,
|
|
287
|
+
int alpha_3,
|
|
288
|
+
int beta_3
|
|
289
|
+
) {
|
|
290
|
+
double total = 0.0;
|
|
291
|
+
double log_b1_b2_b3 = log(beta_1 + beta_2 + beta_3);
|
|
292
|
+
double a1_log_b1 = alpha_1 * log(beta_1);
|
|
293
|
+
double log_b2 = log(beta_2);
|
|
294
|
+
double log_b3 = log(beta_3);
|
|
295
|
+
double loggamma_a1 = lgamma(alpha_1);
|
|
296
|
+
|
|
297
|
+
for (int k = 0; k < alpha_2; k++) {
|
|
298
|
+
double sum_k = a1_log_b1 + k * log_b2 - lgamma(k + 1);
|
|
299
|
+
|
|
300
|
+
for (int l = 0; l < alpha_3; l++) {
|
|
301
|
+
total += exp(
|
|
302
|
+
sum_k + l * log_b3 - (k + l + alpha_1) * log_b1_b2_b3 + lgamma(k + l + alpha_1)
|
|
303
|
+
- lgamma(l + 1) - loggamma_a1
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return 1 - bayestest_prob_1_beats_2(alpha_2, beta_2, alpha_1, beta_1)
|
|
309
|
+
- bayestest_prob_1_beats_2(alpha_3, beta_3, alpha_1, beta_1) + total;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/// Returns the winning probability of each variant for count data.
|
|
313
|
+
static inline int bayestest_count(
|
|
314
|
+
int variants,
|
|
315
|
+
const int* events,
|
|
316
|
+
const int* exposure,
|
|
317
|
+
double* probabilities
|
|
318
|
+
) {
|
|
319
|
+
if (variants < 0 || variants > 3) {
|
|
320
|
+
return -1;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
for (int i = 0; i < variants; i++) {
|
|
324
|
+
if (events[i] < 0) {
|
|
325
|
+
return -1;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (events[i] > INT_MAX / 4) {
|
|
329
|
+
return -1;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (exposure[i] < 0) {
|
|
333
|
+
return -1;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (exposure[i] > INT_MAX / 4) {
|
|
337
|
+
return -1;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
switch (variants) {
|
|
342
|
+
case 0: {
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
case 1: {
|
|
346
|
+
probabilities[0] = 1;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
case 2: {
|
|
350
|
+
int a = 0;
|
|
351
|
+
int b = 1;
|
|
352
|
+
double prob = bayestest_prob_1_beats_2(events[a], exposure[a], events[b], exposure[b]);
|
|
353
|
+
|
|
354
|
+
probabilities[0] = prob;
|
|
355
|
+
probabilities[1] = 1 - prob;
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
case 3: {
|
|
359
|
+
double total = 0.0;
|
|
360
|
+
for (int i = 0; i < 2; i++) {
|
|
361
|
+
int a = i;
|
|
362
|
+
int b = (i + 1) % 3;
|
|
363
|
+
int c = (i + 2) % 3;
|
|
364
|
+
|
|
365
|
+
double prob = bayestest_prob_1_beats_23(
|
|
366
|
+
events[a], exposure[a], events[b], exposure[b], events[c], exposure[c]
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
probabilities[i] = prob;
|
|
370
|
+
total += prob;
|
|
371
|
+
}
|
|
372
|
+
probabilities[2] = 1 - total;
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
default: {
|
|
376
|
+
return -1;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return 0;
|
|
381
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
|
|
3
|
+
#include "bayestest.h"
|
|
4
|
+
|
|
5
|
+
static VALUE probabilities(VALUE self, VALUE results)
|
|
6
|
+
{
|
|
7
|
+
Check_Type(results, T_ARRAY);
|
|
8
|
+
|
|
9
|
+
long count = RARRAY_LEN(results);
|
|
10
|
+
if (count > 4) {
|
|
11
|
+
rb_raise(rb_eArgError, "too many variants");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
int participants[4];
|
|
15
|
+
int conversions[4];
|
|
16
|
+
double probabilities[4];
|
|
17
|
+
|
|
18
|
+
VALUE *results_ptr = RARRAY_PTR(results);
|
|
19
|
+
for (long i = 0; i < count; i++) {
|
|
20
|
+
VALUE v = results_ptr[i];
|
|
21
|
+
participants[i] = NUM2INT(rb_hash_aref(v, ID2SYM(rb_intern("participated"))));
|
|
22
|
+
conversions[i] = NUM2INT(rb_hash_aref(v, ID2SYM(rb_intern("converted"))));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
int status = bayestest_binary((int) count, participants, conversions, probabilities);
|
|
26
|
+
if (status != 0) {
|
|
27
|
+
rb_raise(rb_eRuntimeError, "bad status");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
VALUE rb_probabilities = rb_ary_new_capa(count);
|
|
31
|
+
for (long i = 0; i < count; i++) {
|
|
32
|
+
rb_ary_push(rb_probabilities, DBL2NUM(probabilities[i]));
|
|
33
|
+
}
|
|
34
|
+
return rb_probabilities;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void Init_ext(void)
|
|
38
|
+
{
|
|
39
|
+
VALUE rb_mFieldTest = rb_define_module("FieldTest");
|
|
40
|
+
VALUE rb_mBinaryTest = rb_define_module_under(rb_mFieldTest, "BinaryTest");
|
|
41
|
+
rb_define_singleton_method(rb_mBinaryTest, "probabilities", probabilities, 1);
|
|
42
|
+
}
|
data/ext/field_test/extconf.rb
CHANGED
|
@@ -11,7 +11,7 @@ module FieldTest
|
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def field_test_upgrade_memberships(options
|
|
14
|
+
def field_test_upgrade_memberships(**options)
|
|
15
15
|
participants = FieldTest::Participant.standardize(options[:participant] || field_test_participant)
|
|
16
16
|
preferred = participants.first
|
|
17
17
|
Array(participants[1..-1]).each do |participant|
|
|
@@ -9,9 +9,7 @@ module FieldTest
|
|
|
9
9
|
@description = attributes[:description]
|
|
10
10
|
@variants = attributes[:variants]
|
|
11
11
|
if @variants.any? { |v| !v.is_a?(String) }
|
|
12
|
-
|
|
13
|
-
# or raise error in 0.6
|
|
14
|
-
warn "[field_test] Only string variants are supported (#{id})"
|
|
12
|
+
raise Error, "Only string variants are supported (#{id})"
|
|
15
13
|
end
|
|
16
14
|
@weights = @variants.size.times.map { |i| attributes[:weights].to_a[i] || 1 }
|
|
17
15
|
@winner = attributes[:winner]
|
|
@@ -24,7 +22,7 @@ module FieldTest
|
|
|
24
22
|
@use_events = attributes[:use_events]
|
|
25
23
|
end
|
|
26
24
|
|
|
27
|
-
def variant(participants, options
|
|
25
|
+
def variant(participants, **options)
|
|
28
26
|
return winner if winner && !keep_variant?
|
|
29
27
|
return control if options[:exclude]
|
|
30
28
|
|
|
@@ -149,11 +147,7 @@ module FieldTest
|
|
|
149
147
|
if variants.size <= 3
|
|
150
148
|
probabilities =
|
|
151
149
|
cache_fetch(["field_test", "probabilities"] + results.flat_map { |_, v| [v[:participated], v[:converted]] }) do
|
|
152
|
-
|
|
153
|
-
results.each do |_, v|
|
|
154
|
-
binary_test.add(v[:participated], v[:converted])
|
|
155
|
-
end
|
|
156
|
-
binary_test.probabilities.to_a
|
|
150
|
+
BinaryTest.probabilities(results.values)
|
|
157
151
|
end
|
|
158
152
|
|
|
159
153
|
results.each_key.zip(probabilities) do |variant, prob_winning|
|
data/lib/field_test/helpers.rb
CHANGED
|
@@ -27,10 +27,9 @@ module FieldTest
|
|
|
27
27
|
params_variant
|
|
28
28
|
else
|
|
29
29
|
# cache results for request
|
|
30
|
-
# TODO possibly remove in 0.4.0
|
|
31
30
|
cache_key = [exp.id, participants.map(&:where_values), options.slice(:variant, :exclude)]
|
|
32
31
|
@field_test_cache ||= {}
|
|
33
|
-
@field_test_cache[cache_key] ||= exp.variant(participants, options)
|
|
32
|
+
@field_test_cache[cache_key] ||= exp.variant(participants, **options)
|
|
34
33
|
end
|
|
35
34
|
end
|
|
36
35
|
|
data/lib/field_test/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: field_test
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Kane
|
|
@@ -15,28 +15,28 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '7.
|
|
18
|
+
version: '7.2'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '7.
|
|
25
|
+
version: '7.2'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: activerecord
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '7.
|
|
32
|
+
version: '7.2'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '7.
|
|
39
|
+
version: '7.2'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: browser
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -51,20 +51,6 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '2'
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: rice
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - ">="
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: 4.3.3
|
|
61
|
-
type: :runtime
|
|
62
|
-
prerelease: false
|
|
63
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - ">="
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: 4.3.3
|
|
68
54
|
email: andrew@ankane.org
|
|
69
55
|
executables: []
|
|
70
56
|
extensions:
|
|
@@ -87,8 +73,8 @@ files:
|
|
|
87
73
|
- app/views/field_test/participants/show.html.erb
|
|
88
74
|
- app/views/layouts/field_test/application.html.erb
|
|
89
75
|
- config/routes.rb
|
|
90
|
-
- ext/field_test/bayestest.
|
|
91
|
-
- ext/field_test/ext.
|
|
76
|
+
- ext/field_test/bayestest.h
|
|
77
|
+
- ext/field_test/ext.c
|
|
92
78
|
- ext/field_test/extconf.rb
|
|
93
79
|
- lib/field_test.rb
|
|
94
80
|
- lib/field_test/controller.rb
|
|
@@ -114,14 +100,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
114
100
|
requirements:
|
|
115
101
|
- - ">="
|
|
116
102
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '3.
|
|
103
|
+
version: '3.3'
|
|
118
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
105
|
requirements:
|
|
120
106
|
- - ">="
|
|
121
107
|
- !ruby/object:Gem::Version
|
|
122
108
|
version: '0'
|
|
123
109
|
requirements: []
|
|
124
|
-
rubygems_version:
|
|
110
|
+
rubygems_version: 4.0.6
|
|
125
111
|
specification_version: 4
|
|
126
112
|
summary: A/B testing for Rails
|
|
127
113
|
test_files: []
|
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* BayesTest C++ v0.1.2
|
|
3
|
-
* https://github.com/ankane/bayestest-cpp
|
|
4
|
-
* MIT License
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
#pragma once
|
|
8
|
-
|
|
9
|
-
#include <cmath>
|
|
10
|
-
#include <vector>
|
|
11
|
-
|
|
12
|
-
namespace bayestest {
|
|
13
|
-
|
|
14
|
-
namespace {
|
|
15
|
-
|
|
16
|
-
double logbeta(double a, double b) {
|
|
17
|
-
return std::lgamma(a) + std::lgamma(b) - std::lgamma(a + b);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
double prob_b_beats_a(int alpha_a, int beta_a, int alpha_b, int beta_b) {
|
|
21
|
-
double total = 0.0;
|
|
22
|
-
double logbeta_aa_ba = logbeta(alpha_a, beta_a);
|
|
23
|
-
double beta_ba = beta_b + beta_a;
|
|
24
|
-
|
|
25
|
-
for (auto i = 0; i < alpha_b; i++) {
|
|
26
|
-
total += std::exp(logbeta(alpha_a + i, beta_ba) - std::log(beta_b + i) - logbeta(1 + i, beta_b) - logbeta_aa_ba);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return total;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
double prob_c_beats_ab(int alpha_a, int beta_a, int alpha_b, int beta_b, int alpha_c, int beta_c) {
|
|
33
|
-
double total = 0.0;
|
|
34
|
-
|
|
35
|
-
double logbeta_ac_bc = logbeta(alpha_c, beta_c);
|
|
36
|
-
|
|
37
|
-
std::vector<double> log_bb_j_logbeta_j_bb;
|
|
38
|
-
log_bb_j_logbeta_j_bb.reserve(alpha_b);
|
|
39
|
-
|
|
40
|
-
for (auto j = 0; j < alpha_b; j++) {
|
|
41
|
-
log_bb_j_logbeta_j_bb.push_back(std::log(beta_b + j) + logbeta(1 + j, beta_b));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
double abc = beta_a + beta_b + beta_c;
|
|
45
|
-
std::vector<double> logbeta_ac_i_j;
|
|
46
|
-
logbeta_ac_i_j.reserve(alpha_a + alpha_b);
|
|
47
|
-
|
|
48
|
-
for (auto i = 0; i < alpha_a + alpha_b; i++) {
|
|
49
|
-
logbeta_ac_i_j.push_back(logbeta(alpha_c + i, abc));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
for (auto i = 0; i < alpha_a; i++) {
|
|
53
|
-
double sum_i = -std::log(beta_a + i) - logbeta(1 + i, beta_a) - logbeta_ac_bc;
|
|
54
|
-
|
|
55
|
-
for (auto j = 0; j < alpha_b; j++) {
|
|
56
|
-
total += std::exp(sum_i + logbeta_ac_i_j[i + j] - log_bb_j_logbeta_j_bb[j]);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return 1
|
|
61
|
-
- prob_b_beats_a(alpha_c, beta_c, alpha_a, beta_a)
|
|
62
|
-
- prob_b_beats_a(alpha_c, beta_c, alpha_b, beta_b)
|
|
63
|
-
+ total;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
double prob_d_beats_abc(int alpha_a, int beta_a, int alpha_b, int beta_b, int alpha_c, int beta_c, int alpha_d, int beta_d) {
|
|
67
|
-
double total = 0.0;
|
|
68
|
-
|
|
69
|
-
double logbeta_ad_bd = logbeta(alpha_d, beta_d);
|
|
70
|
-
|
|
71
|
-
std::vector<double> log_bb_j_logbeta_j_bb;
|
|
72
|
-
log_bb_j_logbeta_j_bb.reserve(alpha_b);
|
|
73
|
-
|
|
74
|
-
for (auto j = 0; j < alpha_b; j++) {
|
|
75
|
-
log_bb_j_logbeta_j_bb.push_back(std::log(beta_b + j) + logbeta(1 + j, beta_b));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
std::vector<double> log_bc_k_logbeta_k_bc;
|
|
79
|
-
log_bc_k_logbeta_k_bc.reserve(alpha_c);
|
|
80
|
-
|
|
81
|
-
for (auto k = 0; k < alpha_c; k++) {
|
|
82
|
-
log_bc_k_logbeta_k_bc.push_back(std::log(beta_c + k) + logbeta(1 + k, beta_c));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
double abcd = beta_a + beta_b + beta_c + beta_d;
|
|
86
|
-
std::vector<double> logbeta_bd_i_j_k;
|
|
87
|
-
logbeta_bd_i_j_k.reserve(alpha_a + alpha_b + alpha_c);
|
|
88
|
-
|
|
89
|
-
for (auto i = 0; i < alpha_a + alpha_b + alpha_c; i++) {
|
|
90
|
-
logbeta_bd_i_j_k.push_back(logbeta(alpha_d + i, abcd));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
for (auto i = 0; i < alpha_a; i++) {
|
|
94
|
-
double sum_i = -std::log(beta_a + i) - logbeta(1 + i, beta_a) - logbeta_ad_bd;
|
|
95
|
-
|
|
96
|
-
for (auto j = 0; j < alpha_b; j++) {
|
|
97
|
-
double sum_j = sum_i - log_bb_j_logbeta_j_bb[j];
|
|
98
|
-
|
|
99
|
-
for (auto k = 0; k < alpha_c; k++) {
|
|
100
|
-
total += std::exp(sum_j + logbeta_bd_i_j_k[i + j + k] - log_bc_k_logbeta_k_bc[k]);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return 1
|
|
106
|
-
- prob_b_beats_a(alpha_a, beta_a, alpha_d, beta_d)
|
|
107
|
-
- prob_b_beats_a(alpha_b, beta_b, alpha_d, beta_d)
|
|
108
|
-
- prob_b_beats_a(alpha_c, beta_c, alpha_d, beta_d)
|
|
109
|
-
+ prob_c_beats_ab(alpha_a, beta_a, alpha_b, beta_b, alpha_d, beta_d)
|
|
110
|
-
+ prob_c_beats_ab(alpha_a, beta_a, alpha_c, beta_c, alpha_d, beta_d)
|
|
111
|
-
+ prob_c_beats_ab(alpha_b, beta_b, alpha_c, beta_c, alpha_d, beta_d)
|
|
112
|
-
- total;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
double prob_1_beats_2(int alpha_1, int beta_1, int alpha_2, int beta_2) {
|
|
116
|
-
double total = 0.0;
|
|
117
|
-
double log_b1 = std::log(beta_1);
|
|
118
|
-
double a2_log_b2 = alpha_2 * std::log(beta_2);
|
|
119
|
-
double log_b1_b2 = std::log(beta_1 + beta_2);
|
|
120
|
-
|
|
121
|
-
for (auto k = 0; k < alpha_1; k++) {
|
|
122
|
-
total += std::exp(k * log_b1 +
|
|
123
|
-
a2_log_b2 -
|
|
124
|
-
(k + alpha_2) * log_b1_b2 -
|
|
125
|
-
std::log(k + alpha_2) -
|
|
126
|
-
logbeta(k + 1, alpha_2));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return total;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
double prob_1_beats_23(int alpha_1, int beta_1, int alpha_2, int beta_2, int alpha_3, int beta_3) {
|
|
133
|
-
double total = 0.0;
|
|
134
|
-
|
|
135
|
-
double log_b1_b2_b3 = std::log(beta_1 + beta_2 + beta_3);
|
|
136
|
-
double a1_log_b1 = alpha_1 * std::log(beta_1);
|
|
137
|
-
double log_b2 = std::log(beta_2);
|
|
138
|
-
double log_b3 = std::log(beta_3);
|
|
139
|
-
double loggamma_a1 = std::lgamma(alpha_1);
|
|
140
|
-
|
|
141
|
-
for (auto k = 0; k < alpha_2; k++) {
|
|
142
|
-
double sum_k = a1_log_b1 + k * log_b2 - std::lgamma(k + 1);
|
|
143
|
-
|
|
144
|
-
for (auto l = 0; l < alpha_3; l++) {
|
|
145
|
-
total += std::exp(sum_k + l * log_b3
|
|
146
|
-
- (k + l + alpha_1) * log_b1_b2_b3
|
|
147
|
-
+ std::lgamma(k + l + alpha_1) - std::lgamma(l + 1) - loggamma_a1);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return 1
|
|
152
|
-
- prob_1_beats_2(alpha_2, beta_2, alpha_1, beta_1)
|
|
153
|
-
- prob_1_beats_2(alpha_3, beta_3, alpha_1, beta_1)
|
|
154
|
-
+ total;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/// A test for binary outcomes.
|
|
160
|
-
class BinaryTest {
|
|
161
|
-
public:
|
|
162
|
-
/// Adds a new variant.
|
|
163
|
-
void add(int participants, int conversions) {
|
|
164
|
-
variants.emplace_back(participants, conversions);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/// Returns the winning probability of each variant.
|
|
168
|
-
std::vector<double> probabilities() const {
|
|
169
|
-
std::vector<double> probs;
|
|
170
|
-
probs.reserve(variants.size());
|
|
171
|
-
|
|
172
|
-
switch (variants.size()) {
|
|
173
|
-
case 0: {
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
case 1: {
|
|
177
|
-
probs.push_back(1);
|
|
178
|
-
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
case 2: {
|
|
182
|
-
auto b = variants[0];
|
|
183
|
-
auto a = variants[1];
|
|
184
|
-
|
|
185
|
-
auto prob = prob_b_beats_a(
|
|
186
|
-
1 + a.conversions,
|
|
187
|
-
1 + a.participants - a.conversions,
|
|
188
|
-
1 + b.conversions,
|
|
189
|
-
1 + b.participants - b.conversions
|
|
190
|
-
);
|
|
191
|
-
probs.push_back(prob);
|
|
192
|
-
probs.push_back(1 - prob);
|
|
193
|
-
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
case 3: {
|
|
197
|
-
auto total = 0.0;
|
|
198
|
-
for (auto i = 0; i < 2; i++) {
|
|
199
|
-
auto c = variants[i];
|
|
200
|
-
auto b = variants[(i + 1) % 3];
|
|
201
|
-
auto a = variants[(i + 2) % 3];
|
|
202
|
-
|
|
203
|
-
auto prob = prob_c_beats_ab(
|
|
204
|
-
1 + a.conversions,
|
|
205
|
-
1 + a.participants - a.conversions,
|
|
206
|
-
1 + b.conversions,
|
|
207
|
-
1 + b.participants - b.conversions,
|
|
208
|
-
1 + c.conversions,
|
|
209
|
-
1 + c.participants - c.conversions
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
probs.push_back(prob);
|
|
213
|
-
total += prob;
|
|
214
|
-
}
|
|
215
|
-
probs.push_back(1 - total);
|
|
216
|
-
|
|
217
|
-
break;
|
|
218
|
-
}
|
|
219
|
-
default: {
|
|
220
|
-
auto total = 0.0;
|
|
221
|
-
for (auto i = 0; i < 3; i++) {
|
|
222
|
-
auto d = variants[i];
|
|
223
|
-
auto c = variants[(i + 1) % 4];
|
|
224
|
-
auto b = variants[(i + 2) % 4];
|
|
225
|
-
auto a = variants[(i + 3) % 4];
|
|
226
|
-
|
|
227
|
-
auto prob = prob_d_beats_abc(
|
|
228
|
-
1 + a.conversions,
|
|
229
|
-
1 + a.participants - a.conversions,
|
|
230
|
-
1 + b.conversions,
|
|
231
|
-
1 + b.participants - b.conversions,
|
|
232
|
-
1 + c.conversions,
|
|
233
|
-
1 + c.participants - c.conversions,
|
|
234
|
-
1 + d.conversions,
|
|
235
|
-
1 + d.participants - d.conversions
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
probs.push_back(prob);
|
|
239
|
-
total += prob;
|
|
240
|
-
}
|
|
241
|
-
probs.push_back(1 - total);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return probs;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
private:
|
|
248
|
-
struct Variant {
|
|
249
|
-
Variant(int participants, int conversions) : participants(participants), conversions(conversions) {}
|
|
250
|
-
int participants;
|
|
251
|
-
int conversions;
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
std::vector<Variant> variants;
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
/// A test for count data.
|
|
258
|
-
class CountTest {
|
|
259
|
-
public:
|
|
260
|
-
/// Adds a new variant.
|
|
261
|
-
void add(int events, int exposure) {
|
|
262
|
-
variants.emplace_back(events, exposure);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/// Returns the winning probability of each variant.
|
|
266
|
-
std::vector<double> probabilities() const {
|
|
267
|
-
std::vector<double> probs;
|
|
268
|
-
probs.reserve(variants.size());
|
|
269
|
-
|
|
270
|
-
switch (variants.size()) {
|
|
271
|
-
case 0: {
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
case 1: {
|
|
275
|
-
probs.push_back(1);
|
|
276
|
-
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
case 2: {
|
|
280
|
-
auto a = variants[0];
|
|
281
|
-
auto b = variants[1];
|
|
282
|
-
|
|
283
|
-
auto prob = prob_1_beats_2(
|
|
284
|
-
a.events,
|
|
285
|
-
a.exposure,
|
|
286
|
-
b.events,
|
|
287
|
-
b.exposure
|
|
288
|
-
);
|
|
289
|
-
probs.push_back(prob);
|
|
290
|
-
probs.push_back(1 - prob);
|
|
291
|
-
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
default: {
|
|
295
|
-
auto total = 0.0;
|
|
296
|
-
for (auto i = 0; i < 2; i++) {
|
|
297
|
-
auto a = variants[i];
|
|
298
|
-
auto b = variants[(i + 1) % 3];
|
|
299
|
-
auto c = variants[(i + 2) % 3];
|
|
300
|
-
|
|
301
|
-
auto prob = prob_1_beats_23(
|
|
302
|
-
a.events,
|
|
303
|
-
a.exposure,
|
|
304
|
-
b.events,
|
|
305
|
-
b.exposure,
|
|
306
|
-
c.events,
|
|
307
|
-
c.exposure
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
probs.push_back(prob);
|
|
311
|
-
total += prob;
|
|
312
|
-
}
|
|
313
|
-
probs.push_back(1 - total);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return probs;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
private:
|
|
320
|
-
struct Variant {
|
|
321
|
-
Variant(int events, int exposure) : events(events), exposure(exposure) {}
|
|
322
|
-
int events;
|
|
323
|
-
int exposure;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
std::vector<Variant> variants;
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
}
|
data/ext/field_test/ext.cpp
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#include <rice/rice.hpp>
|
|
2
|
-
#include <rice/stl.hpp>
|
|
3
|
-
|
|
4
|
-
#include "bayestest.hpp"
|
|
5
|
-
|
|
6
|
-
using bayestest::BinaryTest;
|
|
7
|
-
|
|
8
|
-
extern "C"
|
|
9
|
-
void Init_ext() {
|
|
10
|
-
auto rb_mFieldTest = Rice::define_module("FieldTest");
|
|
11
|
-
|
|
12
|
-
Rice::define_class_under<BinaryTest>(rb_mFieldTest, "BinaryTest")
|
|
13
|
-
.define_constructor(Rice::Constructor<BinaryTest>())
|
|
14
|
-
.define_method("add", &BinaryTest::add)
|
|
15
|
-
.define_method("probabilities", &BinaryTest::probabilities);
|
|
16
|
-
}
|