-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathformula_test.html
More file actions
107 lines (88 loc) · 4.12 KB
/
formula_test.html
File metadata and controls
107 lines (88 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<!DOCTYPE html>
<html>
<head>
<title>Formula Test</title>
</head>
<body>
<h1>Sample Size Formula Testing</h1>
<div id="output"></div>
<script>
// Normal inverse function (same as in script.js)
function normalInverse(p) {
const a = [
-3.969683028665376e+01, 2.209460984245205e+02, -2.759285104469687e+02,
1.383577518672690e+02, -3.066479806614716e+01, 2.506628277459239e+00
];
const b = [
-5.447609879822406e+01, 1.615858368580409e+02, -1.556989798598866e+02,
6.680131188771972e+01, -1.328068155288572e+01
];
const c = [
-7.784894002430293e-03, -3.223964580411365e-01, -2.400758277161838e+00,
-2.549732539343734e+00, 4.374664141464968e+00, 2.938163982698783e+00
];
const d = [
7.784695709041462e-03, 3.224671290700398e-01, 2.445134137142996e+00,
3.754408661907416e+00
];
const pLow = 0.02425;
const pHigh = 1 - pLow;
let q, r, result;
if (p < pLow) {
q = Math.sqrt(-2 * Math.log(p));
result = (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
} else if (p <= pHigh) {
q = p - 0.5;
r = q * q;
result = (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q /
(((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
} else {
q = Math.sqrt(-2 * Math.log(1 - p));
result = -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
}
return result;
}
// Standard parameters
const bcr = 5; // 5%
const mde = 30; // 30%
const alpha = 0.05;
const power = 0.8;
const twoSided = true;
const splitRatio = 0.5;
const p1 = bcr / 100; // 0.05
const p2 = p1 * (1 + mde / 100); // 0.05 * 1.30 = 0.065
const zAlpha = twoSided ? normalInverse(1 - alpha / 2) : normalInverse(1 - alpha);
const zBeta = normalInverse(power);
let output = '<h2>Debug Information</h2>';
output += `<p>p1 (baseline): ${p1}</p>`;
output += `<p>p2 (test): ${p2}</p>`;
output += `<p>Z-alpha (two-sided, α=${alpha}): ${zAlpha.toFixed(4)}</p>`;
output += `<p>Z-beta (power=${power}): ${zBeta.toFixed(4)}</p>`;
// Method 1: Current implementation (pooled proportion)
const pBar = (p1 + p2) / 2;
const delta = Math.abs(p2 - p1);
const nEqual1 = 2 * Math.pow(zAlpha + zBeta, 2) * pBar * (1 - pBar) / Math.pow(delta, 2);
output += '<h3>Method 1 (Current - Pooled)</h3>';
output += `<p>p̄ (pooled): ${pBar.toFixed(4)}</p>`;
output += `<p>δ (delta): ${delta.toFixed(4)}</p>`;
output += `<p>n per group (equal): ${Math.ceil(nEqual1)}</p>`;
// Method 2: Alternative with separate variances
const nEqual2 = Math.pow(zAlpha + zBeta, 2) * (p1 * (1 - p1) + p2 * (1 - p2)) / Math.pow(delta, 2);
output += '<h3>Method 2 (Separate Variances)</h3>';
output += `<p>Var1: ${(p1 * (1 - p1)).toFixed(6)}</p>`;
output += `<p>Var2: ${(p2 * (1 - p2)).toFixed(6)}</p>`;
output += `<p>n per group (equal): ${Math.ceil(nEqual2)}</p>`;
// Method 3: Using p1 only for null hypothesis (more conservative)
const nEqual3 = 2 * Math.pow(zAlpha + zBeta, 2) * p1 * (1 - p1) / Math.pow(delta, 2);
output += '<h3>Method 3 (Null Hypothesis Only)</h3>';
output += `<p>Using p1 variance only</p>`;
output += `<p>n per group (equal): ${Math.ceil(nEqual3)}</p>`;
// Statsig expected result
output += '<h3>Expected (Statsig)</h3>';
output += `<p>Total: 6,600 (3,300 per group)</p>`;
document.getElementById('output').innerHTML = output;
</script>
</body>
</html>