kanmi-perf-revenue 1.0.0 → 1.1.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.
- package/README.md +196 -186
- package/dist/empirical/ab-testing.d.ts +83 -0
- package/dist/empirical/ab-testing.d.ts.map +1 -0
- package/dist/empirical/ab-testing.js +281 -0
- package/dist/empirical/ab-testing.js.map +1 -0
- package/dist/empirical/alerting.d.ts +85 -0
- package/dist/empirical/alerting.d.ts.map +1 -0
- package/dist/empirical/alerting.js +358 -0
- package/dist/empirical/alerting.js.map +1 -0
- package/dist/empirical/attribution.d.ts +80 -0
- package/dist/empirical/attribution.d.ts.map +1 -0
- package/dist/empirical/attribution.js +305 -0
- package/dist/empirical/attribution.js.map +1 -0
- package/dist/empirical/cohort.d.ts +75 -0
- package/dist/empirical/cohort.d.ts.map +1 -0
- package/dist/empirical/cohort.js +305 -0
- package/dist/empirical/cohort.js.map +1 -0
- package/dist/empirical/conversion-curve.d.ts +7 -10
- package/dist/empirical/conversion-curve.d.ts.map +1 -1
- package/dist/empirical/conversion-curve.js +37 -4
- package/dist/empirical/conversion-curve.js.map +1 -1
- package/dist/empirical/correlation.d.ts +91 -0
- package/dist/empirical/correlation.d.ts.map +1 -0
- package/dist/empirical/correlation.js +461 -0
- package/dist/empirical/correlation.js.map +1 -0
- package/dist/empirical/data-import.d.ts +22 -0
- package/dist/empirical/data-import.d.ts.map +1 -1
- package/dist/empirical/data-import.js +44 -0
- package/dist/empirical/data-import.js.map +1 -1
- package/dist/empirical/datadog-session-query.d.ts +32 -0
- package/dist/empirical/datadog-session-query.d.ts.map +1 -1
- package/dist/empirical/datadog-session-query.js +238 -17
- package/dist/empirical/datadog-session-query.js.map +1 -1
- package/dist/empirical/engagement-analysis.d.ts +112 -0
- package/dist/empirical/engagement-analysis.d.ts.map +1 -0
- package/dist/empirical/engagement-analysis.js +354 -0
- package/dist/empirical/engagement-analysis.js.map +1 -0
- package/dist/empirical/export.d.ts +75 -0
- package/dist/empirical/export.d.ts.map +1 -0
- package/dist/empirical/export.js +392 -0
- package/dist/empirical/export.js.map +1 -0
- package/dist/empirical/forecasting.d.ts +80 -0
- package/dist/empirical/forecasting.d.ts.map +1 -0
- package/dist/empirical/forecasting.js +287 -0
- package/dist/empirical/forecasting.js.map +1 -0
- package/dist/empirical/funnel.d.ts +66 -0
- package/dist/empirical/funnel.d.ts.map +1 -0
- package/dist/empirical/funnel.js +293 -0
- package/dist/empirical/funnel.js.map +1 -0
- package/dist/empirical/history.d.ts +198 -0
- package/dist/empirical/history.d.ts.map +1 -0
- package/dist/empirical/history.js +396 -0
- package/dist/empirical/history.js.map +1 -0
- package/dist/empirical/index.d.ts +39 -16
- package/dist/empirical/index.d.ts.map +1 -1
- package/dist/empirical/index.js +95 -13
- package/dist/empirical/index.js.map +1 -1
- package/dist/empirical/interactions.d.ts +89 -0
- package/dist/empirical/interactions.d.ts.map +1 -0
- package/dist/empirical/interactions.js +346 -0
- package/dist/empirical/interactions.js.map +1 -0
- package/dist/empirical/opportunity-calculator.d.ts +6 -18
- package/dist/empirical/opportunity-calculator.d.ts.map +1 -1
- package/dist/empirical/opportunity-calculator.js +19 -1
- package/dist/empirical/opportunity-calculator.js.map +1 -1
- package/dist/empirical/report.d.ts +3 -11
- package/dist/empirical/report.d.ts.map +1 -1
- package/dist/empirical/report.js +11 -7
- package/dist/empirical/report.js.map +1 -1
- package/dist/empirical/roi-calculator.d.ts +104 -0
- package/dist/empirical/roi-calculator.d.ts.map +1 -0
- package/dist/empirical/roi-calculator.js +403 -0
- package/dist/empirical/roi-calculator.js.map +1 -0
- package/dist/empirical/seasonality.d.ts +80 -0
- package/dist/empirical/seasonality.d.ts.map +1 -0
- package/dist/empirical/seasonality.js +340 -0
- package/dist/empirical/seasonality.js.map +1 -0
- package/dist/empirical/segmentation.d.ts +135 -0
- package/dist/empirical/segmentation.d.ts.map +1 -0
- package/dist/empirical/segmentation.js +379 -0
- package/dist/empirical/segmentation.js.map +1 -0
- package/dist/empirical/statistics.d.ts +118 -0
- package/dist/empirical/statistics.d.ts.map +1 -0
- package/dist/empirical/statistics.js +344 -0
- package/dist/empirical/statistics.js.map +1 -0
- package/dist/empirical/sweet-spot.d.ts +81 -0
- package/dist/empirical/sweet-spot.d.ts.map +1 -0
- package/dist/empirical/sweet-spot.js +198 -0
- package/dist/empirical/sweet-spot.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistical Significance Analysis
|
|
3
|
+
*
|
|
4
|
+
* Provides statistical rigor for conversion rate comparisons:
|
|
5
|
+
* - Confidence intervals for CVR
|
|
6
|
+
* - Two-proportion z-tests for comparing CVR between groups
|
|
7
|
+
* - Sample size recommendations
|
|
8
|
+
* - Effect size calculations
|
|
9
|
+
*
|
|
10
|
+
* @author Kanmi Obasa <i@kanmiobasa.com>
|
|
11
|
+
*/
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// CONFIDENCE INTERVALS
|
|
14
|
+
// =============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Calculate confidence interval for a proportion using Wilson score interval.
|
|
17
|
+
* More accurate than normal approximation, especially for small samples.
|
|
18
|
+
*/
|
|
19
|
+
export function calculateConfidenceInterval(conversions, sessions, confidenceLevel = 0.95) {
|
|
20
|
+
if (sessions === 0) {
|
|
21
|
+
return {
|
|
22
|
+
estimate: 0,
|
|
23
|
+
lower: 0,
|
|
24
|
+
upper: 0,
|
|
25
|
+
confidence_level: confidenceLevel,
|
|
26
|
+
margin_of_error: 0,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const p = conversions / sessions;
|
|
30
|
+
const n = sessions;
|
|
31
|
+
const z = getZScore(confidenceLevel);
|
|
32
|
+
// Wilson score interval
|
|
33
|
+
const denominator = 1 + (z * z) / n;
|
|
34
|
+
const center = (p + (z * z) / (2 * n)) / denominator;
|
|
35
|
+
const margin = (z * Math.sqrt((p * (1 - p) + (z * z) / (4 * n)) / n)) / denominator;
|
|
36
|
+
return {
|
|
37
|
+
estimate: p,
|
|
38
|
+
lower: Math.max(0, center - margin),
|
|
39
|
+
upper: Math.min(1, center + margin),
|
|
40
|
+
confidence_level: confidenceLevel,
|
|
41
|
+
margin_of_error: margin,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Calculate confidence interval for the difference between two proportions.
|
|
46
|
+
*/
|
|
47
|
+
export function calculateDifferenceCI(conversionsA, sessionsA, conversionsB, sessionsB, confidenceLevel = 0.95) {
|
|
48
|
+
const pA = sessionsA > 0 ? conversionsA / sessionsA : 0;
|
|
49
|
+
const pB = sessionsB > 0 ? conversionsB / sessionsB : 0;
|
|
50
|
+
const diff = pA - pB;
|
|
51
|
+
const z = getZScore(confidenceLevel);
|
|
52
|
+
// Standard error of difference
|
|
53
|
+
const seA = sessionsA > 0 ? Math.sqrt((pA * (1 - pA)) / sessionsA) : 0;
|
|
54
|
+
const seB = sessionsB > 0 ? Math.sqrt((pB * (1 - pB)) / sessionsB) : 0;
|
|
55
|
+
const seDiff = Math.sqrt(seA * seA + seB * seB);
|
|
56
|
+
const margin = z * seDiff;
|
|
57
|
+
return {
|
|
58
|
+
estimate: diff,
|
|
59
|
+
lower: diff - margin,
|
|
60
|
+
upper: diff + margin,
|
|
61
|
+
confidence_level: confidenceLevel,
|
|
62
|
+
margin_of_error: margin,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// =============================================================================
|
|
66
|
+
// HYPOTHESIS TESTING
|
|
67
|
+
// =============================================================================
|
|
68
|
+
/**
|
|
69
|
+
* Two-proportion z-test to compare conversion rates.
|
|
70
|
+
*/
|
|
71
|
+
export function twoProportionZTest(groupAName, conversionsA, sessionsA, groupBName, conversionsB, sessionsB, alpha = 0.05) {
|
|
72
|
+
const pA = sessionsA > 0 ? conversionsA / sessionsA : 0;
|
|
73
|
+
const pB = sessionsB > 0 ? conversionsB / sessionsB : 0;
|
|
74
|
+
const ciA = calculateConfidenceInterval(conversionsA, sessionsA, 1 - alpha);
|
|
75
|
+
const ciB = calculateConfidenceInterval(conversionsB, sessionsB, 1 - alpha);
|
|
76
|
+
// Pooled proportion under null hypothesis
|
|
77
|
+
const pooled = (conversionsA + conversionsB) / (sessionsA + sessionsB);
|
|
78
|
+
// Standard error under null
|
|
79
|
+
const se = Math.sqrt(pooled * (1 - pooled) * (1 / sessionsA + 1 / sessionsB));
|
|
80
|
+
// Z-score
|
|
81
|
+
const zScore = se > 0 ? (pA - pB) / se : 0;
|
|
82
|
+
// P-value (two-tailed)
|
|
83
|
+
const pValue = 2 * (1 - normalCDF(Math.abs(zScore)));
|
|
84
|
+
// Is significant?
|
|
85
|
+
const isSignificant = pValue < alpha;
|
|
86
|
+
// Calculate approximate power
|
|
87
|
+
const power = calculatePower(pA, pB, sessionsA, sessionsB, alpha);
|
|
88
|
+
// Generate insight
|
|
89
|
+
const absoluteDiff = pA - pB;
|
|
90
|
+
const relativeDiff = pB > 0 ? absoluteDiff / pB : 0;
|
|
91
|
+
let insight;
|
|
92
|
+
if (isSignificant) {
|
|
93
|
+
const direction = absoluteDiff > 0 ? 'higher' : 'lower';
|
|
94
|
+
const pctDiff = Math.abs(relativeDiff * 100).toFixed(1);
|
|
95
|
+
insight = `${groupAName} CVR is ${pctDiff}% ${direction} than ${groupBName} (p=${pValue.toFixed(4)}). This difference is statistically significant.`;
|
|
96
|
+
}
|
|
97
|
+
else if (sessionsA < 100 || sessionsB < 100) {
|
|
98
|
+
insight = `Sample size too small for reliable comparison. Need at least 100 sessions per group.`;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
insight = `No statistically significant difference between ${groupAName} and ${groupBName} (p=${pValue.toFixed(4)}).`;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
group_a: {
|
|
105
|
+
name: groupAName,
|
|
106
|
+
conversions: conversionsA,
|
|
107
|
+
sessions: sessionsA,
|
|
108
|
+
cvr: pA,
|
|
109
|
+
ci: ciA,
|
|
110
|
+
},
|
|
111
|
+
group_b: {
|
|
112
|
+
name: groupBName,
|
|
113
|
+
conversions: conversionsB,
|
|
114
|
+
sessions: sessionsB,
|
|
115
|
+
cvr: pB,
|
|
116
|
+
ci: ciB,
|
|
117
|
+
},
|
|
118
|
+
absolute_diff: absoluteDiff,
|
|
119
|
+
relative_diff: relativeDiff,
|
|
120
|
+
z_score: zScore,
|
|
121
|
+
p_value: pValue,
|
|
122
|
+
is_significant: isSignificant,
|
|
123
|
+
alpha,
|
|
124
|
+
power,
|
|
125
|
+
insight,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Calculate effect size (Cohen's h) for proportion differences.
|
|
130
|
+
*/
|
|
131
|
+
export function calculateEffectSize(pA, pB) {
|
|
132
|
+
// Cohen's h = 2 * (arcsin(sqrt(p1)) - arcsin(sqrt(p2)))
|
|
133
|
+
const phiA = 2 * Math.asin(Math.sqrt(pA));
|
|
134
|
+
const phiB = 2 * Math.asin(Math.sqrt(pB));
|
|
135
|
+
const cohensH = Math.abs(phiA - phiB);
|
|
136
|
+
// Interpretation (Cohen's guidelines)
|
|
137
|
+
let interpretation;
|
|
138
|
+
if (cohensH < 0.2) {
|
|
139
|
+
interpretation = 'negligible';
|
|
140
|
+
}
|
|
141
|
+
else if (cohensH < 0.5) {
|
|
142
|
+
interpretation = 'small';
|
|
143
|
+
}
|
|
144
|
+
else if (cohensH < 0.8) {
|
|
145
|
+
interpretation = 'medium';
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
interpretation = 'large';
|
|
149
|
+
}
|
|
150
|
+
const relativeLift = pB > 0 ? (pA - pB) / pB : 0;
|
|
151
|
+
return {
|
|
152
|
+
cohens_h: cohensH,
|
|
153
|
+
interpretation,
|
|
154
|
+
relative_lift: relativeLift,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// =============================================================================
|
|
158
|
+
// SAMPLE SIZE CALCULATIONS
|
|
159
|
+
// =============================================================================
|
|
160
|
+
/**
|
|
161
|
+
* Calculate required sample size for detecting a given effect.
|
|
162
|
+
*/
|
|
163
|
+
export function calculateRequiredSampleSize(baselineCvr, minDetectableEffect, // Relative (e.g., 0.10 for 10% lift)
|
|
164
|
+
alpha = 0.05, power = 0.8) {
|
|
165
|
+
const p1 = baselineCvr;
|
|
166
|
+
const p2 = baselineCvr * (1 + minDetectableEffect);
|
|
167
|
+
// Z-scores for alpha and power
|
|
168
|
+
const zAlpha = getZScore(1 - alpha / 2);
|
|
169
|
+
const zBeta = getZScore(power);
|
|
170
|
+
// Pooled proportion
|
|
171
|
+
const pAvg = (p1 + p2) / 2;
|
|
172
|
+
// Sample size formula for two-proportion test
|
|
173
|
+
const numerator = Math.pow(zAlpha * Math.sqrt(2 * pAvg * (1 - pAvg)) +
|
|
174
|
+
zBeta * Math.sqrt(p1 * (1 - p1) + p2 * (1 - p2)), 2);
|
|
175
|
+
const denominator = Math.pow(p2 - p1, 2);
|
|
176
|
+
const sampleSize = denominator > 0 ? Math.ceil(numerator / denominator) : Infinity;
|
|
177
|
+
return {
|
|
178
|
+
baseline_cvr: baselineCvr,
|
|
179
|
+
mde: minDetectableEffect,
|
|
180
|
+
sample_size_per_group: sampleSize,
|
|
181
|
+
alpha,
|
|
182
|
+
power,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check if current sample size is sufficient.
|
|
187
|
+
*/
|
|
188
|
+
export function isSampleSufficient(sessions, baselineCvr, minDetectableEffect = 0.10, alpha = 0.05, power = 0.8) {
|
|
189
|
+
const recommendation = calculateRequiredSampleSize(baselineCvr, minDetectableEffect, alpha, power);
|
|
190
|
+
const sufficient = sessions >= recommendation.sample_size_per_group;
|
|
191
|
+
let recommendationText;
|
|
192
|
+
if (sufficient) {
|
|
193
|
+
recommendationText = `Sample size sufficient. You can detect a ${(minDetectableEffect * 100).toFixed(0)}% change with ${(power * 100).toFixed(0)}% power.`;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const needed = recommendation.sample_size_per_group - sessions;
|
|
197
|
+
recommendationText = `Need ${needed.toLocaleString()} more sessions to detect a ${(minDetectableEffect * 100).toFixed(0)}% change with ${(power * 100).toFixed(0)}% power.`;
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
sufficient,
|
|
201
|
+
current: sessions,
|
|
202
|
+
required: recommendation.sample_size_per_group,
|
|
203
|
+
recommendation: recommendationText,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// =============================================================================
|
|
207
|
+
// MULTIPLE COMPARISONS
|
|
208
|
+
// =============================================================================
|
|
209
|
+
/**
|
|
210
|
+
* Apply Bonferroni correction for multiple comparisons.
|
|
211
|
+
*/
|
|
212
|
+
export function bonferroniCorrection(alpha, numComparisons) {
|
|
213
|
+
return alpha / numComparisons;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Apply Benjamini-Hochberg FDR correction.
|
|
217
|
+
* Returns adjusted p-values.
|
|
218
|
+
*/
|
|
219
|
+
export function benjaminiHochbergCorrection(pValues) {
|
|
220
|
+
const n = pValues.length;
|
|
221
|
+
const indexed = pValues.map((p, i) => ({ p, i }));
|
|
222
|
+
indexed.sort((a, b) => a.p - b.p);
|
|
223
|
+
const adjusted = new Array(n);
|
|
224
|
+
let minSoFar = 1;
|
|
225
|
+
for (let k = n - 1; k >= 0; k--) {
|
|
226
|
+
const adjustedP = Math.min((indexed[k].p * n) / (k + 1), minSoFar);
|
|
227
|
+
minSoFar = adjustedP;
|
|
228
|
+
adjusted[indexed[k].i] = Math.min(adjustedP, 1);
|
|
229
|
+
}
|
|
230
|
+
return adjusted;
|
|
231
|
+
}
|
|
232
|
+
// =============================================================================
|
|
233
|
+
// HELPERS
|
|
234
|
+
// =============================================================================
|
|
235
|
+
/**
|
|
236
|
+
* Get z-score for a given confidence level.
|
|
237
|
+
*/
|
|
238
|
+
function getZScore(confidenceLevel) {
|
|
239
|
+
// Common z-scores
|
|
240
|
+
if (Math.abs(confidenceLevel - 0.90) < 0.001)
|
|
241
|
+
return 1.645;
|
|
242
|
+
if (Math.abs(confidenceLevel - 0.95) < 0.001)
|
|
243
|
+
return 1.96;
|
|
244
|
+
if (Math.abs(confidenceLevel - 0.99) < 0.001)
|
|
245
|
+
return 2.576;
|
|
246
|
+
// Approximate inverse normal CDF
|
|
247
|
+
return inverseNormalCDF(confidenceLevel);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Approximate inverse normal CDF (probit function).
|
|
251
|
+
*/
|
|
252
|
+
function inverseNormalCDF(p) {
|
|
253
|
+
// Rational approximation (Abramowitz and Stegun)
|
|
254
|
+
if (p <= 0)
|
|
255
|
+
return -Infinity;
|
|
256
|
+
if (p >= 1)
|
|
257
|
+
return Infinity;
|
|
258
|
+
const a = [
|
|
259
|
+
-3.969683028665376e1,
|
|
260
|
+
2.209460984245205e2,
|
|
261
|
+
-2.759285104469687e2,
|
|
262
|
+
1.383577518672690e2,
|
|
263
|
+
-3.066479806614716e1,
|
|
264
|
+
2.506628277459239e0,
|
|
265
|
+
];
|
|
266
|
+
const b = [
|
|
267
|
+
-5.447609879822406e1,
|
|
268
|
+
1.615858368580409e2,
|
|
269
|
+
-1.556989798598866e2,
|
|
270
|
+
6.680131188771972e1,
|
|
271
|
+
-1.328068155288572e1,
|
|
272
|
+
];
|
|
273
|
+
const c = [
|
|
274
|
+
-7.784894002430293e-3,
|
|
275
|
+
-3.223964580411365e-1,
|
|
276
|
+
-2.400758277161838,
|
|
277
|
+
-2.549732539343734,
|
|
278
|
+
4.374664141464968,
|
|
279
|
+
2.938163982698783,
|
|
280
|
+
];
|
|
281
|
+
const d = [
|
|
282
|
+
7.784695709041462e-3,
|
|
283
|
+
3.224671290700398e-1,
|
|
284
|
+
2.445134137142996,
|
|
285
|
+
3.754408661907416,
|
|
286
|
+
];
|
|
287
|
+
const pLow = 0.02425;
|
|
288
|
+
const pHigh = 1 - pLow;
|
|
289
|
+
let q, r;
|
|
290
|
+
if (p < pLow) {
|
|
291
|
+
q = Math.sqrt(-2 * Math.log(p));
|
|
292
|
+
return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
|
|
293
|
+
((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
|
|
294
|
+
}
|
|
295
|
+
else if (p <= pHigh) {
|
|
296
|
+
q = p - 0.5;
|
|
297
|
+
r = q * q;
|
|
298
|
+
return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q /
|
|
299
|
+
(((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
q = Math.sqrt(-2 * Math.log(1 - p));
|
|
303
|
+
return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
|
|
304
|
+
((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Standard normal CDF.
|
|
309
|
+
*/
|
|
310
|
+
function normalCDF(x) {
|
|
311
|
+
// Approximation using error function
|
|
312
|
+
const a1 = 0.254829592;
|
|
313
|
+
const a2 = -0.284496736;
|
|
314
|
+
const a3 = 1.421413741;
|
|
315
|
+
const a4 = -1.453152027;
|
|
316
|
+
const a5 = 1.061405429;
|
|
317
|
+
const p = 0.3275911;
|
|
318
|
+
const sign = x < 0 ? -1 : 1;
|
|
319
|
+
x = Math.abs(x) / Math.sqrt(2);
|
|
320
|
+
const t = 1.0 / (1.0 + p * x);
|
|
321
|
+
const y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
|
|
322
|
+
return 0.5 * (1.0 + sign * y);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Calculate approximate statistical power.
|
|
326
|
+
*/
|
|
327
|
+
function calculatePower(pA, pB, nA, nB, alpha) {
|
|
328
|
+
if (nA === 0 || nB === 0)
|
|
329
|
+
return 0;
|
|
330
|
+
const pooled = (pA * nA + pB * nB) / (nA + nB);
|
|
331
|
+
const se0 = Math.sqrt(pooled * (1 - pooled) * (1 / nA + 1 / nB));
|
|
332
|
+
const se1 = Math.sqrt((pA * (1 - pA)) / nA + (pB * (1 - pB)) / nB);
|
|
333
|
+
const zAlpha = getZScore(1 - alpha / 2);
|
|
334
|
+
const diff = Math.abs(pA - pB);
|
|
335
|
+
if (se0 === 0 || se1 === 0)
|
|
336
|
+
return 0;
|
|
337
|
+
// Non-centrality parameter
|
|
338
|
+
const lambda = diff / se1;
|
|
339
|
+
const criticalValue = zAlpha * se0 / se1;
|
|
340
|
+
// Power = P(|Z| > criticalValue - lambda)
|
|
341
|
+
const power = 1 - normalCDF(criticalValue - lambda) + normalCDF(-criticalValue - lambda);
|
|
342
|
+
return Math.max(0, Math.min(1, power));
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=statistics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statistics.js","sourceRoot":"","sources":["../../src/empirical/statistics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sweet Spot Finder
|
|
3
|
+
*
|
|
4
|
+
* Finds the optimal performance threshold where CVR is highest.
|
|
5
|
+
* Not just "faster = better" — finds the empirical sweet spot from your data.
|
|
6
|
+
*
|
|
7
|
+
* @author Kanmi Obasa <i@kanmiobasa.com>
|
|
8
|
+
*/
|
|
9
|
+
import type { SessionData } from './datadog-session-query.js';
|
|
10
|
+
import type { ConversionCurve } from './conversion-curve.js';
|
|
11
|
+
export type PerformanceMetric = 'lcp' | 'fcp' | 'inp' | 'cls' | 'ttfb' | 'tti' | 'onload' | 'page_size';
|
|
12
|
+
export interface SweetSpot {
|
|
13
|
+
/** The metric analyzed */
|
|
14
|
+
metric: PerformanceMetric;
|
|
15
|
+
/** The bucket with highest CVR */
|
|
16
|
+
peakBucket: {
|
|
17
|
+
range: string;
|
|
18
|
+
lower_ms: number;
|
|
19
|
+
upper_ms: number;
|
|
20
|
+
cvr: number;
|
|
21
|
+
sessions: number;
|
|
22
|
+
conversions: number;
|
|
23
|
+
};
|
|
24
|
+
/** Optimal target threshold (upper bound of peak bucket) */
|
|
25
|
+
optimalTarget: number;
|
|
26
|
+
/** CVR at the sweet spot */
|
|
27
|
+
peakCvr: number;
|
|
28
|
+
/** Overall CVR for comparison */
|
|
29
|
+
overallCvr: number;
|
|
30
|
+
/** How much better is sweet spot vs overall? */
|
|
31
|
+
cvrLift: number;
|
|
32
|
+
/** Statistical confidence */
|
|
33
|
+
confidence: 'high' | 'medium' | 'low';
|
|
34
|
+
/** Insight text */
|
|
35
|
+
insight: string;
|
|
36
|
+
}
|
|
37
|
+
export interface SweetSpotAnalysis {
|
|
38
|
+
/** Sweet spots for each metric */
|
|
39
|
+
spots: {
|
|
40
|
+
lcp: SweetSpot | null;
|
|
41
|
+
fcp: SweetSpot | null;
|
|
42
|
+
inp: SweetSpot | null;
|
|
43
|
+
cls: SweetSpot | null;
|
|
44
|
+
ttfb: SweetSpot | null;
|
|
45
|
+
tti: SweetSpot | null;
|
|
46
|
+
onload: SweetSpot | null;
|
|
47
|
+
page_size: SweetSpot | null;
|
|
48
|
+
};
|
|
49
|
+
/** The single most impactful metric to optimize */
|
|
50
|
+
primaryTarget: {
|
|
51
|
+
metric: PerformanceMetric;
|
|
52
|
+
target: number;
|
|
53
|
+
cvr: number;
|
|
54
|
+
insight: string;
|
|
55
|
+
} | null;
|
|
56
|
+
/** Sessions summary */
|
|
57
|
+
summary: {
|
|
58
|
+
totalSessions: number;
|
|
59
|
+
totalConversions: number;
|
|
60
|
+
overallCvr: number;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export interface SweetSpotConfig {
|
|
64
|
+
/** Minimum sessions per bucket for consideration. Default: 100 */
|
|
65
|
+
minSessions?: number;
|
|
66
|
+
/** Percentile to use for session aggregation (75, 85, 90). Default: 75 */
|
|
67
|
+
percentile?: 75 | 85 | 90;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Find the sweet spot for a single metric.
|
|
71
|
+
*/
|
|
72
|
+
export declare function findSweetSpot(curve: ConversionCurve, metric: PerformanceMetric, config?: SweetSpotConfig): SweetSpot | null;
|
|
73
|
+
/**
|
|
74
|
+
* Find sweet spots for all metrics.
|
|
75
|
+
*/
|
|
76
|
+
export declare function findAllSweetSpots(sessions: SessionData[], config?: SweetSpotConfig): SweetSpotAnalysis;
|
|
77
|
+
/**
|
|
78
|
+
* Generate a sweet spot report.
|
|
79
|
+
*/
|
|
80
|
+
export declare function generateSweetSpotReport(analysis: SweetSpotAnalysis): string;
|
|
81
|
+
//# sourceMappingURL=sweet-spot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweet-spot.d.ts","sourceRoot":"","sources":["../../src/empirical/sweet-spot.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7D,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,WAAW,CAAC;AAExG,MAAM,WAAW,SAAS;IACxB,0BAA0B;IAC1B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,kCAAkC;IAClC,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,mBAAmB;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,KAAK,EAAE;QACL,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;QACtB,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;QACtB,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;QACtB,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;QACvB,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;QACtB,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;QACzB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;KAC7B,CAAC;IACF,mDAAmD;IACnD,aAAa,EAAE;QACb,MAAM,EAAE,iBAAiB,CAAC;QAC1B,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI,CAAC;IACT,uBAAuB;IACvB,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;CAC3B;AAMD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,iBAAiB,EACzB,MAAM,GAAE,eAAoB,GAC3B,SAAS,GAAG,IAAI,CA6DlB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,WAAW,EAAE,EACvB,MAAM,GAAE,eAAoB,GAC3B,iBAAiB,CAsDnB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAuE3E"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sweet Spot Finder
|
|
3
|
+
*
|
|
4
|
+
* Finds the optimal performance threshold where CVR is highest.
|
|
5
|
+
* Not just "faster = better" — finds the empirical sweet spot from your data.
|
|
6
|
+
*
|
|
7
|
+
* @author Kanmi Obasa <i@kanmiobasa.com>
|
|
8
|
+
*/
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// SWEET SPOT FINDER
|
|
11
|
+
// =============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Find the sweet spot for a single metric.
|
|
14
|
+
*/
|
|
15
|
+
export function findSweetSpot(curve, metric, config = {}) {
|
|
16
|
+
const { minSessions = 100 } = config;
|
|
17
|
+
// Filter to buckets with enough sessions
|
|
18
|
+
const validBuckets = curve.buckets.filter((b) => b.sessions >= minSessions);
|
|
19
|
+
if (validBuckets.length === 0) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
// Find bucket with highest CVR
|
|
23
|
+
let peakBucket = validBuckets[0];
|
|
24
|
+
for (const bucket of validBuckets) {
|
|
25
|
+
if (bucket.conversion_rate > peakBucket.conversion_rate) {
|
|
26
|
+
peakBucket = bucket;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const peakCvr = peakBucket.conversion_rate;
|
|
30
|
+
const overallCvr = curve.summary.overall_cvr;
|
|
31
|
+
const cvrLift = overallCvr > 0 ? (peakCvr - overallCvr) / overallCvr : 0;
|
|
32
|
+
// Determine confidence
|
|
33
|
+
let confidence = 'low';
|
|
34
|
+
if (peakBucket.sessions >= 500) {
|
|
35
|
+
confidence = 'high';
|
|
36
|
+
}
|
|
37
|
+
else if (peakBucket.sessions >= 200) {
|
|
38
|
+
confidence = 'medium';
|
|
39
|
+
}
|
|
40
|
+
// Generate insight
|
|
41
|
+
const targetLabel = formatMetricValue(peakBucket.upper_ms, metric);
|
|
42
|
+
const cvrPct = (peakCvr * 100).toFixed(2);
|
|
43
|
+
const liftPct = (cvrLift * 100).toFixed(1);
|
|
44
|
+
let insight;
|
|
45
|
+
if (cvrLift > 0.1) {
|
|
46
|
+
insight = `Sessions with ${metric.toUpperCase()} in ${peakBucket.range} convert at ${cvrPct}% — ${liftPct}% better than average. Target: ${targetLabel}`;
|
|
47
|
+
}
|
|
48
|
+
else if (cvrLift > 0) {
|
|
49
|
+
insight = `Optimal ${metric.toUpperCase()} range is ${peakBucket.range} (${cvrPct}% CVR). Marginal improvement over average.`;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
insight = `No clear ${metric.toUpperCase()} sweet spot. CVR is relatively flat across performance tiers.`;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
metric,
|
|
56
|
+
peakBucket: {
|
|
57
|
+
range: peakBucket.range,
|
|
58
|
+
lower_ms: peakBucket.lower_ms,
|
|
59
|
+
upper_ms: peakBucket.upper_ms,
|
|
60
|
+
cvr: peakBucket.conversion_rate,
|
|
61
|
+
sessions: peakBucket.sessions,
|
|
62
|
+
conversions: peakBucket.conversions,
|
|
63
|
+
},
|
|
64
|
+
optimalTarget: peakBucket.upper_ms,
|
|
65
|
+
peakCvr,
|
|
66
|
+
overallCvr,
|
|
67
|
+
cvrLift,
|
|
68
|
+
confidence,
|
|
69
|
+
insight,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Find sweet spots for all metrics.
|
|
74
|
+
*/
|
|
75
|
+
export function findAllSweetSpots(sessions, config = {}) {
|
|
76
|
+
const { buildConversionCurve } = require('./conversion-curve.js');
|
|
77
|
+
// Build curves for all metrics
|
|
78
|
+
const lcpCurve = buildConversionCurve(sessions, 'lcp', config);
|
|
79
|
+
const fcpCurve = buildConversionCurve(sessions, 'fcp', config);
|
|
80
|
+
const inpCurve = buildConversionCurve(sessions, 'inp', config);
|
|
81
|
+
const clsCurve = buildConversionCurve(sessions, 'cls', config);
|
|
82
|
+
const ttfbCurve = buildConversionCurve(sessions, 'ttfb', config);
|
|
83
|
+
const ttiCurve = buildConversionCurve(sessions, 'tti', config);
|
|
84
|
+
const onloadCurve = buildConversionCurve(sessions, 'onload', config);
|
|
85
|
+
const pageSizeCurve = buildConversionCurve(sessions, 'page_size', config);
|
|
86
|
+
const spots = {
|
|
87
|
+
lcp: findSweetSpot(lcpCurve, 'lcp', config),
|
|
88
|
+
fcp: findSweetSpot(fcpCurve, 'fcp', config),
|
|
89
|
+
inp: findSweetSpot(inpCurve, 'inp', config),
|
|
90
|
+
cls: findSweetSpot(clsCurve, 'cls', config),
|
|
91
|
+
ttfb: findSweetSpot(ttfbCurve, 'ttfb', config),
|
|
92
|
+
tti: findSweetSpot(ttiCurve, 'tti', config),
|
|
93
|
+
onload: findSweetSpot(onloadCurve, 'onload', config),
|
|
94
|
+
page_size: findSweetSpot(pageSizeCurve, 'page_size', config),
|
|
95
|
+
};
|
|
96
|
+
// Find primary target (highest CVR lift with good confidence)
|
|
97
|
+
const validSpots = Object.entries(spots)
|
|
98
|
+
.filter(([_, spot]) => spot !== null && spot.confidence !== 'low')
|
|
99
|
+
.map(([metric, spot]) => ({ metric: metric, spot: spot }));
|
|
100
|
+
validSpots.sort((a, b) => b.spot.cvrLift - a.spot.cvrLift);
|
|
101
|
+
const primaryTarget = validSpots.length > 0
|
|
102
|
+
? {
|
|
103
|
+
metric: validSpots[0].metric,
|
|
104
|
+
target: validSpots[0].spot.optimalTarget,
|
|
105
|
+
cvr: validSpots[0].spot.peakCvr,
|
|
106
|
+
insight: validSpots[0].spot.insight,
|
|
107
|
+
}
|
|
108
|
+
: null;
|
|
109
|
+
// Summary
|
|
110
|
+
const totalSessions = sessions.length;
|
|
111
|
+
const totalConversions = sessions.filter((s) => s.has_purchase).length;
|
|
112
|
+
const overallCvr = totalSessions > 0 ? totalConversions / totalSessions : 0;
|
|
113
|
+
return {
|
|
114
|
+
spots,
|
|
115
|
+
primaryTarget,
|
|
116
|
+
summary: {
|
|
117
|
+
totalSessions,
|
|
118
|
+
totalConversions,
|
|
119
|
+
overallCvr,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Generate a sweet spot report.
|
|
125
|
+
*/
|
|
126
|
+
export function generateSweetSpotReport(analysis) {
|
|
127
|
+
const lines = [];
|
|
128
|
+
lines.push('# Performance Sweet Spots');
|
|
129
|
+
lines.push('');
|
|
130
|
+
lines.push('> Finding the optimal performance thresholds where CVR peaks.');
|
|
131
|
+
lines.push('> Based on empirical data from your sessions — no assumed coefficients.');
|
|
132
|
+
lines.push('');
|
|
133
|
+
// Summary
|
|
134
|
+
lines.push('## Summary');
|
|
135
|
+
lines.push('');
|
|
136
|
+
lines.push(`- **Total Sessions:** ${analysis.summary.totalSessions.toLocaleString()}`);
|
|
137
|
+
lines.push(`- **Conversions:** ${analysis.summary.totalConversions.toLocaleString()}`);
|
|
138
|
+
lines.push(`- **Overall CVR:** ${(analysis.summary.overallCvr * 100).toFixed(2)}%`);
|
|
139
|
+
lines.push('');
|
|
140
|
+
// Primary target
|
|
141
|
+
if (analysis.primaryTarget) {
|
|
142
|
+
lines.push('## Primary Optimization Target');
|
|
143
|
+
lines.push('');
|
|
144
|
+
lines.push('```');
|
|
145
|
+
lines.push(`Metric: ${analysis.primaryTarget.metric.toUpperCase()}`);
|
|
146
|
+
lines.push(`Target: ${formatMetricValue(analysis.primaryTarget.target, analysis.primaryTarget.metric)}`);
|
|
147
|
+
lines.push(`CVR at target: ${(analysis.primaryTarget.cvr * 100).toFixed(2)}%`);
|
|
148
|
+
lines.push('');
|
|
149
|
+
lines.push(analysis.primaryTarget.insight);
|
|
150
|
+
lines.push('```');
|
|
151
|
+
lines.push('');
|
|
152
|
+
}
|
|
153
|
+
// All metrics
|
|
154
|
+
lines.push('## Sweet Spots by Metric');
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push('| Metric | Sweet Spot Range | Peak CVR | vs Average | Confidence |');
|
|
157
|
+
lines.push('|--------|------------------|----------|------------|------------|');
|
|
158
|
+
for (const [metric, spot] of Object.entries(analysis.spots)) {
|
|
159
|
+
if (spot) {
|
|
160
|
+
const liftStr = spot.cvrLift >= 0
|
|
161
|
+
? `+${(spot.cvrLift * 100).toFixed(1)}%`
|
|
162
|
+
: `${(spot.cvrLift * 100).toFixed(1)}%`;
|
|
163
|
+
lines.push(`| ${metric.toUpperCase()} | ${spot.peakBucket.range} | ${(spot.peakCvr * 100).toFixed(2)}% | ${liftStr} | ${spot.confidence} |`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
lines.push(`| ${metric.toUpperCase()} | — | — | — | insufficient data |`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
lines.push('');
|
|
170
|
+
// Insights
|
|
171
|
+
lines.push('## Insights');
|
|
172
|
+
lines.push('');
|
|
173
|
+
for (const [metric, spot] of Object.entries(analysis.spots)) {
|
|
174
|
+
if (spot && spot.cvrLift > 0.05) {
|
|
175
|
+
lines.push(`- **${metric.toUpperCase()}:** ${spot.insight}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (Object.values(analysis.spots).every((s) => !s || s.cvrLift <= 0.05)) {
|
|
179
|
+
lines.push('- CVR is relatively flat across performance tiers. Focus on other conversion drivers.');
|
|
180
|
+
}
|
|
181
|
+
lines.push('');
|
|
182
|
+
lines.push('---');
|
|
183
|
+
lines.push('*Sweet spots identified from empirical session data.*');
|
|
184
|
+
return lines.join('\n');
|
|
185
|
+
}
|
|
186
|
+
// =============================================================================
|
|
187
|
+
// HELPERS
|
|
188
|
+
// =============================================================================
|
|
189
|
+
function formatMetricValue(ms, metric) {
|
|
190
|
+
if (metric === 'cls') {
|
|
191
|
+
return (ms / 1000).toFixed(2);
|
|
192
|
+
}
|
|
193
|
+
if (ms >= 1000) {
|
|
194
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
195
|
+
}
|
|
196
|
+
return `${ms}ms`;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=sweet-spot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweet-spot.js","sourceRoot":"","sources":["../../src/empirical/sweet-spot.ts"],"names":[],"mappings":"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"}
|