因果推断《Causal Inference in Python》中文笔记第2章 随机实验与统计学回顾

《Causal Inference in Python: Applying Causal Inference in the Tech Industry》因果推断啃书系列

  第1章 因果推断导论
  第2章 随机实验与统计学回顾
  第3章 图形化因果模型
  第4章 线性回归的不合理有效性
  第5章 倾向分
  第6章 效果异质性
  第7章 元学习器
  第8章 双重差分

  持续更新中:
  第9章 综合控制
  第10章 Geo实验与Switchback实验
  第11章 不依从性与工具
  第12章 后续行动

第2章 随机实验与统计学回顾

第1章中介绍了因果关系的基础知识,现在是时候来讨论因果推断的推理部分了。首先,本章将在随机实验(Randomized Experiments)的背景下回顾上一章中的一些概念。随机实验是因果推断的黄金标准,所以理解它的特别之处非常重要。即使随机化不是一种选择,但在考虑因果关系时,将其作为一种范式也将会非常有帮助。

接下来,我们将使用随机实验来回顾一些重要的统计概念和工具,如误差、置信区间、假设检验、功效和样本量计算。

2.1 随机化的强制独立性(Brute-Force Independence)

上一章中介绍了关联关系为什么不同于因果关系,也介绍了关联关系等于因果关系的条件:
E ( Y ∣ T = 1 ) − E ( Y ∣ T = 0 ) = E ( Y 1 − Y 0 ∣ T = 1 ) ⏟ A T T + ( E ( Y 0 ∣ T = 1 ) − E ( Y 0 ∣ T = 0 ) ) ⏟ − B i a s 1 E(Y|T=1)-E(Y|T=0)=\underbrace{E(Y_1-Y_0|T=1)}_{ATT}+\underbrace{(E(Y_0|T=1)-E(Y_0|T=0))}_{-Bias_1} E(YT=1)E(YT=0)=ATT E(Y1Y0T=1)+Bias1 (E(Y0T=1)E(Y0T=0))

简要概述就是关联关系可以被描述为两个组成部分的和:被干预者的平均干预效果和偏差。只有当偏差分量为零时,被测量的关联关系才完全归因于因果关系。当 E ( Y t ∣ T = 0 ] = E ( Y t ∣ T = 1 ) E(Y_t|T=0]=E(Y_t|T=1) E(YtT=0]=E(YtT=1) 时,偏差就会消失。换句话说就是,除了受到的干预方法不同,如果被干预组与对照组相同或具有可比性,则关联关系即为因果关系。或者,用稍微专业一点的术语来说,可比性就是干预组的潜在结果与未干预组的潜在结果在期望上相等。潜在结果 Y t i Y_{ti} Yti 是分析单元 i i i 接受干预 t t t 后的结果。

上一章还简要介绍了在潜在结果独立于干预的情况下,如何将关联关系和因果关系等同起来:
( Y 0 , Y 1 ) ⊥ T (Y_0,Y_1) \perp T (Y0,Y1)T

注意,这不是在谈论干预和结果之间的独立性,因为结果就是干预导致的。如果说干预和结果是独立的,说明干预对结果没有影响。

实际上这里指的是干预与潜在结果是独立的。 Y 1 ⊥ T Y_1 \perp T Y1T 意味着如果分析单元接受了干预,观察到的结果与他们是否实际接受干预无关。同样, Y 0 ⊥ T Y_0 \perp T Y0T 意味着如果分析单元没有接受干预,观察到的结果不依赖实际的干预的赋值。

另一种更简单的说法是,独立性假设意味着干预组和对照组具有可比性,或者知道干预任务并不能给出任何关于潜在结果 Y 0 Y_0 Y0 的信息。所以 ( Y 0 , Y 1 ) ⊥ T (Y_0,Y_1) \perp T (Y0,Y1)T 意味着干预是造成干预组和对照组之间差异的唯一原因:
E ( Y 0 ∣ T = 0 ) = E ( Y 0 ∣ T = 1 ) = E ( Y 0 ) E ( Y 1 ∣ T = 0 ) = E ( Y 1 ∣ T = 1 ) = E ( Y 1 ) E(Y_0|T=0)=E(Y_0|T=1)=E(Y_0) \\ E(Y_1|T=0)=E(Y_1|T=1)=E(Y_1) E(Y0T=0)=E(Y0T=1)=E(Y0)E(Y1T=0)=E(Y1T=1)=E(Y1)

从上式可得,将干预组和控制组结构的平均值相减就确定了ATE:
E ( Y ∣ T = 1 ) − E ( Y ∣ T = 0 ) = E ( Y 1 − Y 0 ) = A T E E(Y|T=1)-E(Y|T=0)=E(Y_1-Y_0)=ATE E(YT=1)E(YT=0)=E(Y1Y0)=ATE

尽管独立性只不过是一个假设,但如果随机化干预 T T T,就使得独立性成立。随机化将干预赋值与我们熟知的抛硬币的随机机制联系起来,这个硬币每面朝上的概率不必五五开,我们可以只对10%,甚至更少的1%的分析单元进行干预。只要赋值过程是随机的,就可以得到合适的条件来识别干预效果。

通过随机化干预,可以确保干预组和对照组(在期望值上)大致具有可比性,两组之间唯一的系统性差别是干预本身,这就可以将结果的任何差异归因于干预。从本质上讲,随机化会让干预和潜在结果之间的独立性成立。

现在让我们来看一个例子,使用随机对照试验(RCT)来了解交叉销售电子邮件的影响。

2.2 一个A/B测试案例

对不盈利的产品进行打折甚至免费提供,以吸引更多的新客户,这是很多公司的共同策略。一旦公司拥有了这些客户,它就可以交叉销售其他更有利可图的产品。假设你在一家咖啡外卖公司工作,你的主要产品是每月低价订购服务,这些低价订购能让客户每周都能得到高质、精致的咖啡。除了这种入门的低价订购,你的公司还提供了一种更优质的服务,有冲泡福利和世界上最好的咖啡,比如巴西小镇迪维诺兰迪亚(Divinolandia)的当地生产商生产的咖啡。这是目前为止你最赚钱的服务,因此你的目标是吸引已经订购了低价入门产品的用户开通更高级的服务。为此,您的公司有一个营销团队,主要通过交叉销售电子邮件来试图向客户销售高阶咖啡配送订阅。作为因果推断专家,你的目标是了解这些电子邮件的有效性。

当你希望从现有数据(非随机)中寻找这个问题的答案时,可以清楚地看到,收到电子邮件的客户更有可能购买高级订购。客户购买了你销售的产品,这种情况用专业术语来描述,就是用户被转换(converted)了。所以,你可以说收到电子邮件的客户转换得更多:
E ( C o n v e r s i o n ∣ E m a i l = 1 ) > E ( C o n v e r s i o n ∣ E m a i l = 0 ) E(Conversion|Email=1)>E(Conversion|Email=0) E(ConversionEmail=1)>E(ConversionEmail=0)

不幸的是,你还发现营销团队倾向于向他们认为更有可能转换为客户的客户发送电子邮件。尚不完全清楚他们是如何做到这一点的,也许他们寻找的是那些与公司互动最多的客户,或者是那些在满意度调查中回答积极的客户。不管怎样,这都强有力地表明了:
E ( C o n v e r s i o n 0 ∣ E m a i l = 1 ) > E ( C o n v e r s i o n 0 ∣ E m a i l = 0 ) E(Conversion_0|Email=1)>E(Conversion_0|Email=0) E(Conversion0Email=1)>E(Conversion0Email=0)

