analyticsintermediate45 minPython 3

Rigorous A/B Experimentation Readout & Significance

Perform two-sample hypothesis testing in Python evaluating conversion rates, relative uplift, z-scores, and p-values.

The incident

Company
QuickConvert Labs
Engineer
Arvind Raman, Staff Experimentation Scientist
Trigger
Product managers declared victory on a landing page redesign based on a 0.3 percent conversion lift with underpowered sample size.
Stakes
Shipping an untested redesign risked losing 15 lakh rupees in checkout conversions due to false-positive significance.

On a bright Tuesday morning, Arvind Raman was invited to a product demonstration at QuickConvert Labs, an e-commerce conversion optimization platform serving high-growth direct-to-consumer merchants. A frontend growth squad lead proudly announced that their redesigned checkout button increased conversion rates from 3.2 percent to 3.5 percent over a three-day weekend test and demanded immediate rollout to 100 percent of production traffic. Marketing leads were enthusiastic, preparing to deploy the new interface across all active landing pages before lunchtime to capitalize on what they assumed was immediate customer lift.

Arvind requested the sample sizes behind the celebration. To his dismay, the variant arm had only collected 400 visitors before the team declared victory and initiated release approvals. The squad lead was committing the classic statistical sin of peeking: stopping an experiment early the moment the metric looks positive without evaluating statistical power or minimum sample size thresholds. In small samples, binomial random noise routinely creates transient conversion spikes that evaporate once traffic scales, leading teams into deceptive confirmation bias and premature rollouts.

Arvind explained to the engineering and marketing squads that random variance alone frequently produces temporary positive spikes in short testing windows. Rolling out the change without rigorous hypothesis testing risked shipping an inferior checkout experience that could cost fifteen lakh rupees in lost annual sales across peak checkout funnels. Commercial decisions required mathematical evidence that observed improvements exceeded random chance beyond the standard ninety-five percent confidence threshold, requiring a formal two-sample proportion hypothesis test before any code changes touched production.

Arvind authored a standardized Python readout function designed for automated experiment evaluation across all digital properties. Taking visitor counts and conversions for both Control (A) and Variant (B), the script computed conversion rates, relative percentage uplift, pooled standard error, two-tailed z-score, and p-value using the standard normal distribution error function math.erf. The function enforced strict statistical power requirements before declaring any experiment significant, preventing false positives caused by premature test termination.

When run on a properly powered sample of 10,000 visitors per arm (320 conversions in Control vs 410 conversions in Variant), the z-score reached 3.394 with p = 0.0007. The readout empirically proved genuine statistical significance with a 28.12 percent relative conversion uplift, clearing the feature for full production deployment without financial risk and establishing that customer checkout conversion improved reliably across the entire shopping audience.

Arvind presented the experimentation readout framework to the Vice President of Product and the Engineering Director during the weekly product governance review. The statistical gate prevented premature feature deployments and was mandated as the formal launch gate for all frontend feature experiments across QuickConvert, protecting customer conversion rates across millions of monthly shopping sessions and instilling rigorous scientific standards into product engineering culture.

What you will learn

  • Calculate baseline conversion rates and relative percentage uplift across test variants
  • Compute pooled conversion probabilities and two-sample standard errors
  • Derive two-tailed z-scores and p-values using math.erf
  • Make evidence-based rollout decisions using 95% confidence thresholds (p < 0.05)

What you need first

Hypothesis Testing for ProportionsRates not counts

Two-sample z-tests compare two independent proportions to evaluate whether observed differences represent true uplift or random noise.

PythonMini example
import math
cr_a, cr_b = 0.032, 0.041
p = (320 + 410) / 20000
se = math.sqrt(p * (1 - p) * (2 / 10000))
print(round((cr_b - cr_a) / se, 3))
3.394
Watch out

Evaluating raw difference without standard error normalization, mistaking noise for genuine statistical effect.

Two-Tailed p-Values with math.erfDistribution shapes

The cumulative normal distribution error function math.erf computes the probability of observing test statistics under the null hypothesis.

PythonMini example
import math
z = 3.394
p = 2 * (1 - 0.5 * (1 + math.erf(abs(z) / math.sqrt(2))))
print(round(p, 4))
0.0007
Watch out

Omitting the factor of 2, which accidentally evaluates a one-tailed test instead of a rigorous two-tailed significance check.

Architecture and data flow

Early Peeking vs Statistical Power
Early Peeking vs Statistical Power. Stopping tests early without sample size calculations causes false-positive errors and premature feature rollouts.
Two-Sample z-Test Flow
Two-Sample z-Test Flow. Visitor counts feed into pooled probability, standard error calculation, z-score derivation, and p-value evaluation.
Structured Experiment Readout Dictionary
Structured Experiment Readout Dictionary. The output dictionary reports baseline rates, percentage uplift, z-score, p-value, and the boolean significance flag.

