1. 起因:被负二项分布的参数化解读坑了一次
去年做用户行为分析的时候,我需要回答一个很具体的问题:一个用户平均要访问多少次页面,才会产生第 3 次下单?直觉上这是个"负二项分布"的活儿,我随手敲了两行代码,一行调库,一行按公式手算,结果两个数差了十万八千里。折腾了四十分钟才反应过来,问题不在代码,在于我把"失败次数"的参数化和"试验总次数"的参数化混着用了。这件事之后,我决定把二项分布与负二项分布整理成一套随身卡片,不是为了考试,是为了在真正写代码、写分析报告、跟业务方开会的时候,能在三十秒内把参数对应关系翻出来。
这套卡片我前后改了四版,从最开始的 A4 打印对折,到后来严格按 A6 尺寸排版双面覆膜,中间踩的坑集中在三块:参数化的默认约定、期望方差的记忆、以及"什么时候该从二项换成负二项、什么时候该从泊松换成负二项"。这三块恰恰是教科书里要么一笔带过、要么写得极度抽象的部分。
这篇东西适合谁看?如果你正在学概率论,觉得公式背了就忘;如果你在做 A/B 测试、质量抽检、留存分析,需要判断样本量;如果你在建模时发现计数数据的方差远大于均值,不知道该上泊松还是负二项——这套卡片的制作思路和内容清单可以直接拿去用。整个项目没有任何门槛,一支笔、一台能打印的机器,加上半小时排版,就能做出来。
2. 先把骨架钉死:两个分布的核心四要素
做卡片最容易犯的错,是把教科书的内容原样抄上去。卡片的面积是有限的,A6 纸面真正能舒服阅读的区域大概只有 93mm × 136mm,塞不下推导过程。所以我定的规矩是:每张卡片只回答四个问题——随机变量是什么、支撑集在哪、概率质量函数长什么样、期望和方差是多少。这四要素之外的任何内容,都放到背面,而且必须是"一句话能读完"的形式。
2.1 二项分布:固定试验次数,看成功次数
二项分布的骨架非常干净。做 n 次独立同分布的伯努利试验,每次成功概率固定为 p,那么成功次数 X 服从参数为 (n, p) 的二项分布。它的概率质量函数是 P(X=k) = C(n,k) · p^k · (1-p)^(n-k),k 取 0 到 n 的整数。期望是 np,方差是 np(1-p)。
这里我要特别强调三个前提,它们是二项分布的"入场券",缺一个就不能用:试验次数固定、每次试验相互独立、每次成功概率相同。现实中很多场景看着像二项分布,其实不是。比如"一个用户在一个月内是否会流失",如果这个月他已经被推荐算法干预过一次,下一次的概率就变了,独立性被破坏。再比如"抽检 100 件产品有几个不合格",如果这 100 件是同一批次同一台机器同两个小时生产的,p 未必稳定。这些细节不写清楚,卡片就退化成公式抄写本了。
方差的含义也值得在卡片上单独标注一句:np(1-p) 在 p=0.5 时取最大值 n/4。这个结论在做样本量估算的时候极有用——如果你要估计一个转化率,最坏情况下方差是 n/4,反过来推算样本量时直接用这个上界,比猜 p 值靠谱得多。我自己的经验是,早期做 A/B 测试估算样本量时,如果对基准转化率心里没底,就按 p=0.5 算,宁可多跑几天也别中途发现样本不够。
另外补一个容易被忽略的细节:二项分布的众数是 floor((n+1)p);当 (n+1)p 恰好是整数时,floor((n+1)p) 和 (n+1)p - 1 都是众数。这个性质在写"最可能发生几次"这类业务结论时会用到,比如 n=20、p=0.3 时 (n+1)p=6.3,众数是 6,而不是期望值 6.0 对应的那个"正好"。
2.2 负二项分布:固定成功次数,看需要多少次试验
负二项分布是二项分布的"对偶"。二项是次数定死、成功数浮动;负二项是成功数定死、次数浮动。你问"我要攒够 r 次成功,总共得试多少次",答案服从负二项分布。
但麻烦就麻烦在,负二项分布至少有两套主流参数化,而且每本书、每个库选的都不一样。
第一套是失败次数版。定义 Y 为"取得第 r 次成功之前累计失败的次数",那么 Y 的支撑集是 0, 1, 2, ...,概率质量函数是 P(Y=k) = C(k+r-1, k) · p^r · (1-p)^k。期望是 r(1-p)/p,方差是 r(1-p)/p²。scipy 的nbinom和 numpy 的negative_binomial采用的都是这一套。
第二套是试验总次数版。定义 N 为"取得第 r 次成功时总共进行的试验次数",那么 N = Y + r,支撑集是 r, r+1, r+2, ...,概率质量函数是 P(N=n) = C(n-1, r-1) · p^r · (1-p)^(n-r)。期望是 r/p,方差仍然是 r(1-p)/p²。
注意方差在两套参数化下是一样的,只有期望差了 r。这个 r 就是那个让我少算四十分钟的元凶。当你用代码算出均值 7.5,用手算公式得到 4.5,差值刚好是 r 的时候,不用怀疑,就是参数化对不上。
还有一个必须写进卡片的关系:r = 1 时,负二项分布退化为几何分布。几何分布有两套参数化——"首次成功所需的试验次数"和"首次成功前失败的次数",这两者同样差 1。scipy 的stats.geom用的是试验次数版,P(X=k) = (1-p)^(k-1) · p,支撑从 1 开始,期望 1/p,这和nbinom(n=1)差了整整一个 1。这个坑我在自测里踩过两次。
2.3 一张对照表把两者钉在一起
卡片背面如果要放一张表,我就放这张。它比任何文字描述都直观,因为二项和负二项的对称性在表格里是"看得见"的。
| 维度 | 二项分布 | 负二项分布(失败次数版) |
|---|---|---|
| 固定的量 | 试验次数 n | 成功次数 r |
| 随机变量 | 成功次数 X | 成功前的失败次数 Y |
| 支撑集 | k = 0, 1, ..., n | k = 0, 1, 2, ... |
| PMF | C(n,k) p^k (1-p)^(n-k) | C(k+r-1, k) p^r (1-p)^k |
| 期望 | np | r(1-p)/p |
| 方差 | np(1-p) | r(1-p)/p² |
| 退化特例 | n=1 时为伯努利分布 | r=1 时为几何分布 |
| 别名 | 无常用别名 | Pascal 分布、Polya 分布 |
| 典型场景 | 抽检不合格数、点击人数 | 攒够 r 单需要的曝光量、集齐 r 张卡需要的抽卡次数 |
表格里最后一行"别名"是我特意加的。Pascal 分布一般特指"试验总次数"那套参数化,而 Polya 分布特指负二项分布作为 Gamma-Poisson 混合的那一面。在文献里看到不同叫法时,先别急着套公式,先确认它用的是哪套支撑集。
3. 卡片内容设计:七格模板与十二张卡片清单
内容清单是整个项目里最花时间的部分。我的做法是先定一个固定模板,再往模板里填,而不是想到什么写什么。模板固定下来之后,每张卡片的制作时间能压到十分钟以内,而且后期复习时的阅读顺序是一致的,不用每次重新适应版式。
3.1 七格模板的填写规范
正面四格,背面三格。正面最上方是卡片编号和标题,比如"NB-02 负二项:失败次数版",编号前缀用 B 表示二项、NB 表示负二项、R 表示关系卡、T 表示陷阱卡。第二格是随机变量的严格定义,必须写"Y 表示取得第 r 次成功前失败的次数"这种完整句式,不能只写"失败次数"三个字,因为歧义就藏在这三个字里。第三格是PMF 公式,用 LaTeX 写。第四格是期望与方差,两行,字号加大加粗。
背面第一格是一句话直觉。比如二项分布的直觉是"投 n 次硬币,正面出现几次",负二项失败次数版的直觉是"抛到第 r 个正面为止,中间出现了几个反面"。第二格是典型业务场景,写两个,一个偏互联网、一个偏传统行业。第三格是易错点,这是整套卡片里价值最高的一格,后面单独说。
我强烈建议正面只放公式和定义,不放任何解释性文字。原因很简单:卡片的设计目的是"回忆测试",不是"阅读材料"。如果你一眼看到公式就知道怎么用,那这张卡对你没用;如果你看到一个场景描述能以 80% 的把握写出公式,那才是真的记住了。解释性文字全放背面,翻过去才能看,这个物理动作本身就是个很好的自测机制。
3.2 十二张卡片的分组清单
最终我做了十二张,分成四组,每组三张。
第一组是基础定义卡,编号 B-01、NB-01、NB-02。B-01 是二项分布的标准定义,NB-01 是负二项分布的失败次数版,NB-02 是试验总次数版。这一组的作用是把两套参数化硬性分开,防止混用。
第二组是退化与极限卡,编号 R-01、R-02、R-03。R-01 讲"二项 n=1 退化为伯努利",R-02 讲"负二项 r=1 退化为几何,且几何自身有两套参数化",R-03 讲"二项在 n→∞、p→0、np→λ 时收敛到泊松"。这三张放在一起,是因为它们描述的都是"边界情况",而边界情况恰恰是最容易搞错的地方。
第三组是应用场景卡,编号 A-01 到 A-03。A-01 是 A/B 测试里的样本量估算,A-02 是质量抽检里的接收概率计算,A-03 是"集齐 r 个不同物品需要的抽卡次数"这类负二项应用。A-03 这张卡我用得最多,因为它同时涉及负二项的期望计算和"等概率假设是否成立"的检验。
第四组是陷阱与检验卡,编号 T-01 到 T-03。T-01 是参数化对照速查,T-02 是过离散判据,T-03 是近似替代的边界条件。这三张不常看,但每次写分析报告之前扫一眼,能省掉不少返工。
十二张听起来不多,但双面打印加覆膜之后是一小叠,塞在笔记本夹层里正合适。我试过做二十张,结果复习一遍要十几分钟,反而懒得翻。控制在十二张以内,五分钟能过一遍,可持续性明显更好。
3.3 反例比正例值钱:易错格怎么写
易错格的写法,我的原则是必须写一个具体的、能算出不同数字的反例,而不是写"注意参数含义"这种废话。
举个具体例子。NB-02 那张卡的易错格,我写的是:"若 r=5、p=0.4,用失败次数公式算期望得 5×0.6/0.4=7.5,用试验总次数公式算得 5/0.4=12.5。若你的答案落在 8 到 12 之间,说明你既没搞清支撑集,也没搞清 r 的位置。"
再比如 R-02 那张几何分布的卡,易错格写的是:"scipy 的 geom.pmf(k, p) 当 k=1 时输出 p,支撑从 1 开始;nbinom.pmf(k, 1, p) 当 k=0 时输出 p,支撑从 0 开始。两者对同一个物理事件给出的 k 值差 1,做题时先数清楚'成功那一次'算不算在内。"
这类反例的好处是,它把抽象的"注意参数化"变成了一个具体的数字检验。你算完答案,拿这个数一比对,对不上就立刻知道错在哪,不用再从头推。我个人经验是,这种"数字指纹"式的易错提示,比大段文字说明的纠错效率高出一个量级。
4. 从 Markdown 到手上能摸到的卡片:排版与生成
内容写完之后就是落地。这一块很多人会低估它的麻烦程度,随手用 Word 拉个表格打印,结果中文字体和数学公式对不齐,公式里的下标糊成一团,最后只能凑合看。我第二版就是这么干的,第三版彻底换成了 Markdown 加 Pandoc 的流程。
4.1 工具选型:为什么是 Markdown + Pandoc + xelatex
选这套工具的理由有三个。第一,公式好写。Markdown 里用$...$写 LaTeX 数学公式,Pandoc 能直接渲染成 PDF 里的矢量公式,打印出来清晰度远高于截图贴图。第二,内容与版式分离。卡片内容存在一个.md文件里,版式参数写在命令行里,改字号、改页边距不用动内容一个字。第三,版本可控。每次修订都是一次文本提交,能看到自己上个月把哪个公式写错了。
具体命令是这样:
pandoc cards.md -o cards.pdf \ --pdf-engine=xelatex \ -V geometry:papersize={105mm,148mm} \ -V geometry:margin=6mm \ -V mainfont="Noto Sans CJK SC" \ -V fontsize=9pt \ -V documentclass=article \ --include-in-header=header.tex这里105mm × 148mm就是 A6 尺寸。mainfont要指定一个中文字体,否则 xelatex 遇到中文会直接报错,这个坑几乎所有第一次用 Pandoc 排中文的人都踩过。header.tex里我放了两件事:把段落间距压到 4pt,以及关掉页码。
卡片内容文件里,我用一级标题分隔卡片,二级标题分隔正反面,这样 Pandoc 渲染出来的每个"卡片"是一个独立小节。如果要严格双面打印,更稳妥的做法是每张卡片单独导出一个 PDF 页面,再用 PDF 拼接工具按奇数页、偶数页的规则组装。我一开始图省事,把正反面写在一个页面里,结果打印出来发现背面是倒的,只能手动翻纸,很狼狈。
4.2 尺寸与字号的计算过程
排版参数不能拍脑袋,算一下就知道边界在哪。
A6 是 105mm × 148mm,左右各留 6mm 边距,内容区宽度是 105 - 12 = 93mm。中文字符在 9pt 字号下的字宽约等于字号本身,9pt ≈ 3.17mm。所以一行大约能放 93 ÷ 3.17 ≈ 29 个中文字。一段 90 字的说明文字,会占三行多一点。
如果字号降到 8pt,字宽约 2.82mm,一行能放 93 ÷ 2.82 ≈ 33 个字。看起来只多了 4 个字,但在有限面积内,这意味着同样一段话的能量少占一行。我的选择是正文 8pt、公式 10pt、期望方差 11pt 加粗。公式和关键数值放大,是因为卡片的使用场景是"快速检索",扫一眼就要能定位。
行距也要算。8pt 字号配 1.35 倍行距,一行占 8 × 1.35 = 10.8pt ≈ 3.81mm。内容区高度 148 - 12 = 136mm,去掉标题占的 8mm,剩 128mm,大约能放 33 行。一条 PMF 公式占两行,一个期望方差块占两行,剩下的空间放七到八行说明文字刚好。
双面打印时还要考虑对齐问题。我用的方案是先按单面排版,然后用pdfjam或者打印机的"双面-长边翻页"设置手动处理。实测下来,长边翻页比短边翻页更稳,因为 A6 卡片的翻页轴在长边时,背面内容的上下位置和正面是一致的,不会出现"正着看正面上,翻过去背面文字倒过来"的情况。这个小细节第一次做的时候完全没想到,浪费了小半包纸。
4.3 自测抽卡脚本
卡片做出来之后,如果没有自测机制,很容易变成"收藏品"——做完放在那儿,一年翻不了两次。所以我写了个简单的抽题脚本,随机生成参数,出题让我算,我算完再对答案。
import random from math import comb def check_binomial(): n = random.randint(5, 30) p = random.choice([0.1, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.75]) k = random.randint(0, n) pmf = comb(n, k) * p**k * (1 - p)**(n - k) return { "题": f"X~Bin({n}, {p}),求 P(X={k})、E[X]、Var(X)", "P(X=k)": round(pmf, 6), "E[X]": round(n * p, 4), "Var(X)": round(n * p * (1 - p), 4), "众数": int((n + 1) * p), } def check_negbin(): r = random.randint(2, 8) p = random.choice([0.2, 0.3, 0.4, 0.5, 0.6]) k = random.randint(0, 12) # 失败次数版 pmf_fail = comb(k + r - 1, k) * p**r * (1 - p)**k # 试验总次数版,对应 n = k + r n = k + r pmf_trial = comb(n - 1, r - 1) * p**r * (1 - p)**(n - r) return { "题": f"NB(r={r}, p={p}),失败 k={k} 次的概率是多少?", "失败次数版 PMF": round(pmf_fail, 6), "试验总次数={}时的 PMF(应相等)".format(n): round(pmf_trial, 6), "E[失败次数]": round(r * (1 - p) / p, 4), "E[试验总次数]": round(r / p, 4), "Var(两版相同)": round(r * (1 - p) / p**2, 4), } if __name__ == "__main__": for _ in range(3): print(check_binomial()) print(check_negbin()) print("-" * 40)这个脚本最妙的地方在于"失败次数版 PMF"和"试验总次数版 PMF 应相等"这一行。同一个物理事件的概率,用两套公式算出来必须完全一样,这是对参数化理解最直接的检验。如果你算出来不相等,说明某一边的指数或者组合数写错了。
我用这个脚本刷了大概两百题,主要集中在负二项那部分。刷到第一百题左右的时候,公式基本就变成肌肉记忆了,看到 r 和 p 就知道该往哪个方向算。
5. 别只背结论:用蒙特卡洛把公式跑一遍
卡片只能帮你记公式,但公式对不对、适用条件成不成立,得靠数据说话。我用蒙特卡洛模拟把两个分布的期望方差都验了一遍,同时顺手把"过离散"这个判据也实现了。这一节的内容,我建议你也跑一遍,因为模拟结果和公式吻合的那一刻,对这个分布的信任度会完全不一样。
5.1 二项分布的均值方差核对
import numpy as np rng = np.random.default_rng(20240517) n, p, N = 20, 0.3, 200_000 xs = rng.binomial(n, p, size=N) print("模拟均值 %.4f 理论均值 %.4f" % (xs.mean(), n * p)) print("模拟方差 %.4f 理论方差 %.4f" % (xs.var(ddof=1), n * p * (1 - p)))跑出来模拟均值大约 6.00、理论 6.00,模拟方差大约 4.20、理论 4.20。误差在小数点后第二位,属于抽样误差的正常范围。
这里有个细节值得说:ddof=1是必须加的。numpy 的var默认除以 N,是总体方差,而我们用的是样本,应该除以 N-1。二十万样本下两者差别微乎其微,但如果只抽了一千个样本,差距就能看出来。我在卡片 T-03 上专门写了一句:"做过离散检验时,方差用样本方差(ddof=1),否则小样本下会系统性低估。"
顺便说一个实用的性质:n 越大、p 越接近 0.5,二项分布越接近正态分布。经验法则是 np ≥ 5 且 n(1-p) ≥ 5 时,用正态近似比较稳。这个条件在卡片 R-03 上有,因为它在做样本量估算时是个硬门槛——如果样本量太小,用正态近似算置信区间会明显失真。
5.2 负二项分布的三种参数化核对
from scipy import stats import numpy as np rng = np.random.default_rng(20240517) r, p, N = 5, 0.4, 200_000 # 失败次数版 ys = rng.negative_binomial(r, p, size=N) print("失败次数版 模拟均值 %.4f 理论 %.4f" % (ys.mean(), r * (1 - p) / p)) print("失败次数版 模拟方差 %.4f 理论 %.4f" % (ys.var(ddof=1), r * (1 - p) / p**2)) # 试验总次数版 = 失败次数 + r ns = ys + r print("试验总次数版 模拟均值 %.4f 理论 %.4f" % (ns.mean(), r / p)) # 与 scipy 的 PMF 对齐核对 k = 3 hand = stats.nbinom.pmf(k, r, p) manual = np.math.comb(k + r - 1, k) * p**r * (1 - p)**k print("scipy PMF %.8f 手写 PMF %.8f" % (hand, manual))跑出来的结果是失败次数版均值约 7.50、方差约 18.75,试验总次数版均值约 12.50。这三个数字分别是 r(1-p)/p = 5×0.6/0.4 = 7.5、r(1-p)/p² = 7.5/0.4 = 18.75、r/p = 12.5,完全对得上。
这里要重点提醒numpy的negative_binomial(n, p)语义。它的参数n是成功次数、p是单次成功概率,返回的是"达到 n 次成功之前的失败次数",也就是失败次数版。这一点和 scipy 的nbinom是一致的,两者可以互换使用。但如果你是从别的语言或者别的教材过来的,习惯把 p 理解成"失败概率",那结果会完全反过来。我自己有一次看一段 R 代码,里面写的是rnbinom(n, size, prob),prob是成功概率,和 Python 一致,但如果用的是mu参数化,那又是另一回事了。
第三个要注意的是stats.geom。它的 PMF 是(1-p)^(k-1) * p,支撑从 1 开始,期望是 1/p。而nbinom在 r=1 时,PMF 是p * (1-p)^k,支撑从 0 开始,期望是 (1-p)/p。两者描述的是同一件事,但 k 值差 1,期望也差 1。这个坑我在卡片 R-02 上写得非常醒目。
5.3 过离散判据与负二项回归的入口
这是整套卡片里最"实战"的一块。在计数数据建模里,如果直接用泊松回归,一个隐含假设是方差等于均值。现实中大部分计数数据都不满足这个,方差往往远大于均值,这叫过离散。过离散的后果是标准误被低估,显著性检验变得过于乐观,你会看到一堆"显著"的变量其实什么都没说明。
判断过离散的统计量叫离散度,定义是方差除以均值。泊松分布下这个值等于 1,大于 1 就是过离散。更严格的检验用下面这个统计量:
import numpy as np from scipy import stats def dispersion_test(counts): """零假设:数据来自泊松分布(方差=均值)""" counts = np.asarray(counts, dtype=float) m = counts.size mean = counts.mean() stat = ((counts - mean) ** 2).sum() / mean # 近似服从卡方(m-1) pval = stats.chi2.sf(stat, df=m - 1) ratio = counts.var(ddof=1) / mean return {"离散度": round(ratio, 4), "检验统计量": round(stat, 2), "p值": round(pval, 6)} # 模拟一组过离散数据:个体风险异质 rng = np.random.default_rng(7) lams = rng.gamma(shape=2.0, scale=3.0, size=500) # 均值6,方差18 counts = rng.poisson(lams, size=500) print(dispersion_test(counts))这段代码模拟的场景是真实数据里过离散最常见的来源:个体异质性。每个用户有自己的行为强度 λ,λ 本身服从 Gamma 分布,观测到的计数就不服从泊松了。数学上可以证明,Gamma 混合的泊松,其边缘分布就是负二项分布——这就是负二项分布被称为 Polya 分布的原因,也是它在计数建模里如此常见的原因。
把均值 μ 和散度参数 θ 写出来,负二项回归的形式是 Var = μ + μ²/θ。θ 越大越接近泊松,θ 越小过离散越严重。这个式子在 R 里对应MASS::glm.nb,在 Python 里对应statsmodels的NegativeBinomial。我做过的一个留存分析里,用户每日打开次数的离散度大约是 3.7,直接上泊松回归的话,几个核心变量的 p 值都在 0.01 以下,换成负二项回归之后只有一个还能保住 0.05 的显著性,结论差得非常远。
这也是为什么我在卡片的 T-02 上写了一句:"看到计数数据先算离散度,别急着建模。"这句话救过我至少两次。
6. 常见问题与排查实录
这一节是我在使用这套卡片过程中积累下来的问题清单。有几条是别人问我的,有几条是我自己栽跟头之后补上去的。我把它们整理成速查表的格式,方便对照。
6.1 参数化混淆速查表
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| 手算期望比代码均值大 r | 你按试验总次数算,代码返回失败次数 | 检查支撑集是否含最后那次成功 |
| 几何分布概率总差一个 (1-p) 因子 | scipy 用试验次数版,你用了失败次数版 | 核对 k 的起始值:从 1 开始还是从 0 开始 |
| 负二项方差对不上 | 方差在两套参数化下相同,问题多半在 p 的定义 | 确认 p 是成功概率还是失败概率 |
| 模拟序列的中文乱码 | PDF 引擎没配中文字体 | xelatex 加-V mainfont参数 |
| 卡片背面文字倒置 | 用了短边翻页 | 改成双面长边翻页 |
| 公式在 PDF 里模糊 | 用了截图贴图 | 改成 LaTeX 公式由 Pandoc 渲染 |
这张表我贴在卡片叠的最上面,用的时候先扫一眼,能解决八成的困惑。
6.2 近似替代的边界条件
什么时候能用泊松近似二项、什么时候能用正态近似二项、什么时候负二项可以退化成泊松,这三个边界条件必须写死在卡片上,因为它们直接决定你的分析结论靠不靠谱。
泊松近似二项的条件是n 足够大、p 足够小、np 保持中等大小。经验门槛是 n ≥ 20、p ≤ 0.05 时效果比较好。n 越大越小越好,但也不要迷信,p = 0.1、n = 100 的时候误差就已经能看出来了。我自己在算"页面每天的报错次数"这类低概率高频事件时,会直接用泊松,因为这时候 np 稳定,而且泊松的参数只有一个 λ,估起来更省事。
正态近似二项的条件是np ≥ 5 且 n(1-p) ≥ 5。注意这只是一条经验规则,严格来说还要考虑是否需要连续性修正。当你要算的是"成功次数小于等于 k"这种累积概率时,用 k+0.5 做连续性修正会更准确,尤其是在边界附近。
负二项退化为泊松的条件是散度参数 θ → ∞。实际操作中,如果拟合出来的 θ 大到让 μ²/θ 相对 μ 可以忽略,那负二项和泊松的结果就没差别,直接用泊松更简洁。我的经验阈值是 θ > 100 的时候基本上可以认为两者等价,但我还是会两个模型都跑一遍,比对一下系数,图个心安。
6.3 自测中反复栽跟头的六个点
刷了两百多道自测题之后,我把错得最频繁的六种情况记了下来。这六条现在直接印在卡片叠的封底。
第一条是把"成功次数的分布"和"等待时间的分布"搞混。前者是二项,后者是负二项。看到"总共做 n 次,成功几次"就往二项走,看到"成功 r 次为止,用了多久"就往负二项走。
第二条是忘记检查独立性。这条在考试里不会考,但在真实数据里天天出现。同一用户的多条记录、同一批次的产品、同一时段的流量,都可能不独立。不独立的时候,方差的公式会失效,实际的方差通常比 np(1-p) 大,因为存在正相关。这种情况下的补救办法是上分层模型或者用聚类稳健标准误。
第三条是把 p 理解成失败概率。这是个低级错误,但发生的频率出奇地高,尤其是当你连续看了好几篇不同来源的材料之后。我的防错办法是在每张卡片的 p 后面都加一个括号注明"成功概率",看到括号就条件反射地确认一下。
第四条是混淆组合数的上下标。二项是 C(n, k),负二项是 C(k+r-1, k)。负二项那个组合数里有 r-1 而不是 r,这个 -1 的存在感特别低,抄写的时候很容易漏掉。我的做法是记一句口诀:"失败 k 次前面有 k+r-1 个位置,从中选 k 个放失败。"
第五条是期望和方差的单位搞错。期望 r(1-p)/p 的单位是"次",如果你在业务报告里写成"人次"或者"天",量纲就乱了。这个错误在跨部门沟通时特别容易引起误解,我曾经把"平均需要 7.5 次曝光"写成了"7.5 个用户曝光",被同事问了三遍。
第六条是忽视小样本下的偏差。负二项分布的期望估计在小样本下是有偏的,尤其是 r 很小的时候。如果要用观测数据反推 p,样本量最好在 30 以上,否则置信区间会宽得没什么用。我在做"用户需要看几次推荐才会点"的分析时吃过这个亏,一开始只用了二十几个用户的数据,算出来的 p 波动极大,后来补到一百多个才稳定下来。
这套卡片我用了大半年,最大的感受是:概率分布的知识不是靠理解一次就能记住的,而是靠反复的、低成本的检索练出来的。卡片的价值不在于它写得多全,而在于它降低了检索成本,让你愿意去查、去核对。我做卡片之前,遇到不确定的参数化会凭感觉猜,做完之后,翻一眼就能确认,这个差别在累积几十次分析之后会变得非常明显。
后续我还想往这套卡片里加两张:一张是 Beta-Binomial 的共轭关系,因为它在做转化率贝叶斯估计时太常用了;另一张是负二项分布的最大似然估计,因为用矩估计反推 p 在小样本下确实不够稳。这两张什么时候做出来不好说,但位置已经在卡片盒里留好了。