I am working on an AB tool in JavaScript using the following:
Calculate the Conversion rate based on the conversions divided by the hits.
Calculate the Z Score by passing in the control and treatment objects.
Determine the confidence of the Z Score with Cumulative Normal Distribution.
How accurate is this?
/** Example input: var a = { 'hits' : 15, 'conversions' : 2 }; var b = { 'hits' : 15, 'conversions' : 6 }; */ /** * Calculation of conversion rate * @param t * @returns {number} */ function cr(t) { return t.conversions / t.hits; } /** * Calculation of score * @param c * @param t */ function calcZScore(c, t) { var z = cr(t) - cr(c); var s = (cr(t) * (1 - cr(t))) / t.hits + (cr(c) * (1 - cr(c))) / c.hits; return Math.sqrt(s); } /** * Calculate the Cumulative Normal Distribtion * * @param x * @returns {number} */ function cumNorDist(x) { var b1 = 0.319381530; var b2 = -0.356563782; var b3 = 1.781477937; var b4 = -1.821255978; var b5 = 1.330274429; var p = 0.2316419; var c = 0.39894228; if (x >= 0.0) { t = 1.0 / ( 1.0 + p * x ); return (1.0 - c * Math.exp(-x * x / 2.0) * t * ( t * ( t * ( t * ( t * b5 + b4 ) + b3 ) + b2 ) + b1 )); } else { t = 1.0 / ( 1.0 - p * x ); return ( c * Math.exp(-x * x / 2.0) * t * ( t * ( t * ( t * ( t * b5 + b4 ) + b3 ) + b2 ) + b1 )); } } /** * Given a conversion rate, calculate a recommended sample * size * E.g: 0.25 worst, 0.15, 0.05 best at a 95% confidence * @param conv * @returns {Array} */ function sampleSize(conv) { var a = 3.84145882689; var res = []; var bs = [0.0625, 0.0225, 0.0025]; var len = bs.length; for (var i = 0; i < len; i++) { res.push(((1 - conv) * a / (bs[i] * conv))); } return res; } /** * Calculate the significance between Control and Treatment [A/B/C] * * @param controlObj * @param treatmentObj */ function calculateSig(controlObj, treatmentObj) { if (typeof controlObj !== 'object') { console.log(outputErrorFor('Control Object')); } if (typeof controlObj !== 'object') { console.log(outputErrorFor('Control Object')); } var zScore; var confidence; var confidencePercentage; var cRatio; var cNumHits = controlObj.hits; var cNumConver = controlObj.conversions; var tNumHits = treatmentObj.hits; var tNumConver = treatmentObj.conversions; var cConversionRate = (cNumConver / cNumHits) * 100; var tConversionRate = (tNumConver / tNumHits) * 100; cConversionRate += '%'; tConversionRate += '%'; zScore = calcZScore(controlObj, treatmentObj); confidence = cumNorDist(zScore); confidencePercentage = confidence * 100; console.log("Control Hits is:", cNumHits); console.log("Control Conversions is:", cNumConver); console.log("Treatment Hits is:", tNumHits); console.log("Treatment Conversions is:", tNumConver); console.log("Results are in:"); console.log("Control Conversion Rate is:", cConversionRate); console.log("Treatment Conversion Rate is:", tConversionRate); console.log("Z Score is:", zScore); console.log("Confidence is:", confidence); console.log("Confident percentage is:", confidencePercentage); } /** * * @param name * @returns {string} */ var outputErrorFor = function (name) { return 'The passed in parameter must be an object and contain two properties: "hits" and "conversions" for: ' + name; }; This is a ported version of this repo. (credit to the author)
Side note: With the Z Score, how can I find the P Value?
cumNorDist()formula looks familiar. The error bounds and suitable domain are given in Abramowitz and Stegun Eq. 26.2.17. \$\endgroup\$