The data

A/B testing visitor and conversion counts across Control (A) and Variant (B) experimental cohorts.

ab_experiment_summary: Aggregated experiment parameters

cohortvisitorsconversionsobserved_cr
Control (A)100003203.20%
Variant (B)100004104.10%

Step by step

Read each step, run its code, then check your output against the one shown.

  1. Define experimental cohorts and calculate conversion rates

    We initialize the experiment parameters for Control cohort A (10,000 visitors, 320 conversions) and Variant cohort B (10,000 visitors, 410 conversions). We calculate the individual conversion rates: cr_a = 0.032 (3.2 percent) and cr_b = 0.041 (4.1 percent). Establishing baseline rates is the first step in conversion analytics, ensuring that both test cohorts are normalized by incoming traffic volumes before evaluating performance differences, avoiding raw count comparisons that mislead when traffic distributions are uneven between experimental groups across test runs.

    PythonStep 1
    def calc_rates(visitors_a, conv_a, visitors_b, conv_b):
        cr_a = conv_a / visitors_a
        cr_b = conv_b / visitors_b
        return cr_a, cr_b
    
    cr_a, cr_b = calc_rates(10000, 320, 10000, 410)
    print(f"Control CR: {cr_a * 100:.2f}%, Variant CR: {cr_b * 100:.2f}%")
    Control CR: 3.20%, Variant CR: 4.10%
    Common mistake

    Confusing total visitor impressions with unique visitors, which artificially deflates observed conversion rates.

    How you will know

    Output shows Control CR: 3.20%, Variant CR: 4.10%.

  2. Demonstrate false-positive trap with unnormalized percentage difference

    Wrong turn first: this step shows the mistake before the fix

    Here is the naive approach product managers often try first: taking the absolute percentage point difference (0.9 percentage points) and declaring victory without statistical power testing. In small samples, an absolute lift of 0.9 points can happen purely by chance over 30 percent of the time. Rolling out features based on unverified percentage deltas leads to false-positive regressions in production. We demonstrate this simplistic subtraction to show why variance normalization is mathematically required, illustrating how easily raw arithmetic differences mislead non-technical decision makers.

    PythonStep 2
    visitors_a, conv_a = 10000, 320
    visitors_b, conv_b = 10000, 410
    cr_a = conv_a / visitors_a
    cr_b = conv_b / visitors_b
    
    # Naive approach: simple delta without standard error
    abs_diff = (cr_b - cr_a) * 100.0
    print(f"Naive absolute delta: {abs_diff:.2f} percentage points (no significance check)")
    Naive absolute delta: 0.90 percentage points (no significance check)
    Common mistake

    Confusing absolute percentage point change (0.9%) with relative percentage uplift (28.1%), confusing stakeholders.

    How you will know

    Observe that naive delta reports 0.90 percentage points without proving whether the change is statistically significant.

  3. Compute relative percentage uplift between test variants

    We compute relative percentage uplift by evaluating the proportional increase of Variant B over Control baseline A: (cr_b - cr_a) / cr_a * 100.0. While the absolute difference is 0.9 percentage points, the relative improvement in customer conversion velocity is 28.125 percent. Relative uplift quantifies the commercial impact on business revenues, demonstrating that the redesigned variant generates nearly 28 percent more orders per thousand visitors, translating directly into incremental merchant gross merchandise value and higher operational throughput across the digital catalog.

    PythonStep 3
    def calc_uplift(cr_a, cr_b):
        return (cr_b - cr_a) / cr_a * 100.0
    
    cr_a, cr_b = 320 / 10000, 410 / 10000
    uplift = calc_uplift(cr_a, cr_b)
    print(f"Relative Uplift: {uplift:.2f}%")
    Relative Uplift: 28.12%
    Common mistake

    Dividing by cr_b instead of cr_a, which distorts the baseline denominator when calculating relative lift.

    How you will know

    Output prints Relative Uplift: 28.12%.

  4. Calculate pooled conversion probability and two-sample standard error

    We evaluate the test statistic under the null hypothesis that Control and Variant share an identical true conversion rate. We calculate the pooled probability p_pool = (conv_a + conv_b) / (visitors_a + visitors_b) across all 20,000 visitors (0.0365). We then compute the two-sample standard error se = sqrt(p_pool * (1 - p_pool) * (1/visitors_a + 1/visitors_b)). Dividing the observed conversion difference by standard error yields a z-score of 3.394, proving that the observed lift lies well into the distribution tail beyond random variation.

    PythonStep 4
    import math
    
    def calc_z(visitors_a, conv_a, visitors_b, conv_b):
        cr_a = conv_a / visitors_a
        cr_b = conv_b / visitors_b
        p_pool = (conv_a + conv_b) / (visitors_a + visitors_b)
        se = math.sqrt(p_pool * (1 - p_pool) * (1 / visitors_a + 1 / visitors_b))
        z = (cr_b - cr_a) / se
        return p_pool, se, z
    
    p_pool, se, z = calc_z(10000, 320, 10000, 410)
    print(f"Pooled p: {p_pool:.4f}, Standard Error: {se:.6f}, z-score: {z:.3f}")
    Pooled p: 0.0365, Standard Error: 0.002652, z-score: 3.394
    Common mistake

    Using separate unpooled sample standard errors, which violates null hypothesis assumptions in two-proportion testing.

    How you will know

    Output shows Pooled p: 0.0365, Standard Error: 0.002652, z-score: 3.394.

  5. Derive two-tailed p-value and evaluate 95% confidence significance

    In the final step, we evaluate the two-tailed p-value using Python's standard library math.erf. The cumulative normal probability is derived with 2 * (1 - 0.5 * (1 + math.erf(abs(z) / sqrt(2)))). For z = 3.394, the p-value equals 0.0007, which is far below the standard alpha threshold of 0.05. We package the full analysis into a structured dictionary reporting baseline conversion rates, uplift percentage, z-score, p-value, and the boolean significance flag (True).

    PythonStep 5
    import math
    
    def analyze_ab_test(visitors_a, conv_a, visitors_b, conv_b):
        cr_a = conv_a / visitors_a
        cr_b = conv_b / visitors_b
        uplift = (cr_b - cr_a) / cr_a * 100.0
        p_pool = (conv_a + conv_b) / (visitors_a + visitors_b)
        se = math.sqrt(p_pool * (1 - p_pool) * (1 / visitors_a + 1 / visitors_b))
        z = (cr_b - cr_a) / se
        p_val = 2 * (1 - 0.5 * (1 + math.erf(abs(z) / math.sqrt(2))))
        sig = z > 1.96 and p_val < 0.05
        return {
            "cr_a": round(cr_a * 100, 2),
            "cr_b": round(cr_b * 100, 2),
            "uplift_pct": round(uplift, 2),
            "z_score": round(z, 3),
            "p_value": round(p_val, 4),
            "significant": sig
        }
    
    print(analyze_ab_test(10000, 320, 10000, 410))
    {'cr_a': 3.2, 'cr_b': 4.1, 'uplift_pct': 28.12, 'z_score': 3.394, 'p_value': 0.0007, 'significant': True}
    Common mistake

    Deploying features when p < 0.05 but z-score is negative, accidentally rolling out statistically significant regressions.

    How you will know

    Output prints dictionary with uplift_pct 28.12, z_score 3.394, p_value 0.0007, and significant True.

