A/B-testin merkitsevyyslaskin
Tarkista, onko A/B-testin tulos tilastollisesti merkitsevä. Syötä vierailijat ja muunnokset saadaksesi p-arvon, z-pistemäärän, luottamusvälin ja suhteellisen nosteen.
A/B-testin merkitsevyyslaskin
Syötä vierailijat ja muunnokset jokaiselle variantille tarkistaaksesi, onko ero todellinen vai vain kohinaa.
Tulokset
Tilastollisesti merkitsevä
Ero ei todennäköisesti johdu sattumasta valitulla luottamustasolla.
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.
Dokumentaatio
A/B-testaus yhdessä minuutissa
Vaihdoit painikkeen, otsikon tai kassavirtausta. Versio A on vanha, versio B on uusi. B näyttää paremmalta — se muunsi 15 % vierailijoista verrattuna 10 %:iin A:lle. Voiko tämä ero johtua pelkästään sattumasta, samalla tavalla kuin kolikkoa heittäessä kymmenen kertaa voidaan saada seitsemän kruunaa?
Tämä laskin vastaa juuri siihen. Syötät kuinka monta ihmistä näki jokaisen version ja kuinka moni muuntui, ja se kertoo sinulle todennäköisyyden, että mittaamasi ero on todellinen eikä satunnaista kohinaa. Jos todennäköisyys on riittävän suuri, sinulla on voittaja. Jos ei, jatkat testin suorittamista.
Yksi numero, jota kannattaa seurata, on p-arvo: mahdollisuus nähdä yhtä suuri ero jos kaksi versiota olisivat todella identtisiä. Pieni p-arvo (esimerkiksi alle 0,05) tarkoittaa "tämä tapahtuisi harvoin sattumalla, joten ero on todennäköisesti todellinen."
Kuinka käyttää sitä
- Vierailijat — kuinka monta ainutlaatuista ihmistä näki jokaisen version.
- Muunnokset — kuinka moni heistä teki asian, josta välität (osti, rekisteröityi, klikkasi).
- Luottamustaso — kuinka varma haluat olla ennen voittajan julistamista. 95 % on alan vakio.
- Hypoteesi — kaksipuolinen kysyy "onko B erilainen kuin A?" (turvallisempi, oletusarvo); yksipuolinen kysyy vain "onko B parempi kuin A?" (herkempi, mutta sinun on päätettävä suunta ennen testin suorittamista).
Tulos päivittyy reaaliajassa ja syötteet tallennetaan sivun URL-osoitteeseen, joten voit merkitä tai jakaa tuloksen. Nollaa tyhjentää lomakkeen; Lataa näytetiedot täyttää työskennellyn esimerkin.
Työskennelty esimerkki
| Vierailijat | Muunnokset | Korko | |
|---|---|---|---|
| A (hallinta) | 1 000 | 100 | 10,0 % |
| B (variaatio) | 1 000 | 150 | 15,0 % |
Se on 5 prosenttiyksikön absoluuttinen nosto ja +50 % suhteellinen nosto. Laskin palauttaa p-arvon noin 0,0007 — alle 0,05 — joten 95 %:n luottamustasolla tulos on tilastollisesti merkitsevä. Karkeasti sanottuna, jos kaksi versiota olisivat todella identtisiä, näkisit tämän kokoisen aukon alle kerran tuhannen testin joukossa.
Matematiikka (kahden osuuden z-testi)
Pinnan alla tämä on standardi kahden osuuden z-testi, sama testi, jota opetetaan johdantotilastotieteessä ja jota käyttävät valtavirran A/B-työkalut.
-
Kunkin ryhmän muuntumisaste:
jossa on muunnokset ja on vierailijat.
-
Yhdistetty korko, olettaen (nollahypoteesin osalta), että molemmat ryhmät jakavat yhden todellisen koron:
-
Erojen keskivirhe:
-
Z-pistemäärä — kuinka monta keskivirhettä kaksi korkoa on toisistaan:
-
p-arvo tulee standardinormaalijakaumasta . Kaksipuolinen: . Yksipuolinen: .
-
Merkitsevä, kun , missä (joten 0,05 95 %:lla).
Laskin raportoi myös luottamusvälin erolle, joka lasketaan yhdistämättömällä keskivirheellä : uskottava vaihteluväli todelliselle erolle. Jos kyseinen väli sulkee pois 0:n, tulos on merkitsevä.
95 %:n luottamustasolla z:n arvo, joka ylittää ±1,96:n (varjostetut hännät, 5 % alueesta), on merkitsevä.
Tuloksen rehellinen lukeminen
- Tilastollinen merkitsevyys ei ole liiketaloudellinen merkitsevyys. 0,1 % nosto voi olla tilastollisesti todellinen, mutta silti ei kannata toimittaa.
- Älä kurkista ja lopeta aikaisin. P-arvon tarkistaminen päivittäin ja pysäyttäminen heti, kun se laskee alle 0,05:n, lisää väärien positiivisten tulosten määrää. Päätä otoskoko etukäteen ja anna testin valmistua.
- Noudata näytteen koon nyrkkisääntöä. Normaali likiarvo on luotettava, kun jokaisella ryhmällä on vähintään ~10 muunnosta ja ~10 ei-muunnosta. Pienillä numeroilla pidä tulosta vain suuntaa-antavana.
- Luottamus ≠ todennäköisyys, että B on parempi. 95 % luottamustaso tarkoittaa, että menetelmä on enintään 5 % ajasta väärä pitkällä aikavälillä — se on frekventisti lausunto, ei "95 % mahdollisuus, että B voittaa."
- Yksi testi, yksi mittari. Monien mittareiden tai varianttien testaaminen kerralla moninkertaistaa sattumien mahdollisuuden; korjaa se (esim. Bonferroni) tai esirekisteröi yksittäinen mittari, joka päättää testin.
Laske se itse
Jokainen katkelmä laskee kaksipuolisen p-arvon yllä olevalle työskennellylle esimerkille.
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
3Usein kysytyt kysymykset
Mikä otoskoko minulle sopii? Tarpeeksi, jotta merkittävä nosto olisi havaittavissa. Pohjana tavoittele vähintään muutamaa sataa muunnosta per variantti; käytä erityistä otoskoko-laskinta perusarvolla ja pienimmällä nostolla, joka kannattaa havaita.
Kaksipuolinen vai yksipuolinen? Käytä kaksipuolista, elleivät sinulla ole vahvaa, ennalta määritettyä syytä välittää vain parantumisesta. Yksipuolinen kaksinkertaistaa herkkyytesi, mutta kieltää reagoimisen siihen, että B on huonompi.
Miksi laskin näyttää luottamusvälin? Se näyttää todellisen eron uskottavan vaihteluväliin, ei vain kyllä/ei -tuomio. Leveä väli, joka ratsastaa 0:n yli, tarkoittaa "ei vielä tarpeeksi tietoja".
Onko tämä bayesilainen? Ei — se on frekventisti z-testi, eniten opetettu ja käytetty menetelmä. Bayesilainen A/B-työkalut raportoivat "todennäköisyys, että B voittaa A:n" p-arvon sijaan; molemmat ovat kelvollisia, ne vastaavat hieman eri kysymyksiin.