
有人问我一次投放跑了 2000 次曝光转化了 68 次上一次是 62 次这算涨了吗我一般不会直接回答而是先让他把二项分布算一遍。这个问题看着像运营问题骨架其实是概率分布问题n 次独立重复试验每次只有两种结果成功概率固定我们关心的是成功出现的总次数。把二项分布吃透之后你会发现 A/B 实验的显著性、抽样质检的合格判定、点击率波动到底是不是噪声全都落在这一个分布上。它不难难的是很多人只记住了公式长什么样却没搞清楚四个前提条件哪一个最容易在实际数据里被破坏。这篇文章我打算按概念边界、公式拆解、代码实现、期望方差、近似选择、真实翻车场景、业务落地这条线讲一遍从手算到写代码都覆盖刚入门统计的能看懂做了几年数据分析但没系统梳理过的也能拿走点东西。1. 二项分布描述的到底是什么四个前提缺一不可1.1 从成功次数这个统计量说起二项分布的随机变量 X定义非常明确在 n 次独立重复试验中事件发生的总次数。注意它在统计的是次数不是比例也不是第几次成功。这一点很多人一开始会混淆。比如投 10 次硬币正面出现几次这个几次就是二项分布的取值而第几次才出现正面是几何分布的范畴单位时间内事件发生几次是泊松分布的范畴。分清谁统计什么是选对分布的第一步。再看一个业务场景给 5000 个用户推送一条消息其中有多少人点击。这里 n 是 5000成功定义为点击X 是点击人数取值范围 0 到 5000。这就是标准的二项分布建模。你不需要知道是哪 5000 个人点的也不需要关心点击发生的顺序只要总次数符合四个前提就能用二项分布去描述它的概率规律。我见过不少人在这一步就走偏了把平均点击数和点击人数当成一回事。前者是期望后者是随机变量二者关系是 E[X] n·p。把随机变量和它的数字特征混为一谈后面算方差、算显著性的时候就会一路错下去。1.2 四个前提条件以及实际数据里最容易被破坏的那两个二项分布成立需要同时满足四条试验次数 n 固定每次试验只有两种互斥结果每次试验相互独立每次成功的概率 p 保持恒定。这四条里前两条通常没问题真正会出事的往往是后两条。独立性被破坏的典型案例是不放回抽样。从 100 件产品里抽 10 件检查抽走一件之后剩下的总体就变了下一次抽到的概率和上一次不独立严格来说这属于超几何分布。只有当总体远大于样本一般总体量是样本量 20 倍以上时才可以近似当作二项分布处理。所以抽检这个词底下藏着两种完全不同的模型选错就会把不合格率估偏。p 恒定被破坏往往是隐蔽的。比如你把不同渠道的流量合并统计A 渠道转化率 5%B 渠道 2%混在一起之后每个用户成功的概率并不相同这时候用一个统一的 p 去套二项分布算出来的方差会偏小置信区间会偏窄判断就容易过于乐观。后面第 6 节我会专门讲这种过度分散怎么发现、怎么处理。我个人的习惯是拿到一批计数数据准备套二项分布之前先问三个问题——n 是不是事先定死的试验之间有没有相互影响p 有没有随时间或分组漂移三个问题都过了再动公式。2. 公式拆开看组合数、p 的幂、1-p 的幂各管一段2.1 C(n,k) 是路径数不是概率二项分布的概率质量函数写出来是P(X k) C(n, k) · p^k · (1-p)^(n-k)很多人背下来了但没想过这三块分别代表什么。我用一个具体路径来解释。假设 n 5我们要算恰好 2 次成功的概率。先盯住其中一种特定的结果排列比如成功、成功、失败、失败、失败。这一条路径的概率是 p·p·(1-p)·(1-p)·(1-p)也就是 p²(1-p)³。任何一条恰好 2 次成功的路径概率都是这个数因为乘法可交换。但恰好 2 次成功不止这一条路径。成功、失败、成功、失败、失败也是 2 次成功失败、失败、成功、成功、失败同样是。一共有多少条这样的路径从 5 个位置里挑 2 个放成功就是 C(5,2) 10 条。所以 P(X2) 10 · p²(1-p)³。这三块的分工就很清楚了p^k(1-p)^(n-k) 是任何一条特定路径的概率C(n,k) 是这种路径有多少条。理解这一点之后你就不会再犯忘记乘组合数的低级错误。2.2 手动算一遍 n10、p0.3 的完整分布光看公式还是虚手算一次印象最深。设 n 10p 0.3把 k 从 0 到 10 的概率都算出来。以 k 3 为例C(10,3) 120p³ 0.027(1-p)⁷ 0.7⁷ ≈ 0.0823543。三者相乘120 × 0.027 × 0.0823543 ≈ 0.2668。也就是说在这个设定下恰好出现 3 次成功的概率大约 26.7%。把整个分布列出来kP(X k)累计 P(X ≤ k)00.02820.028210.12110.149320.23350.382830.26680.649640.20010.849750.10290.952760.03680.989470.00900.998480.00140.999990.00014≈1.0000100.000006≈1.0000从表里能读出两个信息。第一概率最大的 k 是 3也就是期望 np 3 附近这不是巧合。第二k 落在 0 到 6 之间的累计概率已经接近 99%超过 6 的概率不到 1.1%。这种尾巴有多厚的判断在业务里就是这个异常值到底是不是噪声的判断依据。2.3 参数一动形状就变p 固定时n 越大分布越接近钟形越平滑n 很小的时候分布是歪的、离散感很强。p 0.5 时分布关于 n/2 对称p 0.5 时明显右偏长尾拖在右边p 0.5 时反过来左偏。n 10、p 0.05 的时候P(X0) 0.95¹⁰ ≈ 0.599一半以上的概率一次都不发生这种分布你拿正态去近似会错得很离谱。我做实验设计的时候会先画一眼形状判断能不能用正态工具去近似再决定后面用什么方法。这个习惯帮我避开了不少坑。3. 手算之后交给代码怎么算才不炸精度3.1 直接套公式会在哪儿翻车把公式直译成代码第一版大概是这样的from math import comb def binom_pmf(n, k, p): return comb(n, k) * p**k * (1 - p)**(n - k)小规模数据没问题n 10、p 0.3 跑得很准。但 n 一旦上千p**k和(1-p)**(n-k)里只要有一个很小就可能下溢成 0乘出来结果变成 0而真实概率其实是个很小的正数。反过来如果先用阶乘去算 C(n,k)n 上到 1000 以上1000! 是个天文数字浮点直接溢出成 inf算出来是 nan。我早期写的一个监控脚本就栽在这上面某次点击率极低的场景下n 5000、p 0.001公式算出来一堆 0我还以为代码逻辑错了查了半天才发现是浮点下溢。3.2 用对数空间算或者直接用成熟库稳妥的做法是在对数空间里计算。组合数用lgamma对数伽马函数把乘除变成加减from math import lgamma, exp, log def binom_pmf_log(n, k, p): if p 0: return 1.0 if k 0 else 0.0 if p 1: return 1.0 if k n else 0.0 log_c lgamma(n 1) - lgamma(k 1) - lgamma(n - k 1) return exp(log_c k * log(p) (n - k) * log(1 - p))这段代码把三项都压在对数域里只在最后一步取指数下溢问题基本解决。但说实话日常工作中我更推荐直接用scipy.stats它内部的实现更成熟还会有边界处理from scipy.stats import binom binom.pmf(3, 10, 0.3) # 0.2668279... binom.cdf(3, 10, 0.3) # P(X 3) 0.6496... binom.sf(2, 10, 0.3) # P(X 3) 0.6172...3.3 sf 和 1 - cdf 的区别别用错算右尾概率 P(X ≥ k) 的时候很多人写1 - binom.cdf(k-1, n, p)。小概率场景下这个写法会丢精度如果真实尾概率是 1e-12而 cdf 的值已经接近 1相减之后可能直接变成 0你以为概率为零实际上是被浮点吃掉了。正确姿势是用binom.sf(k-1, n, p)sf 是生存函数survival function它直接算右尾不经过1 减这一步。我在做异常检测阈值的时候特别在意这个因为异常事件本来就是小概率用 1-cdf 很容易把真实的异常直接抹成 0。提示凡是涉及极小的尾概率都优先用 sf而不是 1 减 cdf。4. 期望和方差一个告诉你中心在哪一个告诉你稳不稳4.1 期望 E[X] np 是怎么来的期望的直觉推导用指示变量最干净。给每次试验定义一个指示变量 Iᵢ第 i 次成功取 1失败取 0。显然 E[Iᵢ] p。而总成功次数 X I₁ I₂ ... Iₙ期望的线性性质直接给出 E[X] np不需要任何独立性假设。这个推导顺便说明了一件事期望的线性可加性不依赖独立性。所以哪怕各次试验的 p 不一样只要把每次的 pᵢ 加起来也能得到总的期望。这一点在混合渠道的场景里很有用。回到前面的例子n 2000、p 0.031期望点击数就是 62 次。实测 68 次比期望高 6 次。光看这 6 次感觉是涨了但到底是不是真涨还得看方差。4.2 方差 Var(X) np(1-p) 和它的标准差二项分布的方差是 np(1-p)标准差是它的平方根。还是拿 n 2000、p 0.031 算方差 2000 × 0.031 × 0.969 ≈ 60.08标准差 σ √60.08 ≈ 7.75也就是说在真实转化率就是 3.1% 的前提下实测次数会围绕 62 上下波动波动幅度大致是 ±7.75 次。实测 68 次距离期望 62 只有 6 次不到一个标准差。结论很清楚这点差异完全可能是随机波动不能说明转化率真的提升了。我见过太多团队在这种幅度上就宣布优化见效然后过了两周数据又滑回去反复折腾。问题不在实验方法而在没有先算一遍方差不知道自己的样本量能分辨多大的差异。4.3 用 σ 快速估一个正常波动范围有个粗略但好用的经验二项分布在 n 足够大时接近正态大约 95% 的观测会落在期望 ± 2σ 之间99.7% 落在 ± 3σ 之间。拿它做快速判断非常省事。场景npE[X]σ±2σ 区间消息点击20003.1%627.7546.5 ~ 77.5质检抽检5002%103.133.7 ~ 16.3小额转化10030%304.5820.8 ~ 39.2看到实测值落在区间里就先别急着下结论多半是噪声。落在区间外再考虑做正式检验。5. 什么时候能用正态近似、什么时候该用泊松近似5.1 正态近似的门槛不是n 大就行中心极限定理告诉我们 n 足够大时二项分布趋近正态但足够大不够精确。常用的经验门槛是 np ≥ 5 且 n(1-p) ≥ 5保守一点用 10。注意这里两个条件要同时满足很多人只看 np 大就以为可以忽略 p 接近 1 的情况。p 0.98、n 100 的时候np 98 看着很大但 n(1-p) 2分布严重左偏正态近似会出问题。用正态近似时要加连续性修正。因为二项分布是离散的正态是连续的算 P(X ≤ k) 的时候用 (k 0.5) 作为分界P(X ≤ k) ≈ Φ((k 0.5 - np) / √(np(1-p)))举个例子n 2000、p 0.031要算 P(X ≥ 68)。先转成 P(X 67)带修正用 67.5z (67.5 - 62) / 7.75 ≈ 0.71对应尾概率约 0.239。也就是说即使真实转化率没变也有将近 24% 的概率能看到 68 次以上。这个数字比看起来像涨了要诚实得多。5.2 泊松近似n 很大、p 很小时的简化当 n 很大、p 很小而乘积 λ np 适中时二项分布可以近似成参数为 λ 的泊松分布。经验条件是 n ≥ 20、p ≤ 0.05更严格一点用 n ≥ 100、p ≤ 0.01。比如 n 10000、p 0.0005λ 5用泊松算会舒服很多计算量小公式也简单P(X k) e^(-λ) λ^k / k!。那什么时候不该用λ 比较大比如超过 20的时候泊松和正态都行但泊松在尾部计算上反而更麻烦不如直接用正态或原始二项公式。我的一般原则是λ 小于 10 用泊松λ 大就用正态加修正拿不准就直接用原始公式或者 scipy反正现代算力不差这点。5.3 无放回场景超几何分布前面提过不放回抽样严格来说不是二项分布。从 N 件产品其中 D 件次品里抽 n 件次品数服从超几何分布。当 N 远大于 n 时一般 N ≥ 20n二项分布是很好的近似因为抽走几件对总体比例的影响可以忽略。我见过一个真实例子从 200 件库存里抽 20 件质检有人直接套二项分布算出来的不合格率置信区间比实际窄了将近 15%。总体量不大的时候这个差别会影响判定结果该用超几何就得用。6. 实际数据里最容易翻车的三种情况6.1 独立性被悄悄破坏最常见的独立性破坏是同一个用户被算了多次。比如统计有多少次会话产生了下单如果同一个用户在一次会话里反复刷新页面这些浏览行为之间是强相关的把它们当作独立的伯努利试验会让方差严重低估。表现出来就是你以为的置信区间很窄实际波动却比你预期大得多实验结论反复横跳。排查方法也简单先按用户维度去重看看统计口径是不是从次数变成了人数。如果业务上确实要统计次数那就要考虑用能处理相关性的模型比如带随机效应的模型或者干脆用 bootstrap 去估置信区间而不是硬套二项分布的方差公式。6.2 p 漂移导致的过度分散我做过一个项目把三个渠道的数据合并统计整体转化率。表面上 n 30000、p 3%算出来 σ 很小任何一点波动都被判成显著。但实际数据里渠道 A 转化率 6%、渠道 B 2%、渠道 C 4%混在一起之后真实的方差远大于 np(1-p)。这种现象叫过度分散over-dispersion。检验方法可以用一个粗略指标把观测方差除以理论方差 np(1-p)如果比值明显大于 1就说明存在过度分散。更正式的做法是用 beta-二项分布去拟合它允许 p 本身是个随机变量能吸收掉漂移带来的额外方差。注意合并不同群体的数据之前先分别算一遍各组的 p差得多就别合并。6.3 多重比较把小概率放大成必然事件二项分布里的小概率事件多做几次就必然出现。假设单次实验出现异常的概率是 1%你同时跑 20 个指标做监控至少有一个指标报警的概率是 1 - 0.99²⁰ ≈ 18%。这不是系统出了问题纯粹是多重比较的后果。处理办法是预先定好主指标别让所有指标都参与判定或者用多重比较校正方法把单次判定阈值收紧。我在搭监控的时候会把报警阈值从 2σ 提到 3σ 甚至更高宁可漏报也别每周围着噪声打转。7. 把二项分布用成决策工具两个真实落地的场景7.1 两样本比例对比A/B 实验的显著性判断对照组 n₁ 次试验中成功 c₁ 次实验组 n₂ 次中成功 c₂ 次要判断两组的真实比例是否不同。在样本量足够时可以用二项分布的正态近似构造 z 统计量z (p̂₁ - p̂₂) / √(p̂_pool · (1 - p̂_pool) · (1/n₁ 1/n₂))其中 p̂_pool 是把两组数据合并后算的总比例。算出 z 之后对照标准正态分布查尾概率。这套方法背后其实就是二项分布理解了前面的期望方差这个公式里每一项的来历就不会显得突兀。实际操作中我更强调先定样本量。如果差异只有 0.3 个百分点而每组只有 2000 个样本那这个实验从设计上就没有分辨能力跑完了也只能得到不显著的结论。可以用功效分析反推想以 80% 的概率检出这个差异每组大概要多少样本。这个数字往往比想象的大好几倍。7.2 抽样质检里的合格判定阈值一批产品规定不合格率不得超过 2%。从批量里抽 500 件如果不合格件数超过某个阈值就判整批不合格。这个阈值怎么定就是找最小的 c使得在真实不合格率恰好是 2% 时P(X c) 小于某个可接受的风险水平比如 5%。用二项分布算n 500、p 0.02期望 10标准差约 3.13。算 P(X ≥ 17) 大约是多少(16.5 - 10)/3.13 ≈ 2.08对应尾概率约 1.9%已经低于 5%。所以阈值定在 16 或 17 就差不多。这个判定规则的背后逻辑就是如果真实水平真的达标那么被判不合格的概率控制在 5% 以内。我帮团队设计抽样方案时会把这个阈值和另一类风险一起考虑真实不合格率其实超标了但抽样没抓出来的概率漏判风险。这两类风险是一对矛盾样本量越大两者能同时压得越小。样本量小的时候只能在两者之间做取舍没有免费午餐。我做这类分析时固定的几个动作踩过的坑多了之后我现在处理任何和二项分布相关的数据基本都按这套流程走一遍先确认 n 是不是固定、试验是否独立、p 是否恒定这三件事然后用期望和标准差快速算一个正常波动范围先判断眼前的差异是不是噪声接着按 np 和 n(1-p) 的大小决定用原始公式、正态近似还是泊松近似最后才是正式的假设检验。这套顺序的好处是大部分看似显著的结论在第二步就被筛掉了不用等到做完复杂检验才发现样本量根本不够。代码层面我有个小习惯凡是算右尾概率一律用binom.sf而不是1 - binom.cdf这个习惯在小概率场景里救过我好几次。另外p 接近 0 或接近 1、n 又很大的时候我会顺手用对数空间算一遍和 scipy 的结果对一下两边一致才敢用于线上监控。多花两分钟做这个交叉验证比事后排查为什么报警值为零要划算得多。