Build it yourself

Write your solution below, run it in your browser, and pass every check.

Python 3Ready to run
Loading editor…
Press Run Code to evaluate
Hypothesis testing script executes cleanly
Computes normal cumulative distribution using math.erf
Output contains verified z-score 3.394
Output contains relative uplift 28.12%
Experiment flags significant as True

What the result means

Interpretation

Two-sample hypothesis testing prevents costly false-positive feature rollouts. By normalizing conversion differences by pooled standard error, the test proves that a 28.12 percent relative uplift across 10,000 visitors has less than a 0.07 percent probability of being random noise.

The message you would send

A/B experimentation readout complete: the checkout redesign achieved a verified 28.12 percent relative conversion uplift (3.20% Control vs 4.10% Variant) across 20,000 visitors. The test reached a z-score of 3.394 with p = 0.0007, meeting our 95 percent confidence threshold and clearing the feature for full production deployment.

Put it on your CV

Resume or LinkedIn bullet

OutputCopy this line
Authored statistical experimentation frameworks in Python conducting two-sample z-tests and power analysis to validate feature rollouts.

README for GitHub

OutputREADME.md
# Rigorous A/B Experimentation Readout & Significance in Python

## Problem
Product squads declared victory on an unpowered 400-user sample, risking 15 lakh rupees in lost sales from false-positive regressions.

## Solution Architecture
- Pure Python implementation using standard `math.erf`.
- Calculated pooled probability and two-sample standard error.
- Computed two-tailed z-score and p-value.
- Enforced `z > 1.96` and `p < 0.05` significance threshold.

## Results
Validated genuine 28.12% conversion uplift with p = 0.0007 across 20,000 visitors.
Stretch goal

Extend the function to calculate the minimum sample size required per arm prior to launching the test.

Keep going