换句话说,收到电子邮件的客户即使根本没有收到电子邮件,转换的数量也会比其他客户多。因此,使用均值的简单类比来评估交叉销售电子邮件的真正因果影响是有偏差的。为了解决这个问题,你需要让干预组和未干预组具有可比性: E ( Y 0 ∣ T = 1 ) = E ( Y 0 ∣ T = 0 ) E(Y_0|T=1)=E(Y_0|T=0) E(Y0T=1)=E(Y0T=0),可以使用随机发送邮件的方式达到这样的目的。如果你能做到这一点,干预组和未干预组的平均转化率将是一样的。假设你这样做了,你们从客户群中随机选择了三个样本,没有给第一个发任何邮件,给第二个发送了一封关于高级订购的又大又漂亮的电子邮件,给第三个发送了关于高级订阅的简短且直截了当的电子邮件。在收集了一段时间的数据之后,你会得到这样的结果:

import pandas as pd
pd.set_option('display.max_rows', 5)
import pandas as pd # for data manipulation
import numpy as np # for numerical computation

data = pd.read_csv("./data/cross_sell_email.csv")
data
gendercross_sell_emailageconversion
0short150
1short270
1no_email160
1long241

323 rows × 4 columns

有323个观测值,数据量不大,但是够用。

模拟数据 vs 真实数据

在教授因果推断时,使用模拟数据是很有帮助的。首先,因为因果推断总是伴随着关于数据是如何产生的声明。模拟让我们可以毫不犹豫地讨论这种产生机制。第二,因果推断涉及到反事实的量,我可以选择展示这些量,以便更好地解释正在发生的事情。然而,为了让数据看起来不太像是人造的,我经常改造真实世界的数据,以贴合我给出的例子。例如,当前使用这个例子的数据就是从William T. Alpert等撰写的论文《A Randomized Assessment of Online Learning》(2016)中获取的,然后将其转换为交叉销售电子邮件数据。

为了估计因果效应,您可以简单地计算每个干预组的平均转化率:

(data
 .groupby(["cross_sell_email"])
 .mean())
genderageconversion
cross_sell_email
long0.55045921.7522940.055046
no_email0.54255320.4893620.042553
short0.63333320.9916670.125000

非常简单。你可以看到,无邮件组的转换率为4.2%,而长邮件组和短邮件组的转换率分别为5.5%和12.5%。 A T E = E ( Y ∣ T = t ] − E ( Y ∣ T = 0 ) ATE=E(Y|T=t]-E(Y|T=0) ATE=EYT=t]EYT=0,作为每个干预组和对照组之间的差值,其中长邮件组和短邮件组相比无邮件组分别增加了1.3和8.3个百分点。有趣的是,发送一封简明扼要的电子邮件似乎比一封详细阐述的邮件要好。

随机对照试验的美妙之处在于,你不再需要担心营销团队是否以某种方式瞄准了可能转化的客户,或者就此而言,你不必担心来自不同干预组的客户有任何系统性的差异。通过设计,随机实验消除了这些差异,在理论上使得 ( Y 0 , Y 1 ) ⊥ T (Y_0,Y_1)\perp T Y0,Y1T

在实践中,为了检查随机化是否正确(或者收集到的数据是否正确),对于待干预变量,需要检查干预组是否等于未干预组。例如,你有关于性别和年龄的数据,你可以看到这两个特征在干预组和对照组之间是否平衡。

当你看年龄时,干预组之间看起来非常相似,但性别上似乎存在差异(woman=1,man=0)。收到短邮件的组似乎有63%是男性,相比之下,没有收到邮件的对照组有54%,收到长邮件的组有55%。这有点令人不安,因为你发现效果最好的干预组似乎与其他组不可比。因此,RCT理论上是具备独立性的,但在实践中却未必成立。无论出于什么原因, E ( Y 0 ∣ m a n ) > E ( Y 0 ∣ w o m a n ) E(Y_0|man)>E(Y_0|woman) EY0man)>EY0woman),也就是不干预的时候,男性就会比女性有更高的参与倾向,这可能就导致了简短电子邮件产生良好效果的情况。

关于如何评估平衡性并没有一个明确的共识,但一个非常简单的建议是检查干预组之间的归一化差值:
μ ^ t r − μ ^ c o ( σ ^ t r 2 − σ ^ c o 2 ) / 2 {\hat\mu_{tr}-\hat\mu_{co}}\over \sqrt{(\hat\sigma_{tr}^2-\hat\sigma_{co}^2)/2} σ^tr2σ^co2)/2 μ^trμ^co

μ ^ \hat\mu μ^ σ ^ 2 \hat\sigma^2 σ^2 分别表示样本的均值和方差。因为案例中有三个干预组,我们可以计算两个发了邮件的组相比对照组的差异:

X = ["gender", "age"]

mu = data.groupby("cross_sell_email")[X].mean()
var = data.groupby("cross_sell_email")[X].var()

norm_diff = ((mu - mu.loc["no_email"])/
             np.sqrt((var + var.loc["no_email"])/2))

norm_diff
genderage
cross_sell_email
long0.0158020.221423
no_email0.0000000.000000
short0.1843410.087370

如果差值太小或太大就应该担忧了。遗憾的是,对于多大的差异是太大,并没有一个明确的阈值,但0.5似乎是一个很好的经验法则。在这个例子中,两个差值都没有超过阈值,但似乎收到短邮件的那一组在性别上有很大差异,而收到长邮件的那一组在年龄上有很大差异。

另请参阅

