#401Sample Size for Statistical PowerMedium

Sample Size for Statistical Power

Background

Every A/B test starts with one question: how big does each arm need to be? Underpowered experiments routinely come back with "+0.3% lift, not significant" and get shipped on vibes; rigorous teams compute the required sample size up front. The standard formula closes the loop between three knobs: the noise in the metric (σ\sigma), the minimum detectable effect (δ\delta), and the significance / power trade-off (α\alpha, β\beta).

Problem statement

Implement sample_size_per_arm(sigma, mde, z_alpha2=1.96, z_beta=0.8416) returning the per-arm sample size needed to detect a difference in means with the given parameters:

n  =  (zα/2+zβ)22σ2δ2n \;=\; \frac{(z_{\alpha/2} + z_{\beta})^{2}\, \cdot\, 2\sigma^{2}}{\delta^{2}}

Defaults correspond to α=0.05\alpha = 0.05 two-sided (z0.0251.96z_{0.025} \approx 1.96) and β=0.20\beta = 0.20 i.e. 80% power (z0.200.8416z_{0.20} \approx 0.8416). Round up to the next integer (you cannot run fractional users).

Input

  • sigma - float > 0, standard deviation of the primary metric per user.
  • mde - float > 0, the minimum detectable effect (absolute difference in means you want to be able to call).
  • z_alpha2 - float, critical value for the two-sided significance level. Default 1.96 (alpha = 0.05).
  • z_beta - float, critical value for the power. Default 0.8416 (power = 0.80).

Output

  • int >= 1 - the required per-arm sample size, rounded up.

Examples

Example 1 - the canonical "watch time" calculation

Input:  sigma = 30.0, mde = 0.3   (a 1% lift on 30 min mean)
Output: 156980

Explanation: (1.96+0.8416)27.849(1.96 + 0.8416)^2 \approx 7.849. n=7.8492900/0.09 156979.2n = 7.849 \cdot 2 \cdot 900 / 0.09 \approx ~156979.2, ceil to 156,980156{,}980.

Example 2 - tightening the MDE quadruples n

Input:  sigma = 30.0, mde = 0.15
Output: 627918       # ~ 4 * 156980

Explanation: n1/δ2n \propto 1/\delta^{2}; halving MDE multiplies nn by 44.

Example 3 - higher power (90%) needs more samples

Input:  sigma = 30.0, mde = 0.3, z_alpha2 = 1.96, z_beta = 1.2816
Output: 210160

Explanation: (1.96+1.2816)210.51(1.96 + 1.2816)^2 \approx 10.51, so n10.512900/0.09210,159.210,064n \approx 10.51 \cdot 2 \cdot 900 / 0.09 \approx 210{,}159. \to 210{,}064.

Constraints

  • Raise ValueError if sigma <= 0 or mde <= 0 (the formula is undefined).
  • Use math.ceil to round up; return a Python int.
  • The formula is for a difference-in-means test; for a difference-in-proportions test, see the variant note.

Notes

  • Rule of thumb. (zα/2+zβ)28(z_{\alpha/2} + z_{\beta})^{2} \approx 8 for the default α,β\alpha,\beta, giving the famous n16σ2/δ2n \approx 16\sigma^{2}/\delta^{2}. The exact value 7.8497.849 is what the formula uses; the rule-of-thumb 88 is for whiteboard arithmetic.
  • Bound MDE before you optimise sample size. The smallest worthwhile MDE is bounded by what the product cares about, not by what the experiment can detect. Computing nn for an MDE smaller than the product cares about wastes capacity.
  • CUPED. Pre-experiment variance reduction shrinks σ2\sigma^{2} by ρ2\rho^{2}, where ρ\rho is the correlation between the pre-experiment baseline and the experiment metric. With ρ=0.7\rho = 0.7, CUPED roughly halves the required nn.
Python
Loading...

▶ Run executes the 3 visible sample tests below in your browser. Submit runs the full suite — including hidden tests — on the server for an official verdict.

  • Reference example: canonical watch-time n
  • Sample: tightening the MDE quadruples n
  • Example: 90% power needs more than 80%