为什么 R 的 fisher.test() 在 Haldane correction 后仍返回 Inf?
为什么加上 0.5 后仍然返回 Inf?
原因有两个:fisher.test() 会将非整数计数取整,其中 0.5 取整后为 0;它返回的 odds ratio 采用条件最大似然估计,和手动计算的校正后交叉乘积比不同。
约 3.8748 的计算结果是正确的,只是它与 fisher.test() 的 estimate 使用了不同的数据处理和估计方法。
加入的 0.5 在检验时被取整了
示例代码加上 0.5 后,传入的表是:
[,1] [,2]
[1,] 1.5 387.5
[2,] 0.5 500.5
Fisher 精确检验使用非负整数计数。R 会先将 data.frame 转为矩阵,对非整数计数调用 round(),并发出取整警告。具体实现见 fisher.test() 源码。
R 的 round() 会将恰好位于两个整数中间的数取整到偶数,所以 round(0.5) 为 0,round(1.5) 为 2。参见 R 的取整规则。
用以下代码可以查看这个例子的取整结果:
tab <- matrix(c(1, 387, 0, 500), nrow = 2, byrow = TRUE)
corrected <- tab + 0.5
round(corrected)
# [,1] [,2]
# [1,] 2 388
# [2,] 0 500
fisher.test() 实际使用的表中仍有一个 0,原来的 1 和 387 也发生了变化。加上 0.5 后,检验并没有使用小数计数。
为什么条件最大似然估计是 Inf?
对于以下 2 × 2 表:
第一列 第二列
第一行 a b
第二行 c d
直接计算的样本 odds ratio 为 a*d/(b*c)。你希望得到的是对四个单元格都加上 0.5 后的结果:
OR_corrected = ((a + 0.5) * (d + 0.5)) /
((b + 0.5) * (c + 0.5))
fisher.test() 返回的 estimate 则是固定行列边际总数后的条件最大似然估计。即使所有计数都是正整数,它也不一定等于直接计算的 a*d/(b*c)。R 官方文档说明了两者的区别。
在本例取整后的表中,第一列合计为 2,两个观测都位于第一行。固定边际总数后,左上角计数达到可能的最大值,条件似然的最大值因此位于 OR 趋向无穷大的边界,返回 Inf;对应的精确置信区间上限也可以是 Inf。
应该怎样计算?
如果需要 Fisher 精确检验的 p 值,应使用原始整数计数。如果还需要一个采用 Haldane correction 的有限 OR,可以单独计算,并标明估计方法:
# 原始计数
tab <- matrix(c(1, 387, 0, 500), nrow = 2, byrow = TRUE)
# Fisher 精确检验:使用原始计数
ft <- fisher.test(tab)
ft$p.value
ft$estimate
ft$conf.int
# Haldane correction:单独计算校正后的 OR
corrected <- tab + 0.5
or_corrected <- (corrected[1, 1] * corrected[2, 2]) /
(corrected[1, 2] * corrected[2, 1])
or_corrected
# 约为 3.874839
原始表中的零计数不会阻止 Fisher 精确检验执行;在这个例子中,条件 OR 估计仍可以为 Inf。有限的校正后 OR 来自另一种估计方式,报告时需要分别说明。
报告结果时注意什么?
- 题目中的
p-value = 0.1917来自所展示的校正后调用,不能直接当作原始计数的检验结果;应使用fisher.test(tab)重新计算。 - 不要将
ft$conf.int标注成校正后 OR 的置信区间。它对应 Fisher 条件推断,区间上限为Inf可以是合理结果。 - 不要通过增大伪计数或把整张表乘以某个数来强行获得有限结果,这会改变用于推断的计数和边际总数。
- 示例调用使用
test_df,日志却显示data: temp_df,复现时应确认输出确实来自同一个对象。
备注:内容仅供参考。