关于这个话题的更深入的讨论,请查看Guido W. Imbens和Donald B. Rubin(Causal Inference for Statistics, Social, and Biomedical Sciences: An Introduction》的第14.2节。

如果当前感觉上面的公式很魔幻,不要担心,一旦你浏览了本章的统计回顾部分,就会更清楚了。现在,我只想让大家注意一下小数据集的情况。即使是在随机化的情况下,一组与另一组也可能碰巧不可比。在大样本中,这种差异趋于消失。这也带来了一个问题,差异多小才足以让你确信干预确实有效,而不是有偶然性的干扰,这是我们很快就会谈到的问题。

2.3 理想化实验

随机实验或随机对照试验是获得因果关系最可靠的方法。这个技巧直截了当且令人信服。它是如此强大,以至于大多数国家都把它作为显示新药有效性的要求。你可以这样想:如果你可以,随机对照试验将是你揭示因果关系所能做的一切。设计良好的随机对照试验是任何科学家和决策者的梦想。

然而,随机对照试验要么非常昂贵(在金钱上,但更重要的是在时间上),要么就是完全不道德。有时你根本无法控制赋值过程。想象你自己是一名医生,试图评估怀孕期间吸烟对婴儿出生时体重的影响。你不能简单地强迫一部分妈妈在怀孕期间吸烟。或者假设你在一家大银行工作,你需要估计信贷额度对客户流失的影响,给你的客户随机分配信用额度代价太大了。或者你想了解提高最低工资对失业的影响,你不能简单地指定一个国家的最低工资标准。此外,正如你将在第3章中看到的,在某些情况下(选择偏差的情况),即使是RCT也不能拯救你。

尽管如此,还是希望你将随机实验看作是揭示因果关系的工具。更确切地说,这里的目标是将其用作基准。当你在没有随机对照试验的情况下进行因果推断时,你应该问自己,回答你问题的完美实验是什么。即使这个理想的实验不可行,它也可以作为一个有价值的基准。即使没有这样的实验,它也能让你明白如何发现因果关系。

2.4 最危险的方程

既然您已经理解了实验的价值,那么是时候回顾一下数据有限意味着什么了。因果推理有两个步骤,RCT在帮助识别方面是非常宝贵的,但如果实验的样本量很小,您将很难进行第二步:推断。为了理解这一点,有必要回顾一些统计概念和工具。如果你已经熟悉了,可以直接跳到下一章

Howard Wainer在他2007年的一篇著名文章中提到了“非常危险的方程”:

“有些方程如果你知道就很危险,而有些方程如果你不知道就很危险。第一类可能会造成危险,因为在它的范围内的秘密打开了大门,背后隐藏着可怕的危险。其中最明显的赢家是爱因斯坦的标志性方程 E = m c 2 E=mc^2 E=mc2,因为它提供了获取隐藏在普通物质中的巨大能量的方法。相反,我感兴趣的是那些在我们不知道的情况下释放危险的方程式,这些方程式近在咫尺,使我们能够清楚地理解事物,但它们的缺失使我们陷入危险的无知。”

他所说的方程就是莫弗(Moivre)方程;
S E = σ n SE=\frac{\sigma}{\sqrt n} SE=n σ
SE表示样本的均值标准误差, σ \sigma σ 表示样本的标准差(Standard Deviation), n n n 表示样本量。

为了了解为什么不知道这个方程是非常危险的,让我们来看一些教育数据。我们收集了ENEM分数的数据
(巴西标准化高中成绩,类似于SAT),来源不同学校,为期三年。还清理了数据,以便在本节中只保留相关的信息。

如果你看看表现最好的学校,有些东西会吸引你的眼球——这些学校的学生数量相当少:

import warnings
warnings.filterwarnings('ignore')

import pandas as pd
import numpy as np
from scipy import stats
import seaborn as sns
from matplotlib import pyplot as plt
from matplotlib import style
from cycler import cycler
import matplotlib

default_cycler = (cycler(color=['0.1', '0.5', '1.0']))

color=['0.3', '0.5', '0.7', '0.9']
linestyle=['-', '--', ':', '-.']
marker=['o', 'v', 'd', 'p']

plt.rc('axes', prop_cycle=default_cycler)

matplotlib.rcParams.update({'font.size': 18})
df = pd.read_csv("data/enem_scores.csv")
df.sort_values(by="avg_score", ascending=False).head(10)
yearschool_idnumber_of_studentsavg_score
2007330626336882.97
20073306540317282.04
20073131172322279.41
20073308767921079.38

10 rows × 4 columns

从另一个角度来看,你只能把1%的顶尖学校分开研究。他们是什么样的人?也许你可以从最优秀的人那里学到一些东西,然后复制到其他地方。当然,如果你看看排名前1%的学校,你会发现,平均而言,它们的学生人数更少:

plot_data = (df
             .assign(top_school = df["avg_score"] >= np.quantile(df["avg_score"], .99))
             [["top_school", "number_of_students"]]
             .query(f"number_of_students<{np.quantile(df['number_of_students'], .98)}")) # remove outliers

plt.figure(figsize=(8,4))
ax = sns.boxplot(x="top_school", y="number_of_students", data=plot_data)

plt.title("Number of Students of 1% Top Schools (Right)")

一个自然的结论是,小规模学校导致更高的学习成绩。这很直观,因为我们认为每个老师负责的学生数量越少,老师就越能集中精力关注每个学生。但这和莫弗方程有什么关系呢?

一旦人们开始根据这些信息做出重要而昂贵的决定,情况就变得危险了。Howard Wainer在他的文章中继续说道:

在20世纪90年代,提倡缩小学校规模变得流行起来。许多慈善组织和政府机构为大型学校的划分提供资金,因为小型学校的学生在高分群体中所占的比例过高。

人们忘记看看排名最后1%的学校,他们的学生也很少!

你在下图中看到的正是莫弗方程所期望的。随着学生人数的增加,平均分数变得越来越精确。学生很少的学校(样本规模小)可能会有很高或很低的分数,这完全是偶然的。这在大型学校中不太可能发生。莫弗方程谈到了关于数据表格中的信息和记录的真实性的一个基本事实:它总是不精确的。那么问题来了:有多不精确?你能做些什么把这些不准确的因素考虑进去呢?

量化不确定性的一种方法是方差。方差表示观测值与期望值的偏离程度。正如莫弗方程所示,这种不确定性会随着你观察到的数据量的增加而减少。如果你看到许多学生在一所学校表现出色,你可以更有信心地认为这确实是一所好学校。然而,如果你看到一所只有10名学生的学校,其中8人表现良好,你就需要更加怀疑了。可能这所学校只是录取了一些中等偏上的学生。

上图中的三角形图案恰恰说明,当样本量很小时,你对学校表现的评估有很大的方差。这也表明,方差随着样本量的增加而减小。这对于学校学生的平均分数来说是正确的,对于任何你可能拥有的汇总统计数据来说也是正确的,包括你经常想要计算的ATE。回到我们的交叉销售电子邮件案例,如果每个处理组中都有数千名客户,而不是数百名,那么将更有信心地认为,在干预组和对照组之间看到的转化率差异不是偶然的。

随机误差(Random Error)和系统误差(Systematic Error)

另一种考虑数据不确定性的方法是对比系统误差和随机误差。系统误差是固有偏差,以同样的方式影响所有测量,而随机误差是由于偶然导致的数据不可预测的波动。系统误差或偏差不会随着你收集更多的数据而减少,因为它将所有的测量都推向同一个方向,远离你想要估计的量。相反,随机误差随着样本量的增加而减小,如莫弗方程所示。统计学是一门处理随机误差造成的不精确性的科学,会将不确定性考虑在内。

2.5 估算的标准误差(Standard Error)

由于这只是对统计学的复习,我将冒昧地讲得快一点。如果您不熟悉分布、方差和标准误差,请继续阅读,但请记住,你可能需要额外查询一些资料。建议去搜索任何麻省理工学院的统计学入门课程,通常都很好,也可以在YouTube上免费观看。

上一节中,我们估算平均干预效果(ATE) E ( Y 1 − Y 0 ) E(Y_1-Y_0) E(Y1Y0) 作为干预组和非干预组之间的平均差异 E ( Y ∣ T = 1 ) − E ( Y ∣ T = 0 ) E(Y|T=1)-E(Y|T=0) E(YT=1)E(YT=0)。具体来说,计算出了两种类型的交叉销售电子邮件在转化上的ATE。然后我们看到短邮件的提升非常令人印象深刻,超过了8个百分点,而长邮件的影响较小,仅增长了1.3个百分点。但仍有一个悬而未决的问题:这些影响是否足够大到可以确信它们不是偶然的?用专业术语来说,你知道它们在统计上是否显著?

要做到这一点,首先需要根据前面展示的方程来估算SE。 n n n 很容易得到,只需要看每种干预值下的样本数。使用pandas的groupby函数,后跟一个size聚合:

data = pd.read_csv("./data/cross_sell_email.csv")

short_email = data.query("cross_sell_email=='short'")["conversion"]
long_email = data.query("cross_sell_email=='long'")["conversion"]
email = data.query("cross_sell_email!='no_email'")["conversion"]
no_email = data.query("cross_sell_email=='no_email'")["conversion"]

data.groupby("cross_sell_email").size()

cross_sell_email
long              109
no_email        94
short             120
dtype: int64

为了得到标准差的估算值,可以使用下面的公式:
σ ^ = 1 N − 1 ∑ i = 0 N ( x − x ‾ ) 2 \hat\sigma=\sqrt{\frac{1}{N-1}\sum_{i=0}^N (x-\overline{x})^2} σ^=N11i=0N(xx)2

其中 x ‾ \overline x x 表示 x x x 的均值。

帽子符号“ ∧ \wedge

本书中在变量上标记“ ∧ \wedge ”表示对参数和预测值的样本估算。

幸运的是,大多数编程软件已经实现了这一点。在pandas中,你可以使用std方法计算标准误差:

def se(y: pd.Series):
    return y.std() / np.sqrt(len(y))

print("SE for Long Email:", se(long_email))
print("SE for Short Email:", se(short_email))

SE for Long Email: 0.021946024609185506
SE for Short Email: 0.030316953129541618

掌握这个公式后非常方便(我们会多次用到它),但要知道pandas也有一个计算标准误差的内置方法,.sem()(即均值标准误差):

print("SE for Long Email:", long_email.sem())
print("SE for Short Email:", short_email.sem())

SE for Long Email: 0.021946024609185506
SE for Short Email: 0.030316953129541618

2.6 置信区间(Confidence Intervals)

估算的标准误差是一种置信度的度量,当你投入到动荡不安、争议不断的统计学洪流中,才能准确理解它的含义。统计学的一种观点——频率论,认为数据只不过是潜在数据生成过程的一种表征。这个过程是抽象而理想的。它是由对我们来说不变且未知的真实的参数控制的。在交叉销售电子邮件的案例中,如果你可以运行多个实验并计算转化率,这些转化率将落在真正的潜在转化率附近,即使不完全等于它。这很像柏拉图对理式(Forms)的描述:

每一种本质理式都以各种各样的组合形式表现出来,这些组合形式有各种活动,有各种物质形式,也有各种相互之间的组合形式,而每一种理式似乎都是多样的。

为了理解这一点,假设我们有简短的交叉销售电子邮件转化率的真实抽象分布。因为转化不是0就是1,符合伯努利分布,假设这个分布的成功概率是0.08,也就是说,每当客户收到短电子邮件,有8%的机会转化。假设我们可以进行10000次实验,每次收集100个客户样本,发送简短的交叉销售电子邮件,观察平均转化率。这10000个转化率将分布在0.08的真实平均值附近(见下图)。有些实验的转化率会低于真实的,有些会高于真实的,但10000个转化率的平均值会非常接近真实的平均值。

列表推导式(List Comprehension)

当使用函数处理序列中的每一项元素时,我倾向于使用大量的列表理解而不是for循环。列表推导式只是映射到for循环的语法糖,增加程序的可读性:

table_2 = []
for n in range(11):
    table_2.append(n*2)

table_2 = [n*2 for n in range(11)]
n = 100
conv_rate = 0.08

def run_experiment(): 
    return np.random.binomial(1, conv_rate, size=n)

np.random.seed(42)

experiments = [run_experiment().mean() for _ in range(10000)]
plt.figure(figsize=(10,4))
freq, bins, img = plt.hist(experiments, bins=20, label="Experiment Means", color="0.6")
plt.vlines(conv_rate, ymin=0, ymax=freq.max(), linestyles="dashed", label="True Mean", color="0.3")
plt.legend()

也就是说,你永远无法确定你的实验的平均值是否符合真实的平均值。然而,可以使用标准误差创建一个区间,该区间包含95%的实验中的真实平均值

在现实生活中,同一个实验无法拥有多个数据集,通常只有一个。但是你可以通过模拟多个实验来构造置信区间(Confidence Interval)。置信区间附带一个概率,最常见的是95%。这个概率告诉你,如果你要进行多个实验并在每个实验中构建95%置信区间,那么真实均值将在95%的情况下落在区间内。

为了计算置信区间,将使用可能是统计学中最令人兴奋的结果:中心极限定理(Central Limit Theorem)。仔细看看刚刚绘制的转化率分布。记住转化不是0就是1,遵循伯努利分布。如果你用直方图来画伯努利分布,它会在0处有一个大柱,在1处有一个小柱,因为成功率只有8%。这看起来一点也不像正态分布。

np.random.seed(42)
plt.figure(figsize=(10,4))
plt.hist(np.random.binomial(1, 0.08, 100), bins=20)

这就是那个令人兴奋的中心极限理论发挥作用的地方。即使数据的分布不是正态分布的(就像转化遵循伯努利分布),数据的平均值总是正态分布的。如果你多次收集转化数据并每次计算平均转换率,这些平均值将遵循正态分布。正态分布众所周知,你可以用它做很多有趣的事情。例如,为了计算置信区间,你可以利用统计理论的知识,即95%的正态分布质量落在平均值上下2(从技术上讲应该是1.96,但2是一个很好的近似值,更容易记住)个标准差之间(见下图)。

x = np.linspace(-4, 4, 100)
y = stats.norm.pdf(x, 0, 1)

plt.figure(figsize=(10,4))
plt.plot(x, y, linestyle="solid")
plt.fill_between(x.clip(-3, +3), 0, y, alpha=0.5, label="~99.7% mass", color="C2")
plt.fill_between(x.clip(-2, +2), 0, y, alpha=0.5, label="~95% mass", color="C1")
plt.fill_between(x.clip(-1, +1), 0, y, alpha=0.5, label="~68% mass", color="C0")
plt.ylabel("Density")
plt.legend()

回到交叉销售实验,现在你知道,如果可以运行多个类似的实验,转化率将遵循正态分布。这个(未知)分布的最佳估算值是这个小实验的均值。此外,标准误差是对样本均值的未知分布的标准差的估计。因此,如果你用标准误差乘以2然后将其与你的实验均值进行加减,你将得到真实均值的95%置信区间:

exp_se = short_email.sem()
exp_mu = short_email.mean()
ci = (exp_mu - 2 * exp_se, exp_mu + 2 * exp_se)
print("95% CI for Short Email: ", ci)

95% CI for Short Email: (0.06436609374091676, 0.18563390625908324)

x = np.linspace(exp_mu - 4*exp_se, exp_mu + 4*exp_se, 100)
y = stats.norm.pdf(x, exp_mu, exp_se)

plt.figure(figsize=(10,4))
plt.plot(x, y, lw=3)
plt.vlines(ci[1], ymin=0, ymax=4, ls="dotted")
plt.vlines(ci[0], ymin=0, ymax=4, ls="dotted", label="95% CI")
plt.xlabel("Conversion")
plt.legend()

当然,不需要将自己限制在95%的置信区间内。如果您想要更小心,您可以生成99%的区间。只需要用标准差乘以正态分布99%质量的系数。

要找到这个系数,可以使用scipy中的ppf函数,是标准正态分布的累积分布函数(CDF)的逆函数。例如,ppf(0.5)将返回0.0,表示标准正态分布的质量的50%低于0.0。因此,对于置信度 α \alpha α,使用 ∣ p p f ( ( 1 − α ) / 2 ) ∣ |ppf((1-\alpha)/2)| ppf((1α)/2) 得到系数,系数乘以SE就得到了曲线对称轴与置信区间上下限的距离:

from scipy import stats

z = np.abs(stats.norm.ppf((1-.99)/2))
print(z)
ci = (exp_mu - z * exp_se, exp_mu + z * exp_se)
ci

2.5758293035489004
(0.04690870373460816, 0.20309129626539185)

stats.norm.ppf((1-.99)/2)

-2.5758293035489004

x = np.linspace(exp_mu - 4*exp_se, exp_mu + 4*exp_se, 100)
y = stats.norm.pdf(x, exp_mu, exp_se)

plt.figure(figsize=(10,4))
plt.plot(x, y, lw=3)
plt.vlines(ci[1], ymin=0, ymax=4, ls="dotted")
plt.vlines(ci[0], ymin=0, ymax=4, ls="dotted", label="99% CI")


ci_95 = (exp_mu - 1.96 * exp_se, exp_mu + 1.96 * exp_se)

plt.vlines(ci_95[1], ymin=0, ymax=4, ls="dashed")
plt.vlines(ci_95[0], ymin=0, ymax=4, ls="dashed", label="95% CI")
plt.xlabel("Conversion")
plt.legend()

这是针对短邮件的。还可以显示与其他干预组相关的转化率的95%的CI:

def ci(y: pd.Series):
    return (y.mean() - 2 * y.sem(), y.mean() + 2 * y.sem())

print("95% CI for Short Email:", ci(short_email))
print("95% CI for Long Email:", ci(long_email))
print("95% CI for No Email:", ci(no_email))

95% CI for Short Email: (0.06436609374091676, 0.18563390625908324)
95% CI for Long Email: (0.01115382234126202, 0.09893792077800403)
95% CI for No Email: (0.0006919679286838468, 0.08441441505003955)

plt.figure(figsize=(10,4))

x = np.linspace(-0.05, .25, 100)
short_dist = stats.norm.pdf(x, short_email.mean(), short_email.sem())
plt.plot(x, short_dist, lw=2, label="Short", linestyle=linestyle[0])
plt.fill_between(x.clip(ci(short_email)[0], ci(short_email)[1]), 0, short_dist, alpha=0.2, color="0.0")

long_dist = stats.norm.pdf(x, long_email.mean(), long_email.sem())
plt.plot(x, long_dist, lw=2, label="Long", linestyle=linestyle[1])
plt.fill_between(x.clip(ci(long_email)[0], ci(long_email)[1]), 0, long_dist, alpha=0.2, color="0.4")

no_email_dist = stats.norm.pdf(x, no_email.mean(), no_email.sem())
plt.plot(x, no_email_dist, lw=2, label="No email", linestyle=linestyle[2])
plt.fill_between(x.clip(ci(no_email)[0], ci(no_email)[1]), 0, no_email_dist, alpha=0.2, color="0.8")

plt.xlabel("Conversion")
plt.legend()
plt.figure(figsize=(10,4))

x = np.linspace(-0.05, .25, 100)
short_dist = stats.norm.pdf(x, short_email.mean(), short_email.sem())
plt.plot(x, short_dist, lw=2, label="Short", linestyle=linestyle[0])
plt.fill_between(x.clip(ci(short_email)[0], ci(short_email)[1]), 0, short_dist, alpha=0.2, color="0.0")

long_dist = stats.norm.pdf(x, long_email.mean(), long_email.sem())
plt.plot(x, long_dist, lw=2, label="Long", linestyle=linestyle[1])
plt.fill_between(x.clip(ci(long_email)[0], ci(long_email)[1]), 0, long_dist, alpha=0.2, color="0.4")

no_email_dist = stats.norm.pdf(x, no_email.mean(), no_email.sem())
plt.plot(x, no_email_dist, lw=2, label="No email", linestyle=linestyle[2])
plt.fill_between(x.clip(ci(no_email)[0], ci(no_email)[1]), 0, no_email_dist, alpha=0.2, color="0.8")

plt.xlabel("Conversion")
plt.legend()

这里可以看到三组95%的CI相互重叠。如果两组没有重叠,就可以得出这样的结论:两组之间转化率的差异不是偶然的。换句话说,你可以说发送交叉销售电子邮件会在转化率上产生统计学上的显著差异。但是事实并非如此,至少当前不成立。重要的是,重叠的置信区间并不足以说明各组之间的差异在统计上不显著,但不重叠的置信区间就意味着它们在统计上不同。换句话说,非重叠置信区间是统计显著性的保守证据。

回顾一下,置信区间是一种将不确定性置于估算值附近的方法。样本量越小,标准误差越大,置信区间越宽。由于它们非常容易计算,缺乏置信区间要么表明一些不良意图,要么只是缺乏知识,都令人担忧。最后,你应该始终对没有任何不确定度度量的测量持怀疑态度。

新冠疫苗的有效性

随机对照试验对制药行业非常重要。也许最广为人知的例子是为确定COVID-19疫苗的有效性而进行的测试,因为这些测试对地球上几乎每个人都产生了巨大的影响。以下是2020年发布的关于mRNA-1273 SARS-CoV-2疫苗的有效性和安全性的研究的结果部分:

该试验招募了30420名志愿者,他们按1:1的比例随机分配接受疫苗或安慰剂(每组15210名参与者)。超过96%的参与者接受了两种注射,2.2%的人在基线时有SARS-CoV-2感染的证据(血清学、病毒学或两者兼有)。安慰剂组185名参与者(56.5/1000人年;95%置信区间(CI),48.7至65.3),mRNA-1273组的11名参与者(3.3/1000人年;95% CI, 1.7至6.0);疫苗有效率为94.1%(95% CI,89.3至96.8%; P < 0.001 P<0.001 P<0.001)。

以下是我对如何根据你已经学习的概念来解释这些结果的看法。请记住,我不是健康专家,我的评论纯粹是关于统计和因果推理的概念。

首先,他们定义了干预组和对照组(安慰剂)组,称干预是随机赋值的,这确保了干预与潜在结果的独立性。这将使他们能够从统计量 E ( Y ∣ T = 0 ) E(Y|T=0) E(YT=0) E ( Y ∣ T = 1 ) E(Y|T=1) E(YT=1) 中确定疫苗的因果影响。接下来,他们将结果定义为每1000人年出现有症状的COVID-19的人数。最后,他们报告估算值 E ( Y ∣ T = 0 ) E(Y|T=0) E(YT=0) E ( Y ∣ T = 1 ) E(Y|T=1) E(YT=1) 的95% CI分别为48.7-65.3和1.7-6.0。这告诉你,与注射安慰剂的人相比,接种疫苗的人检测到的有症状的COVID-19要少得多。他们报告了疫苗的疗效 E ( Y ∣ T = 0 ) / E ( Y ∣ T = 1 ) E(Y|T=0)/E(Y|T=1) E(YT=0)/E(YT=1),以及疗效的95%置信区间:89.3%至96.8%。

最后要提醒你的是:解释置信区间比乍一看要棘手得多。例如,我不应该说一个特定的95%置信区间有95%的概率包含了真实均值。在频率主义统计学中,总体平均被视为一个真正的总体常数。这个常数要么在某个置信区间内,要么在某个置信区间外。也就是说,一个特定的置信区间可能包含真实均值,也可能不包含。如果包含,概率将是100%,而不是95%。如果没有包含,概率为0%。相反,在置信区间中,95%指的是在许多研究中计算的这种置信区间包含真实均值的频率。95%是我们对用于计算95% CI的算法的置信度,而不是特定区间本身的置信度。

话虽如此,作为一名经济学家(统计学家们,请转移视线),我认为这种纯粹主义不是很有用。实践中,人们会说特定置信区间在95%的情况下包含真实均值。虽然这是错误的,但并不是非常有害,因为它仍然在你的估算中提供了可视的不确定性。我的意思是,我宁愿你对置信区间有错误的理解,但对自己的估算值创建了置信区间,也不希望你因为担心理解错误而不使用置信区间。永远不要忘了给你的估值加上置信区间;否则,你会看起来很傻。

可信区间(Credible Intervals)

如果你真的想把一个概率声明附加到一个区间内的参数估算上,你应该检查贝叶斯可信区间。然而,根据我的经验,在大多数情况下(特别是当样本量相对较大时),它们往往会产生类似于频率主义置信区间的结果。这也是为什么我倾向于对置信区间的误解更宽容。

2.7 假设检验(Hypothesis Testing)

另一种度量不确定性的方法是假设检验:两组之间的平均值差异是否在统计上与零(或任何其他值)不同?要回答这类问题,需要记住两个独立正态分布的和或差也是正态分布。结果的平均值将是两个分布的和或差,而方差将始终是之前两个方差的和:
N ( μ 1 , σ 1 2 ) − N ( μ 2 , σ 2 2 ) = N ( μ 1 − μ 2 , σ 1 2 + σ 2 2 ) N ( μ 1 , σ 1 2 ) + N ( μ 2 , σ 2 2 ) = N ( μ 1 + μ 2 , σ 1 2 + σ 2 2 ) N(\mu_1,\sigma_1^2)-N(\mu_2,\sigma_2^2)=N(\mu_1-\mu_2,\sigma_1^2+\sigma_2^2)\\ N(\mu_1,\sigma_1^2)+N(\mu_2,\sigma_2^2)=N(\mu_1+\mu_2,\sigma_1^2+\sigma_2^2) N(μ1,σ12)N(μ2,σ22)=N(μ1μ2,σ12+σ22)N(μ1,σ12)+N(μ2,σ22)=N(μ1+μ2,σ12+σ22)

如果你不记得这个公式也没有关系,我们可以使用代码和模拟数据来核验一下:

import seaborn as sns
from matplotlib import pyplot as plt

np.random.seed(123)

n1 = np.random.normal(4, 3, 30000)
n2 = np.random.normal(1, 4, 30000)
n_diff = n2 - n1

plt.figure(figsize=(10,4))
sns.distplot(n1, hist=False, label="$N(4,3^2)$", color="0.0", kde_kws={"linestyle":linestyle[0]})
sns.distplot(n2, hist=False, label="$N(1,4^2)$", color="0.4", kde_kws={"linestyle":linestyle[1]})
sns.distplot(n_diff, hist=False,
             label=f"$N(-3, 5^2) = N(1,4^2) - (4,3^2)$", color="0.8", kde_kws={"linestyle":linestyle[1]})
plt.legend()

如果你取两组正态分布,使用一组减去另一组,会得到第三个分布。最终分布的均值是均值之差,而标准差是方差之和的平方根。因为我们讨论的是实验平均值的分布,所以我们可以把这些分布的标准偏差看作平均值的标准误差:
μ d i f f = μ 1 − μ 2 S E d i f f = S E 1 2 + S E 2 2 \mu_{diff}=\mu_1-\mu_2\\ SE_{diff}=\sqrt{SE_1^2+SE_2^2} μdiff=μ1μ2SEdiff=SE12+SE22

你可以在比较交叉销售电子邮件实验的转化率的问题中使用这个想法。如果你取两组的估算分布,比如短邮件组和无邮件组,然后使用一组减去另一组,你就得到了差值的分布。有了这个分布,你可以很容易地为均值之差构造一个95%的置信区间:

diff_mu = short_email.mean() - no_email.mean()
diff_se = np.sqrt(no_email.sem()**2 + short_email.sem()**2)

ci = (diff_mu - 1.96*diff_se, diff_mu + 1.96*diff_se)
print(f"95% CI for the differece (short email - no email):\n{ci}")

95% CI for the differece (short email - no email):
(0.01023980847439844, 0.15465380854687816)

x = np.linspace(diff_mu - 4*diff_se, diff_mu + 4*diff_se, 100)
y = stats.norm.pdf(x, diff_mu, diff_se)

plt.figure(figsize=(10,3))
plt.plot(x, y, lw=3)
plt.vlines(ci[1], ymin=0, ymax=4, ls="dotted")
plt.vlines(ci[0], ymin=0, ymax=4, ls="dotted", label="95% CI")
plt.xlabel("Diff. in Conversion (Short - No Email)\n")
plt.legend()
plt.subplots_adjust(bottom=0.15)

2.7.1 零假设(Null Hypothesis)

在这个区间内,你可以回答什么是零假设。例如,您可以声明一个假设:短电子邮件和完全没有电子邮件之间的转化率没有差异。你通常会使用 H 0 H_0 H0 来讨论零假设:
H 0 : C o n v e r s i o n n o e m a i l = C o n v e r s i o n s h o r t e m a i l H_0:Conversion_{no_email}=Conversion_{short_email} H0:Conversionnoemail=Conversionshortemail

一旦你有了这个假设,就该问自己:“如果零假设是真的,我是否有可能观察到这样的差异?”观察数据,看是否符合零假设。如果不符合,你会说看到这样的数据太奇怪了,如果零假设是真的,你应该排斥这样的数据。一种方法是用刚才构造的置信区间。

注意前面的95%置信区间是不包含零的。此外,回想一下,这是转化率差异的CI。由于零假设表明该差为零,但现在置信区间完全在零之外,因此可以说,如果零假设成立,看到这种结果的概率太低。因此,你可以有95%的置信度否决原假设。

显著性水平(Significance Level)

显著性水平用来评估统计结果是否具有统计学上的显著性的一个指标,就是零假设犯第一类错误的概率。显著性是在收集或分析数据之前设定的。为了达到一定的显著性水平,例如5%,需要在分析期间构建一个置信区间,例如95%。

当然,你也可以提出其他的零假设。例如,假设发送电子邮件需要一些成本,这是非常现实的,即使没有显著的金钱成本,如果你向客户发送过多的电子邮件,最终他们会将你标记为垃圾邮件发送者,这将关闭与他们的沟通渠道,导致未来的销售额下降。在这种情况下,可能只有在转化率提升超过1%的情况下,营销团队才愿意推出交叉销售电子邮件的方案。所以,你可以这样陈述零假设:“转化率的差异是1%。”为了验证这一假设,你所需要做的就是将均值之差减去1%来调整置信区间:

# shifting the CI
diff_mu_shifted = short_email.mean() - no_email.mean() - 0.01 
diff_se = np.sqrt(no_email.sem()**2 + short_email.sem()**2)

ci = (diff_mu_shifted - 1.96*diff_se, diff_mu_shifted + 1.96*diff_se)
print(f"95% CI 1% difference between (short email - no email):\n{ci}")

95% CI 1% difference between (short email - no email):
(0.00023980847439844521, 0.14465380854687815)

因为这个95% CI也大于零,所以你也可以否决这个零假设。然而这个95% CI非常接近于零,因果效应的可能性大概2%,你否决这个零假设的置信度没有95%。

非劣性检验(Noninferiority Testing)

在这本书中,大多数零假设将被陈述为一个等式(通常为零)。这种类型的零假设的动机是,只有在发现效果与零显著不同时才进行处理。然而,在某些情况下,只有当干预效果等于零时,才希望采取行动。例如,当营销的影响已经可以忽略不计(或不足以补偿其成本)时,你就会考虑停止营销活动。

不否决零假设 H 0 = 0 H_0=0 H0=0 不代表接受这个零假设。这就是著名的格言“缺失证据不是没有证据”。可以因为样本量太小、产生了一个大的置信区间,你可以简单地否决 H 0 = 0 H_0=0 H0=0

为了解决这个问题,统计学家创建了非劣性测试,是用来测试一种干预是否与另一种干预相同(或干预效果为零)的方法。基本的想法是看看置信区间是否包含零,同时也要确保它足够小。

2.7.2 检验统计量(Test Statistic)

除了置信区间,有时可以使用检验统计量(Test Statistic)否决零假设。测试统计量较高时,倾向于否决零假设。最常用的检验统计量之一是t统计量。它是对产生置信区间的分布进行标准化:
t Δ = μ Δ − H 0 S E Δ = ( μ 1 − μ 2 ) − H 0 σ 1 2 / n 1 + σ 2 2 / n 2 t_\Delta=\frac{\mu_\Delta-H_0}{SE_\Delta}=\frac{(\mu_1-\mu_2)-H_0}{\sqrt{\sigma_1^2/n_1+\sigma_2^2/n_2}} tΔ=SEΔμΔH0=σ12/n1+σ22/n2 (μ1μ2)H0
H 0 H_0 H0 是零假设的参数项。

注意,分子是观测到的平均差值和零假设之间的差值。如果零假设为真,则该分子的期望值为零: E ( μ Δ − H 0 ) = 0 E(\mu_\Delta-H_0)=0 E(μΔH0)=0。分母只是标准误差,使统计量标准化,具有单位方差,确保零假设成立时, t Δ t_\Delta tΔ 遵循标准正态分布 N ( 0 , 1 ) N(0,1) N(0,1)。由于 t Δ t_\Delta tΔ 在零假设成立时以零为中心,高于或低于1.96的值将是极不可能出现的(概率小于5%)。这意味着,如果看到如此极端的t统计量,也可以否决零假设。在我们的运行示例中,与无效果 H 0 H_0 H0 相关的统计量大于2,这意味着你可以在95%的置信度下否决它:

t_stat = (diff_mu - 0) / diff_se
t_stat

2.2379512318715364

此外,由于t统计量在零假设下是正态分布的,因此可以使用它轻松地计算p值。

T分布 vs 正态分布

从技术上讲,这里使用正态分布是不准确的。相反,你应该使用T分布,其自由度等于样本量减去你估算的参数数量(2,因为你比较的是两个平均值)。然而,在样本超过100的情况下,两者之间的区别几乎没有实际意义。

2.8 p值(p-values)

之前说,如果没有收到电子邮件和短电子邮件的客户的转化率相同,那么你观察到这种极端差异的可能性不到5%。但你能准确地估计出这个概率是多少吗?p值(p-values)能回答这个问题。

就像置信区间(事实上,也是大多数频率统计)一样,p值的真正定义可能非常令人困惑。因此,为了不冒任何风险,我们直接复制维基百科中的定义:“p值是在零假设成立的情况下,获得测试结果至少与测试中实际观察到的结果一样极端的概率。”

更简洁地说,p值是在零假设成立的情况下看到此类数据的概率,如下图。它衡量的是在零假设成立的情况下,你看到的测量结果的不可能性有多大。当然,这经常与零假设成立的概率混淆。注意这里的区别。p值不是 P ( H 0 ∣ d a t a ) P(H_0|data) P(H0data),而是 P ( d a t a ∣ H 0 ) P(data|H_0) P(dataH0)

x = np.linspace(-4, 4, 100)
y = stats.norm.pdf(x, 0, 1)

plt.figure(figsize=(10,4))
plt.plot(x, y, lw=2)
plt.vlines(t_stat, ymin=0, ymax=0.1, ls="dotted", label="T-Stat", lw=2)
plt.fill_between(x.clip(t_stat), 0, y, alpha=0.4, label="P-value")
plt.legend()

为了得到p值,你所需要做的就是在单侧零假设(“差异大于x”或“差异小于x”)的检验统计量之前计算标准正态分布下的面积,并在双侧零假设(“差异是x”)下将结果乘以2:

print("P-value:", (1 - stats.norm.cdf(t_stat))*2)

P-value: 0.025224235562152142

2.9 功效(Power)

到目前为止,你已经从一个看到已有测试数据的数据分析师的角度研究了这些统计学概念。你正在处理给定的数据。但如果你被要求设计一个实验,而不是仅仅阅读已经设计好的实验呢?在这种情况下,你需要确定为每个变体提供的样本。例如,如果您还没有进行交叉销售电子邮件的实验,需要决定应该给多少客户发送长电子邮件,给多少人发短电子邮件,多少人不会收到电子邮件?从这个角度来看,目标是拥有足够大的样本,以便能够正确地判断是否应该否决无效时的零假设。一个测试能正确否决零假设的概率被称为该测试的功效(Power)。这是一个有用的概念,不仅能帮助你弄清楚需要的实验样本量,而且能让你发现实验中可能存在的操作不当的问题。

功效与统计显著性密切相关。 α \alpha α 是零假设成立时,否决该零假设的概率;而功效 ( 1 − β ) (1-\beta) (1β) 是零假设不成立时,否决该零假设的概率。在某种意义上,功效也被定义为需要多少证据来正确地否决零假设。

回想一下95%置信区间是如何表示95%的实验将包含你试图估算的真实参数的。这也意味着他们中的5%不会被置信区间包含,将导致你在5%的情况下错误地否决零假设。在 α = 0.05 \alpha=0.05 α=0.05 的情况下,你需要 δ \delta δ(参数估算和零假设之间的差异)至少与零有1.96个SE的距离,才能得出它在统计上是显著的结论。这是因为 δ − 1.96 S E \delta-1.96SE δ1.96SE 是95%置信区间的下限。

所以你需要 δ − 1.96 S E > 0 \delta-1.96SE>0 δ1.96SE>0 来表明结果是显著的。但你有多大可能看到这种显著差异呢?这就是需要功效的地方。功效是正确否决零假设的概率,也就是 1 − β 1-\beta 1β,其中 β \beta β 是零假设不成立时,不否决该零假设的概率(假阴性的概率)。功效的行业标准是80%,这意味着当零假设不成立时,您只有20%( β = 0.2 \beta=0.2 β=0.2)的机会不否决该零假设。为了达到80%的功效,需要在零假设为假的情况下否决80%的零假设。因为否决零假设意味着 δ − 1.96 S E > 0 \delta-1.96SE>0 δ1.96SE>0,你需要在80%的情况下得到这么大的差异。换句话说,您需要让95% CI的下限在80%的情况下大于零。

95%置信区间的下限也遵循正态分布。和样本均值的分布一样,95% CI下限分布的方差等于SE,不过均值为 δ − 1.96 S E \delta-1.96SE δ1.96SE,是样本均值的分布平移了1.96个SE。因此,为了在80%的情况下保证 δ − 1.96 S E > 0 \delta-1.96SE>0 δ1.96SE>0(功效为80%),你需要差值与零的距离为 1.96 + 0.84 S E 1.96+0.84SE 1.96+0.84SE:1.96以提供95% CI,0.84以便该区间的下限在80%的时间内大于零。

stats.norm.cdf(0.84)

0.7995458067395503

2.10 样本量计算

看待这个问题的另一种方法是认识 δ \delta δ,是零假设和观察到的估算值之间的差异,如果零假设不成立,就一定可以观测到。当 α = 5 \alpha=5% α=5 1 − β = 80 1-\beta=80% 1β=80,可观测的效果由2.8SE=(1.96SE+0.85SE)决定。因此,如果你想要设计一个交叉销售的电子邮件实验,想要检测到1%的差异,那样本量必须能带来的差异至少为1%=2.8SE。如果你把表示效果的SE的公式打开, S E Δ = S E 1 2 + S E 2 2 SE_\Delta=\sqrt{SE_1^2+SE_2^2} SEΔ=SE12+SE22 。假设你现在是一个没有见过这个实验,但正在尝试设计这个实验的分析师,没有干预组的SE,但可以假设干涉组和对照组的方差是相同的, S E Δ = 2 S E 2 = 2 σ 2 / n = σ 2 / n SE_\Delta=\sqrt{2SE^2}=\sqrt{2\sigma^2/n}=\sigma\sqrt{2/n} SEΔ=2SE2 =2σ2/n =σ2/n 。将此代入可观测差异中,如果你想要80%的功效和95%的显著性,你最终会得到一个相当简单的公式,用于确定测试中每个变种的样本量:
δ = 2.8 σ 2 / n n = 2 ∗ 2. 8 2 σ 2 / δ 2 ≈ 16 σ 2 / δ 2 \delta=2.8\sigma\sqrt{2/n}\\ n=2*2.8^2\sigma^2/\delta^2\approx16\sigma^2/\delta^2 δ=2.8σ2/n n=22.82σ2/δ216σ2/δ2

δ \delta δ 是可观测差异, 2 ∗ 2. 8 2 2*2.8^2 22.82四舍五入为16。将这个公式应用到你的数据中,使用对照组的方差作为 σ 2 \sigma^2 σ2 的最佳猜测,你最终会得到以下所需的样本量:

# in the book it is np.ceil(16 * no_email.std()**2/0.01), but it is missing the **2 in the denominator.
np.ceil(16 * no_email.std()**2 / 0.1**2)

66.0

*这个地方δ不是应该为0.01吗?

data.groupby("cross_sell_email").size()

cross_sell_email
long              109
no_email        94
short             120
dtype: int64

这在实验设计方面当然是无价的,对于我们目前的交叉销售实验来说也是一个好消息。两个干预组有超过100个样本,对照组有94样本,表明这是一个数量够用的测试。

另请参阅

这个非常简单的计算样本量的方法是从Ron Kohavi等人撰写的论文《A/B Testing Intuition Busters: Common Misunderstandings in Online Controlled Experiments》(2022)中获取的。这个样本大小公式只是这篇论文中介绍的许多非常有趣和有用的内容之一,所以我绝对建议你查看一下。

2.11 要点总结

本章的思想是将因果识别与估算联系起来(并回顾一些重要的统计概念)。回想一下,因果推断的目标是从数据中了解因果量。这个过程的第一步是识别,就是使用关键假设,使用从数据中估算的可观测统计量来得到不可观测的因果量。

例如,ATE是一个因果量;它是由不可观测的潜在结果定义的, A T E = E ( Y 1 − Y 0 ) ATE=E(Y_1-Y_0) ATE=E(Y1Y0)。为了确定ATE,你使用了独立性假设 T ⊥ ( Y 0 , Y 1 ) T \perp (Y_0,Y_1) T(Y0,Y1),这允许你可以用可观察量 E ( Y ∣ T = 0 ) E(Y|T=0) E(YT=0) E ( Y ∣ T = 1 ) E(Y|T=1) E(YT=1) 来表示ATE。即在独立性假设下:
E ( Y 1 − Y 0 ) = E ( Y ∣ T = 1 ) − E ( Y ∣ T = 0 ) E(Y_1-Y_0)=E(Y|T=1)-E(Y|T=0) E(Y1Y0)=E(YT=1)E(YT=0)

你还看到了如何使用随机对照试验(RCT)使这一假设更加合理。如果你将干预随机化,你就是在强迫它独立于潜在的结果 Y t Y_t Yt

但识别只是因果推断的第一步。一旦你能够用统计量表示因果量,你仍然需要对这些统计量进行估算。例如,即使你可以用 E ( Y ∣ T = 0 ) E(Y|T=0) E(YT=0) E ( Y ∣ T = 1 ) E(Y|T=1) E(YT=1) 来表示ATE,你仍然需要估算 E ( Y ∣ T = 0 ) E(Y|T=0) E(YT=0) E ( Y ∣ T = 1 ) E(Y|T=1) E(YT=1)

本章的第二部分介绍了估算过程中使用的统计概念。具体来说,你了解了标准误差:
S E = σ / n SE=\sigma/\sqrt{n} SE=σ/n
也学会了将它置于 估算值 μ \mu μ 附件的置信区间中:
μ ^ ± z ∗ S E \hat\mu\pm z*SE μ^±zSE
其中 z z z 是正态分布中间 α \alpha% α 的质量的对应的系数。

你还学习了如何为两组之间的平均值差异构建置信区间,这归结为对这些组的方差求和并找到差异的标准误差:
S E d i f f = S E 1 2 + S E 2 2 SE_{diff}=\sqrt{SE_1^2+SE_2^2} SEdiff=SE12+SE22

最后,你了解了功效以及如何使用它来计算你希望运行的实验的样本量。具体来说,对于95%置信度和80%功效,你可以将样本量公式简化为:
N = 16 ∗ σ 2 / δ 2 N=16*\sigma^2/\delta^2 N=16σ2/δ2

*原书此处δ忘了加平方

其中 σ 2 \sigma^2 σ2 是结果的方差, δ \delta δ 是可观测差异。


系列文章专栏:
使用Python进行因果推断(Causal Inference in Python)

  第1章 因果推断导论
  第2章 随机实验与统计学回顾
  第3章 图形化因果模型
  第4章 线性回归的不合理有效性
  第5章 倾向分
  第6章 效果异质性
  第7章 元学习器
  第8章 双重差分

  持续更新中:
  第9章 综合控制
  第10章 Geo实验与Switchback实验
  第11章 不依从性与工具
  第12章 后续行动


【参考】

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MangoGO芒狗狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值