组织每天都会依据客户行为、产品表现和运营波动来做决策。为确保这些决策建立在数据中真实存在的关联和模式之上,分析师常常使用统计检验。
尽管常见的统计检验适用于大型数据集,但企业并不总是有幸拥有大样本。事实上,许多决策不得不基于小样本、细分客户群或罕见事件。在这些情况下,传统检验(如卡方检验 )可能力有不逮,要么给出误导性结果,要么缺乏足够精度,难以据此自信采取行动。
这正是 Fisher 精确检验发挥价值的地方。继续阅读,我将向您展示如何使用它。
什么是 Fisher 精确检验?
Fisher 精确检验用于在数据受限时评估分类变量之间的关联。它最常用于 2×2 列联表,计算在原假设(独立性)成立的前提下,观测到与已收集数据同等极端的数据的精确概率。
例如,在评估定向营销活动的有效性时,分析师可能会使用 Fisher 精确检验。若要比较电子邮件 与 社交媒体 活动、且样本来自小众客户群,分析师可能会构建列联表并运行 Fisher 精确检验。或者, 在质量控制场景中,分析师可以用该检验来处理事故报告和缺陷跟踪的挑战。某些失效类型仅发生数次时,Fisher 精确检验可用于判断如机器类型等因素是否与缺陷发生真正相关。
统计理论与数学基础
在形成对该检验重要性的直观理解后,我们可以加以形式化。Fisher 精确检验本质上是一种用于判断两个分类变量之间是否存在非随机关联的统计方法。它最常用于 2×2 列联表,比较两个群体在两个结局上的差异,例如“处理与 对照”对应“改善与 未改善”。
有趣的是,它的起源可追溯到统计学中广为人知的“女士品茶”实验。Ronald A. Fisher 爵士设计了一项实验,以检验 Muriel Bristol 博士是否能仅凭味觉分辨是一杯先倒牛奶还是先倒茶。作为实验的一部分,Fisher 准备了八杯茶,请她识别哪些是“先牛奶”与“先茶”。由于样本量很小,Fisher 提出了一个精确方法,用于判断正确猜测的模式是否可能偶然出现,这也为后来我们所知的 Fisher 精确检验奠定了基础。
在数据有限、不平衡或难以获取的情形(如临床研究)中,Fisher 检验有助于判断治疗与结局是否存在有意义的关联。生态学、流行病学、遗传学和质量控制等涉及稀疏数据的领域也依赖该检验。
Fisher 精确检验通过计 算超几何分布的精确概率来评估独立性。对于 2×2 表:
B1 组
B2 组
合计
A1 组
a
b
a+b
A2 组
c
d
c+d
合计
a+c
b+d
n
在原假设(无关联)下观测到该精确 配置的概率可通过超几何分布计算:
检验可以是单侧的,例如回答“在 B1 中 A1 是否比在 B2 中更可能出现?”,也可以是双侧的:“是否存在任何关联?”与大样本近似方法(如卡方)不同,Fisher 方法即便在期望频数很低时也能保证准确性。
假设与前提条件
要应用 Fisher 精确检验,需要满足以下条件。
数据必须是分类变量
以 2×2 列联表形式呈现
观测彼此独立
在原假设下行列边际(行列合计)固定
当表中任一格的期望计数极低(小于 5),或结果稀疏或严重不平衡时,优先选择 Fisher 检验。
在 Python 中进行 Fisher 精确检验
假设我们要检验某种药 物是否能改善康复:
已康复
未康复
合计
治疗组
8
2
10
对照组
4
11
15
合计
12
13
25
原假设与备择假设如下:
H₀:治疗与康复独立
H₁:治疗提升康复率(单侧)
在 Python 中可以这样处理:
from scipy. stats import fisher_exact
table = [ [ 8 , 2 ] ,
[ 4 , 11 ] ]
oddsratio, p_value = fisher_exact( table, alternative= 'two-sided' )
print ( "Odds Ratio:" , oddsratio)
print ( "Two-sided p-value:" , round ( p_value, 2 ) )
_, p_value_one = fisher_exact( table, alternative= 'greater' )
print ( "One-sided p-value:" , round ( p_value_one, 2 ) ) 技术支持
Odds Ratio: 11.0
Two- sided p- value: 0.02
One- sided p- value: 0.01 技术支持
较小的 p 值 0.02 表明治疗与康复之间存在真实关联。
现在,让我们使用公开的 NHANES 2017–2018 数据,考察“当前吸烟状态”与“是否曾被告知患有哮喘”在成人中的关联。
所用文件(均来自 NHANES 官方网站):
DEMO_J.XPT — 人口统计(年龄、性别、权重等)
SMQ_J.XPT — 吸烟 - 香烟使用(吸烟史与当前使用)
MCQ_J.XPT — 医疗状况(包括“是否曾被告知患有哮喘”)
我们将遵循以下步骤:
下载并载入数据集
创建二元变量:
current_smoker(是/否)
asthma_ever(是/否)
构建 2×2 列联表
运行 Fisher 精确检验(双侧与单侧)
计算比值比的 95% 置信区间
解释结果
注意 :规范的 NHANES 分析需要使用抽样权重和复杂设计。
此处为教学目的忽略权重。让我们导入所需库。
import pandas as pd
import numpy as np
from scipy. stats import fisher_exact
from statsmodels. stats. contingency_tables import Table2x2技术支持
请从 CDC 手动下载 XPT 文件,并将其放入本地 data/ 文件夹:
访问:
下载:
DEMO_J Data [XPT]
MCQ_J Data [XPT]
SMQ_J Data [XPT]
将它们保存为:
data/DEMO_J.XPT
data/MCQ_J.XPT
data/SMQ_J.XPT
DEMO_PATH = "data/DEMO_J.XPT"
MCQ_PATH = "data/MCQ_J.XPT"
SMQ_PATH = "data/SMQ_J.XPT"
demo = pd. read_sas( DEMO_PATH, format = "xport" )
mcq = pd. read_sas( MCQ_PATH, format = "xport" )
smq = pd. read_sas( SMQ_PATH, format = "xport" ) 技术支持
demo. head( ) 技术支持
mcq. head( ) 技术支持
smq. head( ) 技术支持
让我们查看这些 DataFrame 中的一些关键变量。此处 SEQN 为受访者 ID,可用作连接键。
print ( "DEMO columns (subset):" , demo. columns[ : 10 ] )
print ( "MCQ columns (subset):" , mcq. columns[ : 10 ] )
print ( "SMQ columns (subset):" , smq. columns[ : 10 ] ) 技术支持
DEMO columns ( subset) : Index( [ 'SEQN' , 'SDDSRVYR' , 'RIDSTATR' , 'RIAGENDR' , 'RIDAGEYR' , 'RIDAGEMN' ,
'RIDRETH1' , 'RIDRETH3' , 'RIDEXMON' , 'RIDEXAGM' ] ,
dtype= 'object' )
MCQ columns ( subset) : Index( [ 'SEQN' , 'MCQ010' , 'MCQ025' , 'MCQ035' , 'MCQ040' , 'MCQ050' , 'AGQ030' ,
'MCQ053' , 'MCQ080' , 'MCQ092' ] ,
dtype= 'object' )
SMQ columns ( subset) : Index( [ 'SEQN' , 'SMQ020' , 'SMD030' , 'SMQ040' , 'SMQ050Q' , 'SMQ050U' , 'SMD057' ,
'SMQ078' , 'SMD641' , 'SMD650' ] ,
dtype= 'object' ) 技术支持
让我们筛选所需列,并按年龄(大于等于 20 岁)过滤。
demo_sub = demo[ [ "SEQN" , "RIDAGEYR" , "RIAGENDR" ] ] . copy( )
mcq_sub = mcq[ [ "SEQN" , "MCQ010" ] ] . copy( )
smq_sub = smq[ [ "SEQN" , "SMQ040" ] ] . copy( )
df = (
demo_sub
. merge( mcq_sub, on= "SEQN" , how= "inner" )
. merge( smq_sub, on= "SEQN" , how= "inner" )
)
df = df[ df[ "RIDAGEYR" ] >= 20 ] . copy( )
df. head( ) 技术支持
MCQ010 表示哮喘相关状况,其中 1 代表是,2 代表否。类似地,SMQ040 显示受访者当前是否吸烟。
df[ "asthma_ever" ] = np. nan
df. loc[ df[ "MCQ010" ] == 1 , "asthma_ever" ] = 1
df. loc[ df[ "MCQ010" ] == 2 , "asthma_ever" ] = 0
df[ "current_smoker" ] = np. nan
df. loc[ df[ "SMQ040" ] . isin( [ 1 , 2 ] ) , "current_smoker" ] = 1
df. loc[ df[ "SMQ040" ] == 3 , "current_smoker" ] = 0
df_clean = df. dropna( subset= [ "asthma_ever" , "current_smoker" ] ) . copy( )
df_clean[ [ "RIDAGEYR" , "MCQ010" , "asthma_ever" , "SMQ040" , "current_smoker" ] ] . head( ) 技术支持
我们创建了新列 asthma_ever 和 current_smoker
让我们创建列联表,以便使用 Fisher 精确检验。
df_clean[ "asthma_ever" ] = df_clean[ "asthma_ever" ] . astype( int )
df_clean[ "current_smoker" ] = df_clean[ "current_smoker" ] . astype( int )
contingency = pd. crosstab(
df_clean[ "current_smoker" ] ,
df_clean[ "asthma_ever" ] ,
rownames= [ "Current smoker (1=yes,0=no)" ] ,
colnames= [ "Ever told asthma (1=yes,0=no)" ]
)
contingency技术支持
将该列联表转换为 NumPy 数组。
table = np. array( [
[ contingency. loc[ 0 , 0 ] , contingency. loc[ 0 , 1 ] ] ,
[ contingency. loc[ 1 , 0 ] , contingency. loc[ 1 , 1 ] ]
] )
table技术支持
array( [ [ 1102 , 226 ] ,
[ 832 , 172 ] ] ) 技术支持
最后,运行双侧和单侧的 Fisher 精确检验。
oddsratio, p_two_sided = fisher_exact( table, alternative= "two-sided" )
_, p_smoker_higher_asthma = fisher_exact( table, alternative= "greater" )
_, p_smoker_lower_asthma = fisher_exact( table, alternative= "less" )
print ( "2x2 table (rows: non-smoker, smoker; cols: no asthma, asthma)" )
print ( table)
print ( )
print ( f"Odds ratio (Fisher): { oddsratio: .3f } " )
print ( f"Two-sided p-value: { p_two_sided: .5f } " )
print ( f"One-sided p-value (H1: smokers have higher asthma odds): { p_smoker_higher_asthma: .5f } " )
print ( f"One-sided p-value (H1: smokers have lower asthma odds): { p_smoker_lower_asthma: .5f } " ) 技术支持
2x2 table ( rows: non- smoker, smoker; cols: no asthma, asthma)
[ [ 1102 226 ]
[ 832 172 ] ]
Odds ratio ( Fisher) : 1.008
Two- sided p- value: 0.95571
One- sided p- value ( H1: smokers have higher asthma odds) : 0.49274
One- sided p- value ( H1: smokers have lower asthma odds) : 0.55144 技术支持
较高的 p 值表明,在此样本中,没有证据显示当前吸烟与曾被诊断为哮喘之间存在统计学上显著的关联。
比值比约为 1,意味着当前吸烟者报告曾被诊断哮喘的几率与非吸烟者几乎相同。所见差异完全可能由随机因素造成。
与其他统计方法的比较
根据样本量、数据结构和假设条件,分析列联表关系可采用不同的统计检验:
Fisher 精确检验
最适合小样本及部分期望频数较低的情形。它提供精确的 p 值,因而非常可靠,但随着表规模变大,计算可能变慢。
卡方检验
大样本的标准选择。速度快、应用广泛、容易上手,但当期望计数低于 5 时,由于依赖近似而变得不准确。
Barnard 精确检验
在 2×2 表中较 Fisher 检验更有检验力的替代方案。它不假定固定边际,因此结果可能更敏感,但该方法在软件中的实现较少。
McNemar 检验
为配对或匹配的分类数据设计(如同一受试者治疗前后)。它考虑观测间的依赖性,但不能用于独立样本的情形。
方法
适用场景
优点
局限
Fisher 精确检验
小样本、期望计数低
精确 p 值
偏保守,大表计算慢
卡方检验
大样本
快速、广泛使用
当期望计数 < 5 时有偏
Barnard 精确检验
Fisher 的替代方案,检验力更强
不受固定边际限制
实现较少
McNemar 检验
配对分类数据
可处理依赖性
不用于独立样本
计算层面的考量
Fisher 精确检验的性能可能令人担忧,因为超出简单 2×2 列联表后,计算扩展性较差。尽管 Freeman–Halton 扩展允许将检验应用到 2×3 和 3×3 表,但随着表规模增大,计算成本会迅速上升。因此,在遗传学和临床研究等领域,往往需要在许多小而复杂的数据集上反复执行精确检验,此时高效算法与优化尤为重要。
结果解读与统计报告
在解释并报告 Fisher 精确检验结果时,重要的是报告精确 p 值、给出比值比及其 95% 置信区间,并明确说明进行的是单侧还是双侧检验。还应结合实际背景进行阐释,帮助读者理解发现的现实意义。例如,可以这样写:“Fisher 精确检验显示治疗与康复之间存在显著关联(OR=11.0,p=0.02,双侧)。”
扩展与进阶应用
Fisher 精确检验的扩展与高级应用有助于应对更复杂的数据情境,并在处理小样本或稀疏数据时提升准确性。Freeman–Halton 扩展可将检验应用到超出标准 2×2 格式的更大列联表,如 2×3 或 3×3。mid-P 精确检验相较 Fisher 更不保守,可在保持可靠性的同时提高统计功效。贝叶斯精确检验将先验知识纳入分析,当数据有限或专家判断可引导推断时尤其有用。此外,比值比的偏差校正方法可在小样本或出现零计数的研究中提供更准确的效应量估计,从而更好地解释关联的强度与方向。
局限与方法学注意事项
Fisher 精确检验可能偏保守,这会降低统计功效,使得在小样本研究中更难检测到真实效应。当数据非常稀疏时,结果解释也更具挑战性,因为置信区间会变宽、估计更不稳定。此外,在许多变量上反复应用该检验会增加伪发现风险,因此需谨慎进行多重检验校正。
列联表的可视化
用于呈现列联表结果的良好可视化方式包括堆叠条形图、并列条形图和马赛克图。这些图形有助于清晰传达分类变量之间的关系,并使模式或群体差异更易解读。
以下是一个 Python 示例:
import seaborn as sns
import matplotlib. pyplot as plt
import pandas as pd
df = pd. DataFrame( {
"Outcome" : [ "Recovered" , "Not Recovered" ] * 2 ,
"Count" : [ 8 , 2 , 4 , 11 ] ,
"Group" : [ "Treatment" ] * 2 + [ "Control" ] * 2
} )
sns. barplot( data= df, x= "Outcome" , y= "Count" , hue= "Group" )
plt. title( "Treatment Outcomes" )
plt. show( ) 技术支持
结语
Fisher 精确检验仍然是分析小型分类数据集的金标准。从花园中的一杯茶开始,它已经发展为医学、遗传学、人类研究与商业实验等领域重要且广泛使用的工具。
报名参加我们的 Python 统计基础 技能路径,继续学习假设检验等主题,并通过线性回归与逻辑回归练习统计建模。
组织每天都会依据客户行为、产品表现和运营波动来做决策。为确保这些决策建立在数据中真实存在的关联和模式之上,分析师常常使用统计检验。
尽管常见的统计检验适用于大型数据集,但企业并不总是有幸拥有大样本。事实上,许多决策不得不基于小样本、细分客户群或罕见事件。在这些情况下,传统检验(如卡方检验)可能力有不逮,要么给出误导性结果,要么缺乏足够精度,难以据此自信采取行动。
这正是 Fisher 精确检验发挥价值的地方。继续阅读,我将向您展示如何使用它。
什么是 Fisher 精确检验?
Fisher 精确检验用于在数据受限时评估分类变量之间的关联。它最常用于 2×2 列联表,计算在原假设(独立性)成立的前提下,观测到与已收集数据同等极端的数据的精确概率。
例如,在评估定向营销活动的有效性时,分析师可能会使用 Fisher 精确检验。若要比较电子邮件 与 社交媒体活动、且样本来自小众客户群,分析师可能会构建列联表并运行 Fisher 精确检验。或者,在质量控制场景中,分析师可以用该检验来处理事故报告和缺陷跟踪的挑战。某些失效类型仅发生数次时,Fisher 精确检验可用于判断如机器类型等因素是否与缺陷发生真正相关。
统计理论与数学基础
在形成对该检验重要性的直观理解后,我们可以加以形式化。Fisher 精确检验本质上是一种用于判断两个分类变量之间是否存在非随机关联的统计方法。它最常用于 2×2 列联表,比较两个群体在两个结局上的差异,例如“处理与对照”对应“改善与未改善”。
有趣的是,它的起源可追溯到统计学中广为人知的“女士品茶”实验。Ronald A. Fisher 爵士设计了一项实验,以检验 Muriel Bristol 博士是否能仅凭味觉分辨是一杯先倒牛奶还是先倒茶。作为实验的一部分,Fisher 准备了八杯茶,请她识别哪些是“先牛奶”与“先茶”。由于样本量很小,Fisher 提出了一个精确方法,用于判断正确猜测的模式是否可能偶然出现,这也为后来我们所知的 Fisher 精确检验奠定了基础。
在数据有限、不平衡或难以获取的情形(如临床研究)中,Fisher 检验有助于判断治疗与结局是否存在有意义的关联。生态学、流行病学、遗传学和质量控制等涉及稀疏数据的领域也依赖该检验。
Fisher 精确检验通过计算超几何分布的精确概率来评估独立性。对于 2×2 表:
B1 组
B2 组
合计
A1 组
a
b
a+b
A2 组
c
d
c+d
合计
a+c
b+d
n
在原假设(无关联)下观测到该精确配置的概率可通过超几何分布计算:
检验可以是单侧的,例如回答“在 B1 中 A1 是否比在 B2 中更可能出现?”,也可以是双侧的:“是否存在任何关联?”与大样本近似方法(如卡方)不同,Fisher 方法即便在期望频数很低时也能保证准确性。
假设与前提条件
要应用 Fisher 精确检验,需要满足以下条件。
当表中任一格的期望计数极低(小于 5),或结果稀疏或严重不平衡时,优先选择 Fisher 检验。
在 Python 中进行 Fisher 精确检验
假设我们要检验某种药物是否能改善康复:
已康复
未康复
合计
治疗组
8
2
10
对照组
4
11
15
合计
12
13
25
原假设与备择假设如下:
在 Python 中可以这样处理:
较小的 p 值 0.02 表明治疗与康复之间存在真实关联。
现在,让我们使用公开的 NHANES 2017–2018 数据,考察“当前吸烟状态”与“是否曾被告知患有哮喘”在成人中的关联。
所用文件(均来自 NHANES 官方网站):
DEMO_J.XPT— 人口统计(年龄、性别、权重等)SMQ_J.XPT— 吸烟 - 香烟使用(吸烟史与当前使用)MCQ_J.XPT— 医疗状况(包括“是否曾被告知患有哮喘”)我们将遵循以下步骤:
下载并载入数据集
创建二元变量:
current_smoker(是/否)asthma_ever(是/否)构建 2×2 列联表
运行 Fisher 精确检验(双侧与单侧)
计算比值比的 95% 置信区间
解释结果
注意:规范的 NHANES 分析需要使用抽样权重和复杂设计。
此处为教学目的忽略权重。让我们导入所需库。
请从 CDC 手动下载 XPT 文件,并将其放入本地
data/文件夹:访问:
人口统计:2017–2018 人口统计数据(DEMO_J)
医疗状况:MCQ_J
吸烟 - 香烟使用:SMQ_J
下载:
DEMO_J Data [XPT]
MCQ_J Data [XPT]
SMQ_J Data [XPT]
将它们保存为:
data/DEMO_J.XPTdata/MCQ_J.XPTdata/SMQ_J.XPT让我们查看这些 DataFrame 中的一些关键变量。此处
SEQN为受访者 ID,可用作连接键。让我们筛选所需列,并按年龄(大于等于 20 岁)过滤。
MCQ010表示哮喘相关状况,其中1代表是,2代表否。类似地,SMQ040显示受访者当前是否吸烟。我们创建了新列
asthma_ever和current_smoker让我们创建列联表,以便使用 Fisher 精确检验。
将该列联表转换为 NumPy 数组。
最后,运行双侧和单侧的 Fisher 精确检验。
较高的 p 值表明,在此样本中,没有证据显示当前吸烟与曾被诊断为哮喘之间存在统计学上显著的关联。
比值比约为 1,意味着当前吸烟者报告曾被诊断哮喘的几率与非吸烟者几乎相同。所见差异完全可能由随机因素造成。
与其他统计方法的比较
根据样本量、数据结构和假设条件,分析列联表关系可采用不同的统计检验:
Fisher 精确检验
最适合小样本及部分期望频数较低的情形。它提供精确的 p 值,因而非常可靠,但随着表规模变大,计算可能变慢。
卡方检验
大样本的标准选择。速度快、应用广泛、容易上手,但当期望计数低于 5 时,由于依赖近似而变得不准确。
Barnard 精确检验
在 2×2 表中较 Fisher 检验更有检验力的替代方案。它不假定固定边际,因此结果可能更敏感,但该方法在软件中的实现较少。
McNemar 检验
为配对或匹配的分类数据设计(如同一受试者治疗前后)。它考虑观测间的依赖性,但不能用于独立样本的情形。
方法
适用场景
优点
局限
Fisher 精确检验
小样本、期望计数低
精确 p 值
偏保守,大表计算慢
卡方检验
大样本
快速、广泛使用
当期望计数 < 5 时有偏
Barnard 精确检验
Fisher 的替代方案,检验力更强
不受固定边际限制
实现较少
McNemar 检验
配对分类数据
可处理依赖性
不用于独立样本
计算层面的考量
Fisher 精确检验的性能可能令人担忧,因为超出简单 2×2 列联表后,计算扩展性较差。尽管 Freeman–Halton 扩展允许将检验应用到 2×3 和 3×3 表,但随着表规模增大,计算成本会迅速上升。因此,在遗传学和临床研究等领域,往往需要在许多小而复杂的数据集上反复执行精确检验,此时高效算法与优化尤为重要。
结果解读与统计报告
在解释并报告 Fisher 精确检验结果时,重要的是报告精确 p 值、给出比值比及其 95% 置信区间,并明确说明进行的是单侧还是双侧检验。还应结合实际背景进行阐释,帮助读者理解发现的现实意义。例如,可以这样写:“Fisher 精确检验显示治疗与康复之间存在显著关联(OR=11.0,p=0.02,双侧)。”
扩展与进阶应用
Fisher 精确检验的扩展与高级应用有助于应对更复杂的数据情境,并在处理小样本或稀疏数据时提升准确性。Freeman–Halton 扩展可将检验应用到超出标准 2×2 格式的更大列联表,如 2×3 或 3×3。mid-P 精确检验相较 Fisher 更不保守,可在保持可靠性的同时提高统计功效。贝叶斯精确检验将先验知识纳入分析,当数据有限或专家判断可引导推断时尤其有用。此外,比值比的偏差校正方法可在小样本或出现零计数的研究中提供更准确的效应量估计,从而更好地解释关联的强度与方向。
局限与方法学注意事项
Fisher 精确检验可能偏保守,这会降低统计功效,使得在小样本研究中更难检测到真实效应。当数据非常稀疏时,结果解释也更具挑战性,因为置信区间会变宽、估计更不稳定。此外,在许多变量上反复应用该检验会增加伪发现风险,因此需谨慎进行多重检验校正。
列联表的可视化
用于呈现列联表结果的良好可视化方式包括堆叠条形图、并列条形图和马赛克图。这些图形有助于清晰传达分类变量之间的关系,并使模式或群体差异更易解读。
以下是一个 Python 示例:
结语
Fisher 精确检验仍然是分析小型分类数据集的金标准。从花园中的一杯茶开始,它已经发展为医学、遗传学、人类研究与商业实验等领域重要且广泛使用的工具。
报名参加我们的 Python 统计基础技能路径,继续学习假设检验等主题,并通过线性回归与逻辑回归练习统计建模。