Contents
, where and
Deep Breakdown
What it tells me (Situation Awareness): The Binomial distribution counts how many successes I get in independent Bernoulli trials that all share the same success probability . It answers questions like "if 10% of visitors convert, how likely is it that exactly 2 of the next 20 convert?" or "how surprising is it to see 5 or more?". Its mean is and its variance is , so the spread of a count grows with while the spread of the rate shrinks with , which is the whole reason bigger samples give tighter conversion-rate estimates. The two assumptions that matter in practice are independence (one user's click does not change another's) and a constant (no mix of very different segments). In DA/DS: conversion and click counts in A/B tests, number of churners in a cohort, defect counts in a batch of size , exact binomial tests and Clopper-Pearson intervals for small samples, the Normal approximation behind the two-proportion z-test, and logistic regression on aggregated data (successes out of trials per row).
Flowchart
Real-world use
Where it is used
A/B testing (conversions out of visitors per variant), email marketing (opens out of sends), churn analysis (churners out of a cohort), quality control (defective units out of a batch), LLM evaluation (correct answers out of benchmark questions), and any "successes out of trials" count.
Problem it solves
Gives the exact probability of every possible count of successes, so I can tell whether an observed count is ordinary noise or a real signal, and put error bars on rates computed from a finite sample.
How it solves the problem
Every specific sequence with successes and failures has probability (independence lets me multiply). There are such sequences, and they are mutually exclusive, so I add them up: . Tail sums of this PMF give p-values; inverting them gives confidence intervals.
Example in a business
A landing page historically converts at . A new campaign sends 20 visitors and 5 convert. Is that unusual? , so under the old rate this would happen only about 4% of the time: a hint that the campaign converts better, worth a bigger test before drawing conclusions. The most likely outcome under the old rate is 2 conversions, with .
The formula, piece by piece
- : the random count of successes in the trials.
- : number of trials (fixed in advance, e.g. visitors, emails sent).
- : the specific count I am asking about, between and .
- : success probability of each single trial (the Bernoulli parameter).
- : failure probability of each trial.
- : number of ways to choose which of the trials are the successes.
- : probability that the chosen trials all succeed.
- : probability that the remaining trials all fail.
- Mean , variance , both from summing independent Bernoulli means and variances.
The formula in words
"The probability of exactly successes is the number of ways to place them among the trials, times the probability of any one such arrangement." Tiny example: 5 emails, open rate 20%. .
Critical Concepts
- Bernoulli is the atom: , and a Binomial is the sum of independent Bernoullis.
- Independence matters: clustered data (several sessions from the same user, emails to the same household) breaks independence and makes the real variance larger than (overdispersion). Beta-Binomial or cluster-robust methods fix this.
- Constant p matters: mixing segments with different conversion rates also inflates variance; stratify first.
- Normal approximation: for large with and , . This is the basis of the two-proportion z-test. For small counts use exact binomial tests.
- Poisson limit: with large and small (rare events), .
- MLE: , with standard error .
Step-by-step calculation (tiny example)
Step 1: Set up emails, open rate, want .
Step 2: Count the arrangements .
Step 3: Probability of one arrangement .
Step 4: Multiply .
Step 5: Full distribution and check , summing to . Mean , variance .
Code examples
Beginner example (Python)
from scipy.stats import binom
n, p = 5, 0.2
dist = binom(n, p)
print(f"P(X=2) = {dist.pmf(2):.4f}") # 0.2048
for k in range(n + 1):
print(f"P(X={k}) = {dist.pmf(k):.5f}")
print(f"Sum = {dist.pmf(range(n + 1)).sum():.4f}") # 1.0000
print(f"E[X] = {dist.mean():.2f} | Var[X] = {dist.var():.2f}") # 1.00 | 0.80Beginner example (R)
n <- 5; p <- 0.2
cat(sprintf("P(X=2) = %.4f\n", dbinom(2, size = n, prob = p))) # 0.2048
print(round(dbinom(0:n, size = n, prob = p), 5))
cat(sprintf("P(X >= 5 | n=20, p=0.1) = %.4f\n", 1 - pbinom(4, size = 20, prob = 0.1))) # 0.0432Professional example (Python)
import numpy as np
from scipy.stats import binom, binomtest
def campaign_conversion_check(
conversions: int, visitors: int, baseline_rate: float
) -> dict[str, float]:
"""Exact one-sided binomial test of a campaign against a baseline conversion rate.
Args:
conversions: Observed successes.
visitors: Number of trials.
baseline_rate: Historical conversion rate p0.
Returns:
Dict with observed rate, expected count, exact p-value and a 95% CI.
"""
test = binomtest(conversions, visitors, baseline_rate, alternative="greater")
ci = test.proportion_ci(confidence_level=0.95, method="exact")
return {
"observed_rate": conversions / visitors,
"expected_conversions": visitors * baseline_rate,
"p_value_exact": test.pvalue,
"ci_low": ci.low,
"ci_high": ci.high,
}
result = campaign_conversion_check(conversions=5, visitors=20, baseline_rate=0.10)
for key, val in result.items():
print(f"{key}: {val:.4f}")
# p_value_exact = P(X >= 5 | n=20, p=0.1) = 0.0432
print(f"Check with survival function: {binom.sf(4, 20, 0.10):.4f}")
# Simulation sanity check: rate of 5+ conversions under the baseline
rng = np.random.default_rng(42)
sims = rng.binomial(n=20, p=0.10, size=200_000)
print(f"Simulated P(X >= 5): {(sims >= 5).mean():.4f}") # close to 0.0432Source: domain knowledge