A/B Test Significantie Calculator
Controleer of je A/B-testresultaat statistisch significant is. Voer bezoekers en conversies in om de p-waarde, z-score, betrouwbaarheidsinterval en relatieve lift te krijgen.
A/B Test Significantie Calculator
Voer bezoekers en conversies in voor elke variant om te controleren of het verschil echt is of alleen ruis.
Resultaten
Statistisch Significant
Het verschil is onwaarschijnlijk door toeval op je gekozen betrouwbaarheidsniveau.
Chance this gap is just luck — lower is stronger.
How far apart the two rates are, in standard errors.
The likely range for the true difference.
Documentatie
A/B-testing in één minuut
Je hebt een knop, kop of checkout-flow gewijzigd. Versie A is de oude, versie B is de nieuwe. B ziet er beter uit — het converteerde 15% van de bezoekers tegenover 10% voor A. Maar zou dat verschil gewoon geluk kunnen zijn, op dezelfde manier als dat tien keer gooien met een munt zeven koppen kan geven?
Deze calculator beantwoordt precies dat. Je voert in hoeveel mensen elke versie hebben gezien en hoeveel hebben geconverteerd, en het vertelt je de waarschijnlijkheid dat het verschil dat je hebt gemeten echt is in plaats van willekeurige ruis. Als die waarschijnlijkheid groot genoeg is, heb je een winnaar. Zo niet, dan laat je de test verder lopen.
Het enige getal om te volgen is de p-waarde: de kans om een verschil van minstens deze grootte te zien als de twee versies in werkelijkheid identiek waren. Een kleine p-waarde (bijvoorbeeld onder 0,05) betekent "dit zou zelden door geluk gebeuren, dus het verschil is waarschijnlijk echt."
Hoe te gebruiken
- Bezoekers — hoeveel unieke mensen elke versie hebben gezien.
- Conversies — hoeveel van hen hebben gedaan wat je belangrijk vindt (kopen, aanmelden, klikken).
- Betrouwbaarheidsniveau — hoe zeker je wilt zijn voordat je een winnaar aanroept. 95% is de industriestandaard.
- Hypothese — tweezijdig vraagt "is B anders dan A?" (veiliger, de standaard); eenzijdig vraagt alleen "is B beter dan A?" (gevoeliger, maar je moet de richting vooraf bepalen voordat je de test uitvoert).
Het resultaat wordt in real-time bijgewerkt en de invoer wordt opgeslagen in de pagina-URL, dus je kunt een resultaat bladwijzeren of delen. Reset wist het formulier; Voorbeeldgegevens laden vult een uitgewerkt voorbeeld in.
Een uitgewerkt voorbeeld
| Bezoekers | Conversies | Percentage | |
|---|---|---|---|
| A (controle) | 1.000 | 100 | 10,0% |
| B (variatie) | 1.000 | 150 | 15,0% |
Dat is een lift van 5 procentpunten absolute waarde en een +50% relatieve lift. De calculator geeft een p-waarde van ongeveer 0,0007 — onder 0,05 — dus met 95% betrouwbaarheid is het resultaat statistisch significant. Ruwweg gezegd zou je, als de twee versies echt identiek waren, een verschil van deze grootte minder dan eens in de duizend tests zien.
De wiskunde (twee-proporties z-toets)
Achter de schermen is dit de standaard twee-proporties z-toets, dezelfde test die in inleidende statistiek wordt onderwezen en die door de meeste A/B-tools wordt gebruikt.
-
Conversiesnelheid van elke groep:
waarbij conversies zijn en bezoekers.
-
Gepoelde snelheid, aangenomen (voor de nulhypothese) dat beide groepen één werkelijke snelheid delen:
-
Standaardfout van het verschil:
-
De z-score — hoeveel standaardfouten de twee snelheden uit elkaar liggen:
-
De p-waarde komt uit de standaard normale verdeling . Tweezijdig: . Eenzijdig: .
-
Significant wanneer , waarbij (dus 0,05 bij 95%).
De calculator rapporteert ook een betrouwbaarheidsinterval voor het verschil, berekend met de niet-gepoelde standaardfout : het plausibele bereik voor het werkelijke verschil. Als dat interval 0 uitsluit, is het resultaat significant.
Bij 95% betrouwbaarheid telt een z voorbij ±1,96 (de gearceerde staarten, 5% van het gebied) als significant.
Het resultaat eerlijk lezen
- Statistische significantie is niet hetzelfde als zakelijke significantie. Een lift van 0,1% kan statistisch echt zijn, maar niet de moeite waard om uit te voeren.
- Kijk niet vroeg in en stop niet voortijdig. De p-waarde dagelijks controleren en stoppen zodra deze onder 0,05 daalt, vergroot je vals-positieve percentage. Bepaal de steekproefomvang vooraf en laat de test afmaken.
- Let op de vuistregel voor steekproefomvang. De normale benadering is betrouwbaar wanneer elke groep minstens ~10 conversies en ~10 niet-conversies heeft. Met kleine aantallen, behandel het resultaat als alleen richtinggevend.
- Betrouwbaarheid ≠ waarschijnlijkheid dat B beter is. Een betrouwbaarheidsniveau van 95% betekent dat de procedure hooguit 5% van de tijd verkeerd is op lange termijn — het is een frequentistische verklaring, niet "95% kans dat B wint."
- Één test, één metric. Het testen van veel metrics of varianten tegelijk vermenigvuldigt de kans op toeval; corrigeer ervoor (bijv. Bonferroni) of registreer vooraf de enkele metric die de test beslist.
Bereken het zelf
Elk fragment berekent de tweezijdige p-waarde voor het uitgewerkte voorbeeld hierboven.
1=2*(1-NORM.S.DIST(
2 (B2/A2 - B1/A1) /
3 SQRT(((B1+B2)/(A1+A2))*(1-(B1+B2)/(A1+A2))*(1/A1+1/A2)),
4 TRUE))
5' A1,B1 = control visitors, conversions ; A2,B2 = variation visitors, conversions
61from math import sqrt, erf
2
3def ab_pvalue(n1, x1, n2, x2, two_sided=True):
4 p1, p2 = x1 / n1, x2 / n2
5 p = (x1 + x2) / (n1 + n2)
6 se = sqrt(p * (1 - p) * (1 / n1 + 1 / n2))
7 z = (p2 - p1) / se
8 phi = lambda v: 0.5 * (1 + erf(v / sqrt(2))) # standard normal CDF
9 return 2 * (1 - phi(abs(z))) if two_sided else 1 - phi(z)
10
11print(ab_pvalue(1000, 100, 1000, 150)) # ~0.00072
121// erf via Abramowitz & Stegun 7.1.26
2function erf(x) {
3 const s = x < 0 ? -1 : 1;
4 x = Math.abs(x);
5 const t = 1 / (1 + 0.3275911 * x);
6 const y = 1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t
7 - 0.284496736) * t + 0.254829592) * t * Math.exp(-x * x);
8 return s * y;
9}
10const phi = z => 0.5 * (1 + erf(z / Math.SQRT2));
11
12function abPValue(n1, x1, n2, x2, twoSided = true) {
13 const p1 = x1 / n1, p2 = x2 / n2, p = (x1 + x2) / (n1 + n2);
14 const se = Math.sqrt(p * (1 - p) * (1 / n1 + 1 / n2));
15 const z = (p2 - p1) / se;
16 return twoSided ? 2 * (1 - phi(Math.abs(z))) : 1 - phi(z);
17}
18console.log(abPValue(1000, 100, 1000, 150)); // ~0.00072
191public static double abPValue(int n1, int x1, int n2, int x2) {
2 double p1 = (double) x1 / n1, p2 = (double) x2 / n2;
3 double p = (double) (x1 + x2) / (n1 + n2);
4 double se = Math.sqrt(p * (1 - p) * (1.0 / n1 + 1.0 / n2));
5 double z = (p2 - p1) / se;
6 // Phi via the error function; erf approximated (A&S 7.1.26) separately.
7 double phi = 0.5 * (1 + erf(Math.abs(z) / Math.sqrt(2)));
8 return 2 * (1 - phi);
9}
101static double AbPValue(int n1, int x1, int n2, int x2) {
2 double p1 = (double)x1 / n1, p2 = (double)x2 / n2;
3 double p = (double)(x1 + x2) / (n1 + n2);
4 double se = Math.Sqrt(p * (1 - p) * (1.0 / n1 + 1.0 / n2));
5 double z = (p2 - p1) / se;
6 double phi = 0.5 * (1 + Erf(Math.Abs(z) / Math.Sqrt(2)));
7 return 2 * (1 - phi);
8}
91#include <cmath>
2
3double ab_pvalue(int n1, int x1, int n2, int x2) {
4 double p1 = (double)x1 / n1, p2 = (double)x2 / n2;
5 double p = (double)(x1 + x2) / (n1 + n2);
6 double se = std::sqrt(p * (1 - p) * (1.0 / n1 + 1.0 / n2));
7 double z = (p2 - p1) / se;
8 double phi = 0.5 * (1 + std::erf(std::fabs(z) / std::sqrt(2.0)));
9 return 2 * (1 - phi);
10}
111import "math"
2
3func abPValue(n1, x1, n2, x2 float64) float64 {
4 p1, p2 := x1/n1, x2/n2
5 p := (x1 + x2) / (n1 + n2)
6 se := math.Sqrt(p * (1 - p) * (1/n1 + 1/n2))
7 z := (p2 - p1) / se
8 phi := 0.5 * (1 + math.Erf(math.Abs(z)/math.Sqrt2))
9 return 2 * (1 - phi)
10}
111// erf from the `libm` crate; std has no erf.
2fn ab_pvalue(n1: f64, x1: f64, n2: f64, x2: f64) -> f64 {
3 let (p1, p2) = (x1 / n1, x2 / n2);
4 let p = (x1 + x2) / (n1 + n2);
5 let se = (p * (1.0 - p) * (1.0 / n1 + 1.0 / n2)).sqrt();
6 let z = (p2 - p1) / se;
7 let phi = 0.5 * (1.0 + libm::erf(z.abs() / 2f64.sqrt()));
8 2.0 * (1.0 - phi)
9}
101function ab_pvalue($n1, $x1, $n2, $x2) {
2 $p1 = $x1 / $n1; $p2 = $x2 / $n2;
3 $p = ($x1 + $x2) / ($n1 + $n2);
4 $se = sqrt($p * (1 - $p) * (1 / $n1 + 1 / $n2));
5 $z = ($p2 - $p1) / $se;
6 $phi = 0.5 * (1 + erf(abs($z) / sqrt(2)));
7 return 2 * (1 - $phi);
8}
91def ab_pvalue(n1, x1, n2, x2)
2 p1, p2 = x1.to_f / n1, x2.to_f / n2
3 p = (x1 + x2).to_f / (n1 + n2)
4 se = Math.sqrt(p * (1 - p) * (1.0 / n1 + 1.0 / n2))
5 z = (p2 - p1) / se
6 phi = 0.5 * (1 + Math.erf(z.abs / Math.sqrt(2)))
7 2 * (1 - phi)
8end
91# R has this built in: prop.test does the whole thing.
2prop.test(c(100, 150), c(1000, 1000), correct = FALSE)$p.value # ~0.00072
3Veelgestelde vragen
Welke steekproefomvang heb ik nodig? Genoeg zodat een betekenisvolle lift detecteerbaar zou zijn. Staar minimaal naar een paar honderd conversies per variant; gebruik een speciale calculator voor steekproefomvang met je basispercentage en de kleinste lift die detecteerbaar is.
Tweezijdig of eenzijdig? Gebruik tweezijdig tenzij je een sterke, vooraf vastgestelde reden hebt om alleen verbetering te willen. Eenzijdig verdubbelt je gevoeligheid maar verbiedt reageren op B erger is.
Waarom toont de calculator een betrouwbaarheidsinterval? Het toont het plausibele bereik van het werkelijke verschil, niet alleen een ja/nee-antwoord. Een breed interval dat 0 overspant betekent "nog niet genoeg gegevens."
Is dit Bayesiaans? Nee — het is een frequentistische z-toets, de meest onderwezen en gebruikte methode. Bayesiaanse A/B-tools rapporteren "waarschijnlijkheid dat B A verslaat" in plaats van een p-waarde; beide zijn geldig, ze beantwoorden iets andere vragen.