Experimentell design, del 4
I detta inlägg går jag igenom hur ett Paired T-test är användbart. Såväl detta test tar avstamp i nollhypotesen och är användbart när man vill jämföra två medelvärden som är beroende av varandra (paired). Till skillnad från A/B-testet där två urvalsgrupper fått varsin variant av exempelvis en webbsida, får här istället hela användargruppen ta del av båda varianterna. Vanligtvis bibehålls gruppindelningen oavsett; för att låta grupp 1 utföra en given uppgift (use case) först på variant A och sedan B, medan grupp 2 istället börjar med Variant B.
Testet går även att sätta upp på så vis att det bara finns en variant (A) som varje deltagare får utföra en eller ett par uppgifter i, för att sedan göra om precis samma test igen, en tid senare. Istället för att jämföra genomförandegrad och tidsåtgången mellan A och B, mäts nu istället förändringen (förhoppningsvis förbättringen) över tid. Det är differensen mellan dessa två medelvärden som är vårt egentliga mätvärde i detta test.
Testets ändamål är att säkerställa om differensen är signifikant större än noll, eller inte. Varför inte bara titta på de två medelvärdena och jämföra dem? Det kan du, men nyckeln här är att säkerställa att det finns en skillnad även i populationen. Har du läst mina tidigare inlägg om Permutations- och A/B-testet är du redan bekant med detta koncept, men i korthet vill vi inte att resultatet av vårt test ska kunna härledas till slumpen. Med signifikant skillnad menas att urvalsgruppens resultat är representativt för hela populationen, vilket skulle kunna vara alla anställda administratörer på ett företag, eller alla administratörer ifrån olika företag som använder samma produkt, exempelvis ett ekonomisystem.
T-testet förutsätter att värdet som skall jämföras följer samma skala, så kallande kvantitativa kontinuerliga variabler, vilka har samma ordning och jämnstora skalsteg. Ett exempel som uppfyller detta kriterium är sekunder. Jämför du en variabel som kan ha värdet 0–5 med en som kan ha värdet 0–50 är det heller inte så konstigt ifall de skulle ha olika medelvärden, eller hur? Den andra premissen för testet är att värdet som prövas måste vara normalfördelat. Att jämföra två gruppers medelvärden innebär egentligen att man jämför distributionerna. En normalfördelad variabel antar ofta värden som ligger nära medelvärdet och mycket sällan värden som har en stor avvikelse.
Skulle vi exempelvis ta tid på hur lång tid det tog för 20 ekonomiadministratörer att slutföra en uppgift i ett nytt ekonomisystem, för sedan göra om samma uppgift i system 2, hade vi kunna ställa upp vårt data i en tabell enligt nedan:
I tabellens första kolumn ser vi tiderna för de 20 administratörerna i system 1 och på slutraden ett medelvärde (en genomsnittstid) för detta, vilket vi kan räkna ut såhär:
Medelvärde
const deviations = a.map((val, i) => b[i] - val)
const n = a.length
const m = deviations.reduce((a, b) => a + b, 0) / n
I nästa steg jämför vi differensen mellan den gamla och den nya tiden med detta medelvärde och till sist upphöjs denna differens till 2. Längst ned i sista kolumnen summerar vi alla värden i den, vilket ger oss den så kallade kvadratsumman (sum of squares). Detta är ett viktigt nyckelvärde inom statistiken. I Excel kan vi göra detta utan några större bekymmer, men vi kan även göra det i vår kod enligt följande:
Kvadratsumman
const ss = deviations.reduce((acc, curr) => {
const ds = curr - m
const ds2 = Math.pow(ds, 2)
return acc + ds2
}, 0)
För att kunna beräkna vår t-value (vårt mål) deklarerar vi en frihetsgrad, antalet värden i den slutliga beräkningen av en statistik som är fria att variera. Vi sätter den till n - 1 för detta.
Frihetsgrad
const df = n - 1
Uträkningen av vår t-value följer:
T-value
const s2 = ss / df
const s2m = s2 / n
const sm = Math.sqrt(s2m)
const tValue = Math.abs(m / sm)
Nu har vi allt vi behöver för att slutligen beräkna vår teststatistik. Jag har medvetet utelämnat funktionen ibeta i koden nedan. På min GitHub finns hela källkoden att ta del av och med anledning av att koden emellanåt omarbetas hänvisar jag dig istället dit för denna.
P-value och Statistisk signifikans
const df2 = df / 2
const statisticalSignificance = ibeta(
(tValue + Math.sqrt(tValue * tValue + df)) / (2 * Math.sqrt(tValue * tValue + df)),
df2,
df2
)
För läsbarhetens skull kan vi returnera vår teststatistik som en formaterad sträng:
Resultat
return `${(100 * statisticalSignificance).toFixed(2)}%`
Bonus: Unpaired T-test
För dig som tittar vidare i koden hittar du där även ett exempel på ett Unpaired T-test. Jag har personligen inte haft användning av detta test lika ofta, men det har funnits tillfällen. Ett Unpaired T-test är lite mer likt ett A/B-test på det vis att två separata urvalsgrupper får varsin variant av det som nu testas. Ett exempel på ett bra användningstillfälle är om du har utvecklat en ny fullversion av något, där förändringarna är stora. Det skulle exempelvis kunna vara ett checkout-flöde som designats om i sin helhet. Eftersom det är flera förändringar kan vi med hjälp av ett A/B-test kanske inte härleda vilken av ändringarna som föreligger en förbättring (eller försämring) av försäljningen. Det ända vi egentligen kan testa är om den är bättre eller sämre en variant A, samt om resultatet är signifikant. Skillnaden mot ett paired T-test är alltså att användarna inte är desamma.