关于数据 本指南中的所有记录和结果均为模拟数据。所有示例均可重复生成,且不包含任何可识别个人身份的健康信息。模拟出的关联仅用于教学,不能作为真实人群的证据。
各章均遵循相同的学习脉络:为何这一概念重要、核心概念、公共卫生示例、R 代码以及结果解读检查。请先阅读讲解,再运行或调整代码,最后展开答案面板。代码默认显示,可通过页面上的代码折叠按钮收起。
你不必记住每一个公式。请着重培养以下三个习惯:
生物统计学将统计推理应用于健康、疾病、卫生服务和人群相关问题。它并非一系列检验方法的简单集合,而是一条严谨的路径:从问题出发,形成证据,再据此作出审慎恰当的决策。
存在哪些健康事件、暴露和分布模式?
具有实际意义的组别或时间段之间差异有多大?
样本能够告诉我们目标总体的哪些信息?
一个实用的工作流程是:
问题 → 设计 → 测量 → 数据检查 → 描述 → 估计 → 不确定性 → 解读 → 行动
统计方法无法弥补问题定义模糊、抽样有偏或测量质量低下等缺陷。这些关键决策早在计算 p 值之前就已作出。
假设某城市调查了 520 名成年人,以估计流感疫苗接种覆盖率。
| 术语 | 含义 | 示例 |
|---|---|---|
| 目标总体 | 研究问题所指向的完整群体 | 本季居住在该市、未居住于机构中的所有成年人 |
| 样本 | 实际观察到的人群 | 参与调查的 520 人 |
| 参数 | 固定但通常未知的总体数量特征 | 所有目标成年人中的真实疫苗接种比例 |
| 统计量/估计值 | 根据样本计算得到的数量特征 | 调查中观察到的疫苗接种比例 |
| 目标估计量 | 对拟估计对象的精确定义 | 本季目标总体中的疫苗接种覆盖率 |
观察单位同样至关重要。数据表中的一行可能代表一个人、一个家庭、一家诊所、一个社区或一个人日。所用方法必须与该观察单位相匹配,并妥善处理可能存在的聚集性。
在打开 R 之前,请先明确写出:
问题示例 在冬季开始时居住于该市的成年人中(总体),已接种疫苗者与未接种疫苗者相比(比较),12 周内发生实验室确诊呼吸道感染(结局)的风险是多少?结果以风险比表示(目标估计量)。
在 800 名高中生的随机样本中,18% 报告目前使用电子烟。这里的参数是什么?
答案:参数是所定义目标总体中目前使用电子烟的学生真实比例。观察到的 18% 是样本统计量。它能否良好估计该参数,取决于抽样、测量和未应答情况。变量所扮演的角色取决于研究问题。
| 变量类型 | 记录内容 | 公共卫生示例 | 常用汇总指标 |
|---|---|---|---|
| 名义分类变量 | 无顺序的类别标签 | 社区、诊所 | 频数和比例 |
| 有序分类变量 | 有顺序但类别间距未知的标签 | 自评健康:从差到极好 | 频数、比例、中位类别 |
| 二分类变量 | 两个类别 | 是否接种疫苗:是/否 | 频数和比例 |
| 离散计数变量 | 非负的事件次数 | 一年内急诊就诊次数 | 均值、方差、率;通常使用计数模型 |
| 连续变量 | 测得的数值 | 血压、年龄、BMI | 均值/标准差或中位数/四分位距 |
| 事件发生时间变量 | 至事件发生或删失的时间 | 至复发的时间 | 生存概率、风险率、中位生存时间 |
不要仅凭变量的存储格式选择汇总方法。以数字存储的邮政编码仍是分类变量,五级评分通常是有序变量,而非真正的连续变量。
| 设计 | 如何开始 | 适用目的 | 主要注意事项 |
|---|---|---|---|
| 横断面调查 | 在某一时点或较短时间段内抽取人群样本 | 患病率和当前分布模式 | 暴露与结局的时间先后可能不清楚 |
| 队列研究 | 按暴露情况分组并随访结局 | 发病、风险、率及时间顺序 | 失访和混杂 |
| 病例对照研究 | 抽取病例和对照,再评估既往暴露 | 罕见结局或潜伏期较长的结局 | 选择偏倚/回忆偏倚;无法从抽样所得表格直接估计风险 |
| 随机试验 | 随机分配干预 | 试验条件下的干预效应 | 依从性、失访、伦理及结果的可推广性 |
| 整群随机试验 | 将学校、诊所或社区随机分组 | 人群层面的项目 | 同一群组内的结局彼此相关 |
| 监测系统 | 持续收集已明确定义的事件 | 趋势、信号和项目监测 | 病例定义、检测方式和完整性的变化 |
研究设计决定结果解读 横断面研究中的关联无法确定变量出现的时间先后。病例对照研究所得优势比在其抽样设计下是有效的,但样本中的病例比例并非疾病患病率。分析聚集数据时,需要采用能够处理群组内相似性的方法。
三者是不同的问题:
| 问题 | 通俗含义 | 示例 | 常见应对方式 |
|---|---|---|---|
| 随机性 | 不同随机样本所得结果会有所不同 | 一个样本中的覆盖率为 68%,另一个样本中为 71% | 用标准误和置信区间量化 |
| 选择偏倚 | 是否被纳入研究与研究问题中的重要变量有关 | 网络调查遗漏了无法上网的居民 | 改进抽样和招募;评估未应答 |
| 信息偏倚 | 暴露或结局的测量方式存在差异或测量不准确 | 病例对既往暴露的回忆比对照更完整 | 尽可能采用有效、标准化且设盲的测量方法 |
| 混杂 | 一个共同原因造成或扭曲暴露与结局之间的关联 | 年龄同时影响疫苗接种和感染风险 | 依据专业知识与因果推理进行研究设计和分析 |
混杂并非简单的“任意第三变量”,也不能仅凭较小的 p 值来判定。只有在协变量、模型、测量和因果假设均恰当时,调整才有助于减少混杂。
研究者选取 300 名肺癌患者和 300 名未患肺癌者,并询问他们 30 年前的职业性石棉暴露情况。
答案:这是一项病例对照研究。病例与对照对久远暴露史的回忆或重建可能存在差异,从而造成信息偏倚;对照的选择也可能造成选择偏倚。对于这种抽样所得的 2×2 表,优势比是自然的关联指标。下面展示模拟教学数据集的前几行。切勿在检查变量名、单位、类别、不可能值、重复记录和缺失情况之前就直接进行统计检验。
## [1] 520 10
head(
ph_data[c(
"participant_id", "age", "neighborhood", "smoking_status",
"physical_activity_min_week", "systolic_bp"
)],
6
)| participant_id | age | neighborhood | smoking_status | physical_activity_min_week | systolic_bp |
|---|---|---|---|---|---|
| P001 | 20 | North | Not current | 80 | 114 |
| P002 | 27 | South | Current | 81 | 110 |
| P003 | 58 | Central | Not current | 76 | 130 |
| P004 | 28 | Central | Not current | 6 | 116 |
| P005 | 35 | Central | Not current | 284 | 98 |
| P006 | 64 | West | Not current | 68 | 124 |
## participant_id age neighborhood
## 0 0 0
## smoking_status physical_activity_min_week bmi
## 0 18 0
## systolic_bp access_to_care vaccinated
## 0 0 0
## respiratory_infection
## 0
缺失的身体活动值编码为
NA,而不是零。零表示测得的活动量为零;NA
表示该值未知。
如果分布大致对称,且均值与标准差能够回答研究问题,可使用均值和标准差。对于明显偏斜的分布,或当典型排序位置更有意义时,应使用中位数和四分位距。无论如何,都应先查看数据分布。
summary_table <- data.frame(
Variable = c("年龄(岁)", "收缩压(mmHg)",
"身体活动(分钟/周)"),
N_observed = c(sum(!is.na(ph_data$age)),
sum(!is.na(ph_data$systolic_bp)),
sum(!is.na(ph_data$physical_activity_min_week))),
Mean = c(mean(ph_data$age), mean(ph_data$systolic_bp),
mean(ph_data$physical_activity_min_week, na.rm = TRUE)),
SD = c(sd(ph_data$age), sd(ph_data$systolic_bp),
sd(ph_data$physical_activity_min_week, na.rm = TRUE)),
Median = c(median(ph_data$age), median(ph_data$systolic_bp),
median(ph_data$physical_activity_min_week, na.rm = TRUE)),
IQR = c(IQR(ph_data$age), IQR(ph_data$systolic_bp),
IQR(ph_data$physical_activity_min_week, na.rm = TRUE))
)
knitr::kable(
summary_table,
digits = 1,
col.names = c("变量", "观测数", "均值", "标准差", "中位数", "四分位距"),
caption = "模拟参与者数据的描述性统计"
)| 变量 | 观测数 | 均值 | 标准差 | 中位数 | 四分位距 |
|---|---|---|---|---|---|
| 年龄(岁) | 520 | 44 | 15.5 | 44 | 23 |
| 收缩压(mmHg) | 520 | 124 | 14.4 | 123 | 19 |
| 身体活动(分钟/周) | 502 | 119 | 97.3 | 97 | 112 |
old_par <- par(mfrow = c(1, 2), mar = c(4.2, 4.2, 2.2, 1), las = 1)
hist(
ph_data$systolic_bp,
breaks = "FD",
col = palette_ph["sky"],
border = "white",
main = "收缩压分布",
xlab = "收缩压(mmHg)",
ylab = "参与者人数"
)
boxplot(
systolic_bp ~ smoking_status,
data = ph_data,
col = c(palette_ph["teal"], palette_ph["orange"]),
border = palette_ph["navy"],
names = c("非当前吸烟者", "当前吸烟者"),
main = "不同吸烟状态的收缩压",
xlab = "吸烟状态",
ylab = "收缩压(mmHg)"
)在该模拟样本中,收缩压大致呈单峰分布,其分布因当前吸烟状态而有所不同。
图形呈现的诚信原则 应标明单位,说明百分比所对应的分母,保持时间顺序,并解释数据排除情况。截断纵轴可能夸大小幅差异。三维效果和彩虹色板会增加视觉干扰,并可能降低可访问性。
始终应说明总体、时间窗口、分子和分母。“率为 12%”这种表述含义不清,因为百分数通常描述比例,而率通常以人时为单位。
vaccination_counts <- table(ph_data$vaccinated)
vaccination_summary <- data.frame(
Vaccinated = names(vaccination_counts),
Count = as.vector(vaccination_counts),
Proportion = as.vector(vaccination_counts) / sum(vaccination_counts)
)
knitr::kable(
vaccination_summary,
digits = 3,
col.names = c("疫苗接种状态", "频数", "比例"),
caption = "模拟样本中的疫苗接种状态"
)| 疫苗接种状态 | 频数 | 比例 |
|---|---|---|
| No | 151 | 0.29 |
| Yes | 369 | 0.71 |
急诊科等待时间明显右偏,因为少数患者需要等待数小时。均值/标准差与中位数/四分位距相比,哪一组指标更有信息价值?
答案:对于明显偏斜的分布,中位数和四分位距通常更能代表其集中趋势和离散程度。如果标注清楚,补充报告特定百分位数或将均值作为具有政策意义的附加指标,仍可能有用。概率的取值范围为 0(不可能)到 1(必然)。对于事件 :
条件概率 是在满足 的个体中发生 的概率。分母的界定至关重要。例如,灵敏度是在真正患有该病的人群中检测结果为阳性的概率。
如果得知事件 与 中的一个发生,不会改变另一个事件发生的概率,则二者相互独立:
独立性是需要论证的假设,而不是默认条件。同一个人的重复观测,以及同一家庭成员的观测,往往彼此相关。
| 分布 | 可表示的情形 | 关键条件 |
|---|---|---|
| 二项分布 | 在固定数量、暴露情况相近的人群中的感染人数 | 试验次数固定、每次只有两种结果、概率相同、各次试验相互独立 |
| 正态分布 | 对称的连续测量值或抽样分布 | 在适当情境下可用钟形曲线近似 |
| 泊松分布 | 指定时间或空间范围内的事件数 | 事件以近似稳定的速率发生;其均值与方差的关系可能过于严格 |
真实数据可能不满足这些条件。例如,传染病例会出现聚集,其变异程度可能超过简单泊松模型所允许的范围。
old_par <- par(mfrow = c(1, 3), mar = c(4, 3.8, 2.4, 0.8), las = 1)
x_bin <- 0:12
plot(x_bin, dbinom(x_bin, size = 12, prob = 0.20), type = "h", lwd = 6,
lend = 1, col = palette_ph["blue"],
xlab = "12 人中的病例数", ylab = "概率", main = "二项分布")
x_norm <- seq(-3.5, 3.5, length.out = 300)
plot(x_norm, dnorm(x_norm), type = "l", lwd = 3,
col = palette_ph["teal"],
xlab = "标准化值", ylab = "密度", main = "正态分布")
x_pois <- 0:14
plot(x_pois, dpois(x_pois, lambda = 4), type = "h", lwd = 6,
lend = 1, col = palette_ph["vermillion"],
xlab = "每日事件数", ylab = "概率", main = "泊松分布")二项、正态和泊松概率模型示例。每种模型回答的问题类型各不相同。
如果反复抽取随机样本并计算某个估计量,这些估计值便构成一个抽样分布。该分布的标准差就是估计量的标准误(SE)。对于来自方差有限的同一总体且相互独立的观测,样本均值的标准误估计为:
标准差(SD)描述的是观测值之间的变异;标准误(SE)描述的是估计值的不确定性。二者不能互换。
由于我们只有一个样本,真实的抽样分布未知。非参数自助法通过按照原样本量,从观测数据中反复进行有放回抽样来近似该分布。此时经验样本暂时替代未知总体;这种近似无法纠正有偏或缺乏代表性的样本。
set.seed(410)
bootstrap_means <- replicate(
1000,
mean(sample(ph_data$systolic_bp, size = nrow(ph_data), replace = TRUE))
)
hist(bootstrap_means, breaks = 24, col = palette_ph["sky"], border = "white",
main = "均值的自助抽样分布",
xlab = "自助样本中的平均收缩压",
ylab = "自助样本数")
abline(v = mean(ph_data$systolic_bp), col = palette_ph["vermillion"],
lwd = 3, lty = 2)
legend("topright", legend = "观测样本均值", lty = 2, lwd = 3,
col = palette_ph["vermillion"], bty = "n")样本均值的自助抽样分布以观测均值附近为中心。虚线表示观测到的样本均值。
由于标准误通常按 的比例减小,将样本量增加到原来的四倍,大约只能使标准误减半,而不是降为四分之一。对于聚类、配对、加权或其他存在依赖关系的观测,这个简单公式不能原样套用;标准误的计算必须反映研究设计。
se_example <- data.frame(
Sample_size = c(25, 100, 400),
Assumed_SD = 16,
Standard_error = 16 / sqrt(c(25, 100, 400))
)
knitr::kable(se_example, digits = 1,
col.names = c("样本量", "假定的标准差", "标准误"),
caption = "标准差为 16 时,样本量如何改变标准误")| 样本量 | 假定的标准差 | 标准误 |
|---|---|---|
| 25 | 16 | 3.2 |
| 100 | 16 | 1.6 |
| 400 | 16 | 0.8 |
某报告称:“平均收缩压为 128 mmHg(SD 17)。”这里的 17 描述的是什么?
答案: 它描述的是参与者血压值围绕样本均值的变异程度,并不直接表示总体均值估计的不确定性;后者由标准误描述。点估计是根据数据得到的单一最佳估计值。区间估计用于表达抽样不确定性。95% 置信区间(CI)由估计值、标准误以及适当的参考分布共同确定。
在常见条件下,均值的置信区间为:
# 基于 t 分布计算平均收缩压的置信区间。
n_bp <- sum(!is.na(ph_data$systolic_bp))
mean_bp <- mean(ph_data$systolic_bp, na.rm = TRUE)
se_bp <- sd(ph_data$systolic_bp, na.rm = TRUE) / sqrt(n_bp)
ci_mean_bp <- mean_bp + c(-1, 1) * qt(0.975, df = n_bp - 1) * se_bp
# 基于得分法计算疫苗接种比例的置信区间。
x_vax <- sum(ph_data$vaccinated == "Yes")
n_vax <- sum(!is.na(ph_data$vaccinated))
vax_ci_object <- prop.test(x_vax, n_vax, correct = FALSE)
vax_estimate <- unname(vax_ci_object$estimate)
vax_ci <- vax_ci_object$conf.int
ci_table <- data.frame(
Quantity = c("平均收缩压(mmHg)", "疫苗接种比例"),
Estimate = c(mean_bp, vax_estimate),
Lower_95_CI = c(ci_mean_bp[1], vax_ci[1]),
Upper_95_CI = c(ci_mean_bp[2], vax_ci[2])
)
knitr::kable(ci_table, digits = 3,
col.names = c("指标", "估计值", "95% 置信区间下限", "95% 置信区间上限"),
caption = "模拟样本的点估计与区间估计")| 指标 | 估计值 | 95% 置信区间下限 | 95% 置信区间上限 |
|---|---|---|---|
| 平均收缩压(mmHg) | 124.30 | 123.060 | 125.536 |
| 疫苗接种比例 | 0.71 | 0.669 | 0.747 |
估计的平均收缩压为 124.3 mmHg(95% CI:123.1 至 125.5)。在模型与抽样假设成立的前提下,该区间描述的是总体均值的不确定性,而不是包含 95% 个体血压值的范围。
如果反复采用同一种有效的抽样方法和区间估计程序,所得区间中约有 95% 会包含真实参数。计算出一个具体区间后,参数是固定的;按照标准的频率学派解释,不能说这个特定区间有 95% 的概率包含真实参数。
置信区间的宽度反映模型下的随机不确定性。它不会自动涵盖选择偏倚、测量误差、未控制的混杂、模型设定错误或数据处理错误。
区间不是“通过/不通过”的判定工具 围绕微小效应的窄区间可能并不重要;宽区间则可能同时包含具有实际意义的获益与危害。应结合估计值、区间界限、单位、研究设计和公共卫生情境进行解释。
某风险差为 −4 个百分点,95% 置信区间为 −9 至 +1 个百分点。应如何谨慎解释?
答案: 在相关假设成立的前提下,数据所支持的效应范围从具有实际意义的降低到小幅增加。由于区间包含 0,在双侧 5% 显著性水平下,数据不能拒绝差异为 0;但“差异无统计学显著性”并不能证明效应不存在。假设检验从一个零假设 开始,通常表示无差异或无关联,并设有备择假设 。p 值是:
在零假设和模型假设成立的前提下,获得与零假设至少像观测数据这样不相容的数据的概率。
p 值不是零假设为真的概率,不是结果“由偶然造成”的概率,也不是效应大小。
检验效能取决于样本量、变异程度、效应大小、结局频率、研究设计、缺失情况以及所选显著性阈值。在条件允许时,应在收集数据之前规划检验效能。
韦尔奇 t 检验不要求两组方差相等,因此通常是比较两个独立均值的合理默认方法。这里用它比较不同当前吸烟状态人群的平均收缩压。
bp_test <- t.test(systolic_bp ~ smoking_status, data = ph_data)
bp_means <- aggregate(systolic_bp ~ smoking_status, data = ph_data, mean)
knitr::kable(bp_means, digits = 1,
col.names = c("当前吸烟状态", "平均收缩压(mmHg)"),
caption = "按当前吸烟状态分组的平均收缩压")| 当前吸烟状态 | 平均收缩压(mmHg) |
|---|---|
| Not current | 123 |
| Current | 129 |
将差值定义为当前吸烟者减去非当前吸烟者时,估计的均值差为 5.6 mmHg。双侧 p 值为 0.0011。这一模拟的观察性比较反映的是关联,不能说明吸烟导致了该差异,因为两组的其他特征也可能不同。
| 研究问题 | 常见的入门方法 | 重要检查事项 |
|---|---|---|
| 比较两个独立均值 | 韦尔奇两独立样本 t 检验 | 观测单位相互独立;检查分布和强影响观测值 |
| 比较配对的前后测量值 | 对个体内差值进行配对 t 检验 | 配对关系正确;检查差值分布 |
| 比较两个分类变量 | 卡方独立性检验 | 检查期望格数;数据稀疏时使用费舍尔精确检验 |
| 比较有序或高度偏态的结局 | 适当时采用基于秩的方法 | 秩和检验并不自动等同于中位数检验 |
| 估计调整后的连续型关联 | 线性回归 | 函数形式、残差模式、强影响观测值 |
| 估计调整后的二分类关联 | 逻辑回归 | 模型设定正确、数据稀疏性、优势比的解释 |
统计学显著不等于具有公共卫生重要性 大样本可能使很小的效应得到很小的 p 值,小型研究则可能漏掉重要效应。应报告效应估计值、置信区间、单位、可能时的绝对风险、相关假设及后果,而不能只报告 p 是否 < 0.05。
多重检验同样需要重视。检验大量假设时,出现假阳性结果的可能性会增加。应预先指定主要研究问题,限制机会性检验,并在科学情境需要时采用多重性校正方法。
以下哪种说法正确:“零假设为真的概率是 3%”,还是“如果零假设和模型假设成立,得到至少像当前结果这样与零假设不相容的结果,其发生概率约为 3%”?
答案: 第二种说法正确。第一种说法错误地将 p 值当作 。p 值并不提供这个概率。假设对 240 名工人随访一个呼吸道疾病暴发期。
| 患病 | 未患病 | 合计 | |
|---|---|---|---|
| 暴露组 | 120 | ||
| 未暴露组 | 120 |
两组的风险分别为:
以下三种常见关联指标回答不同的问题:
a <- 48; b <- 72; c <- 24; d <- 96
risk_exposed <- a / (a + b)
risk_unexposed <- c / (c + d)
risk_difference <- risk_exposed - risk_unexposed
risk_ratio <- risk_exposed / risk_unexposed
odds_ratio <- (a * d) / (b * c)
# 用于教学演示的大样本置信区间。
se_rd <- sqrt(risk_exposed * (1 - risk_exposed) / (a + b) +
risk_unexposed * (1 - risk_unexposed) / (c + d))
ci_rd <- risk_difference + c(-1, 1) * 1.96 * se_rd
se_log_rr <- sqrt(1 / a - 1 / (a + b) + 1 / c - 1 / (c + d))
ci_rr <- exp(log(risk_ratio) + c(-1, 1) * 1.96 * se_log_rr)
se_log_or <- sqrt(1 / a + 1 / b + 1 / c + 1 / d)
ci_or <- exp(log(odds_ratio) + c(-1, 1) * 1.96 * se_log_or)
effect_table <- data.frame(
Measure = c("风险差", "风险比", "优势比"),
Estimate = c(risk_difference, risk_ratio, odds_ratio),
Lower_95_CI = c(ci_rd[1], ci_rr[1], ci_or[1]),
Upper_95_CI = c(ci_rd[2], ci_rr[2], ci_or[2])
)
knitr::kable(effect_table, digits = 2,
col.names = c("指标", "估计值", "95% 置信区间下限", "95% 置信区间上限"),
caption = "暴发示例中未经调整的关联指标")| 指标 | 估计值 | 95% 置信区间下限 | 95% 置信区间上限 |
|---|---|---|---|
| 风险差 | 0.20 | 0.09 | 0.31 |
| 风险比 | 2.00 | 1.31 | 3.04 |
| 优势比 | 2.67 | 1.50 | 4.75 |
暴露组工人的患病风险为 40%,未暴露组为 20%。估计风险差为 20 个百分点,即在本次暴发期内,每 100 名工人中增加 20 例病例。风险比为 2.0,表示暴露组观察到的风险是未暴露组的两倍。优势比为 2.67,不应将其误称为风险比。
设想一个包含 1,000 人的筛查项目。参考标准判定其中 80 人患有该病。筛查试验得到以下结果:
| 患病 | 未患病 | 合计 | |
|---|---|---|---|
| 检测阳性 | TP = 68 | FP = 92 | 160 |
| 检测阴性 | FN = 12 | TN = 828 | 840 |
| 合计 | 80 | 920 | 1,000 |
tp <- 68; fp <- 92; fn <- 12; tn <- 828
screening <- data.frame(
Measure = c("灵敏度", "特异度", "阳性预测值",
"阴性预测值"),
Estimate = c(tp / (tp + fn), tn / (tn + fp),
tp / (tp + fp), tn / (tn + fn))
)
knitr::kable(screening, digits = 3,
col.names = c("指标", "估计值"),
caption = "假设项目中的筛查性能")| 指标 | 估计值 |
|---|---|
| 灵敏度 | 0.850 |
| 特异度 | 0.900 |
| 阳性预测值 | 0.425 |
| 阴性预测值 | 0.986 |
灵敏度为 85%,特异度为 90%。然而,在检测阳性者中,真正患病的比例只有 42.5%(PPV)。出现这一差异,是因为该筛查人群的患病率为 8%,而人数多得多的未患病组会累积较多假阳性结果。
当灵敏度和特异度固定时,PPV 通常随患病率升高而升高,NPV 通常随之降低。
prevalence_grid <- seq(0.01, 0.50, by = 0.01)
sens <- 0.85
spec <- 0.90
ppv_grid <- sens * prevalence_grid /
(sens * prevalence_grid + (1 - spec) * (1 - prevalence_grid))
npv_grid <- spec * (1 - prevalence_grid) /
((1 - sens) * prevalence_grid + spec * (1 - prevalence_grid))
plot(prevalence_grid, ppv_grid, type = "l", lwd = 3,
col = palette_ph["vermillion"], ylim = c(0, 1),
xlab = "患病率", ylab = "预测值",
main = "预测值取决于患病率")
lines(prevalence_grid, npv_grid, lwd = 3, lty = 2,
col = palette_ph["blue"])
legend("right", legend = c("PPV", "NPV"), lwd = 3, lty = c(1, 2),
col = c(palette_ph["vermillion"], palette_ph["blue"]), bty = "n")即使灵敏度固定为 85%、特异度固定为 90%,预测值仍会随患病率变化。
提高灵敏度的阈值选择通常会降低特异度,反之亦然。应如何权衡,取决于漏诊、误报的后果,以及随访资源和公平性。由于疾病谱和验证过程不同,灵敏度和特异度也可能因人群而异。
计算灵敏度时,分母应为所有检测阳性者,还是所有真正患病者?
答案: 分母应为所有真正患病者,即 (TP+FN)。在检测阳性者中,真阳性所占比例是 PPV,其分母为 (TP+FP)。皮尔逊相关系数概括两个数值变量之间线性关系的强度。斯皮尔曼相关系数概括单调的秩相关关系。离群值、受限的取值范围、混合的亚组以及非线性模式都可能影响这两种相关系数。
plot(ph_data$age, ph_data$systolic_bp,
pch = 16, cex = 0.65, col = rgb(0, 114/255, 178/255, 0.38),
xlab = "年龄(岁)", ylab = "收缩压(mmHg)",
main = "年龄与收缩压")
abline(lm(systolic_bp ~ age, data = ph_data),
col = palette_ph["vermillion"], lwd = 3)在模拟数据中,收缩压往往随年龄增加而升高,但个体之间存在较大差异。
皮尔逊相关系数为 0.62。该数值并不能证明年龄的变化会使血压发生某个特定幅度的变化。要作因果解释,除了相关性之外,还需要合理、可辩护的研究设计与假设。
多元线性模型可以将平均收缩压描述为若干协变量的函数:
bp_model <- lm(systolic_bp ~ age + bmi + smoking_status, data = ph_data)
coef_table <- summary(bp_model)$coefficients
coef_table_display <- coef_table
rownames(coef_table_display) <- c(
"截距", "年龄(每增加 1 岁)", "体重指数(每增加 1 单位)",
"当前吸烟(参照:当前不吸烟)"
)
knitr::kable(
coef_table_display,
digits = 3,
col.names = c("估计值", "标准误", "t 值", "p 值"),
caption = "收缩压的线性回归"
)| 估计值 | 标准误 | t 值 | p 值 | |
|---|---|---|---|---|
| 截距 | 90.533 | 3.220 | 28.12 | 0.000 |
| 年龄(每增加 1 岁) | 0.569 | 0.032 | 17.99 | 0.000 |
| 体重指数(每增加 1 单位) | 0.308 | 0.108 | 2.85 | 0.005 |
| 当前吸烟(参照:当前不吸烟) | 3.532 | 1.265 | 2.79 | 0.005 |
年龄的回归系数为每增加 1 岁 0.57 mmHg。在该模型中,体重指数和吸烟状态相同而年龄相差 1 岁的参与者,其平均收缩压相差约 0.57 mmHg。这个系数表示调整后的关联,并不自然等同于因果效应。
应检查残差模式、线性关系、有影响力的观测、观测间依赖性,以及用线性模型描述条件均值在科学上是否合理。样本量很大也无法弥补错误的函数形式。
逻辑回归对二元结局优势的对数进行建模。将回归系数取指数得到的是优势比,而不是风险比。
infection_model <- glm(
respiratory_infection ~ vaccinated + age + smoking_status + neighborhood,
data = ph_data,
family = binomial()
)
model_coef <- summary(infection_model)$coefficients
non_intercept <- rownames(model_coef) != "(Intercept)"
or_table <- data.frame(
Term = rownames(model_coef)[non_intercept],
Odds_ratio = exp(model_coef[non_intercept, "Estimate"]),
Lower_95_CI = exp(model_coef[non_intercept, "Estimate"] -
1.96 * model_coef[non_intercept, "Std. Error"]),
Upper_95_CI = exp(model_coef[non_intercept, "Estimate"] +
1.96 * model_coef[non_intercept, "Std. Error"]),
P_value = model_coef[non_intercept, "Pr(>|z|)"],
row.names = NULL
)
term_labels_zh <- c(
vaccinatedYes = "已接种(参照:未接种)",
age = "年龄(每增加 1 岁)",
smoking_statusCurrent = "当前吸烟(参照:当前不吸烟)",
neighborhoodNorth = "北区(参照:中心区)",
neighborhoodSouth = "南区(参照:中心区)",
neighborhoodWest = "西区(参照:中心区)"
)
or_table_display <- or_table
or_table_display$Term <- unname(term_labels_zh[or_table_display$Term])
knitr::kable(
or_table_display,
digits = 3,
col.names = c("变量或对比", "优势比", "95% 置信区间下限", "95% 置信区间上限", "p 值"),
caption = "呼吸道感染的调整后优势比(未列出截距)"
)| 变量或对比 | 优势比 | 95% 置信区间下限 | 95% 置信区间上限 | p 值 |
|---|---|---|---|---|
| 已接种(参照:未接种) | 0.510 | 0.324 | 0.802 | 0.004 |
| 年龄(每增加 1 岁) | 1.000 | 0.986 | 1.014 | 0.979 |
| 当前吸烟(参照:当前不吸烟) | 1.554 | 0.931 | 2.593 | 0.091 |
| 北区(参照:中心区) | 0.980 | 0.544 | 1.767 | 0.947 |
| 南区(参照:中心区) | 0.893 | 0.491 | 1.623 | 0.710 |
| 西区(参照:中心区) | 1.014 | 0.546 | 1.883 | 0.966 |
接种者与未接种者相比的调整后优势比为 0.51(95% 置信区间:0.32 至 0.8)。它描述的是该模拟模型中的条件关联,而不是调整后的风险比;仅凭这一结果也不能确立疫苗有效性。
分类变量项比较所显示的因子水平与其参照水平;年龄的优势比对应年龄相差 1 岁。截距取指数后表示基线优势,并不是优势比,因此表中有意省略了截距。
调整并非万能 不要仅因为协变量在单变量分析中的 p 值较小就选择它们。应依据专业领域知识和因果框架预先指定变量。估计总效应时,应避免调整暴露的后果;同时要记住,未测量或测量不佳的混杂仍可能存在。
一项经得起推敲的分析,应使其他分析人员能够理解具体做法并复现结果。
# 建模前应进行的简要检查。
data_quality <- data.frame(
Check = c("数据行数", "重复的参与者编号", "缺失的身体活动值",
"年龄超出 18–85 岁", "收缩压超出 70–250 mmHg"),
Result = c(
nrow(ph_data),
sum(duplicated(ph_data$participant_id)),
sum(is.na(ph_data$physical_activity_min_week)),
sum(ph_data$age < 18 | ph_data$age > 85),
sum(ph_data$systolic_bp < 70 | ph_data$systolic_bp > 250)
)
)
knitr::kable(
data_quality,
col.names = c("检查项目", "结果"),
caption = "部分可复现的数据质量检查"
)| 检查项目 | 结果 |
|---|---|
| 数据行数 | 520 |
| 重复的参与者编号 | 0 |
| 缺失的身体活动值 | 18 |
| 年龄超出 18–85 岁 | 0 |
| 收缩压超出 70–250 mmHg | 0 |
应追问数值为何缺失、不同组的缺失模式是否不同,以及分析采用了哪些假设。完整个案分析可能降低精确度并引入偏倚。绝不能在不作说明的情况下把缺失值转换为零,也不要自动删除数据行而不报告其影响。
更进阶的处理方法包括多重插补、逆概率加权、基于似然的方法和敏感性分析。这些方法是否有效取决于相应假设,而这些假设应被明确陈述和检验。
健康数据代表着真实的人与制度系统 应保护隐私,避免污名化语言,说明类别的定义方式,并让相关社区参与数据收集和结果报告的决策。即使模型在技术上正确,如果研究问题、标签或结果用途不恰当,仍可能造成伤害。
种族和族裔变量通常反映社会、政治、历史和测量过程,不应被当作简单的生物学原因。当观察到健康差异时,应调查结构性条件、服务可及性、歧视、环境和测量实践。应避免公布可能导致个人身份被识别的小频数单元格,尤其是在地理信息与罕见疾病相结合时。
调查权重、分层、聚类、重复测量、多层结构、事件发生时间结局、空间相关以及因果估计目标,通常需要采用本入门指南范围之外的方法。正确的下一步是识别这些数据结构并寻找合适的方法,而不是忽略它们。
一幅地图标出了六名罕见感染患者的精确居住位置。首要担忧是什么?
答案: 再识别和隐私风险。发布前应汇总或掩蔽地理信息、应用信息披露控制规则,并让数据管理人员和受影响社区参与决策。公共利益并不能免除保护参与者的义务。研究问题: 在模拟的社区样本中,接种疫苗是否与较低的 12 周呼吸道感染观察风险相关?
出于教学目的,我们把该样本视为在同一时间段内接受观察的队列。先进行未调整分析,再结合上文的调整后逻辑回归模型进行解释。由于这些数据是模拟的观察性数据,所得结果并不是对现实世界疫苗效果的估计。
vax_table <- with(
ph_data,
table(Vaccinated = vaccinated, Infection = respiratory_infection)
)
vax_table_display <- vax_table
dimnames(vax_table_display) <- list(
接种状态 = c("未接种", "已接种"),
感染状态 = c("未感染", "感染")
)
vax_table_display## 感染状态
## 接种状态 未感染 感染
## 未接种 106 45
## 已接种 303 66
risk_vaccinated <- vax_table["Yes", "Yes"] / sum(vax_table["Yes", ])
risk_unvaccinated <- vax_table["No", "Yes"] / sum(vax_table["No", ])
rd_vax <- risk_vaccinated - risk_unvaccinated
rr_vax <- risk_vaccinated / risk_unvaccinated
a_v <- unname(vax_table["Yes", "Yes"])
b_v <- unname(vax_table["Yes", "No"])
c_v <- unname(vax_table["No", "Yes"])
d_v <- unname(vax_table["No", "No"])
se_log_rr_vax <- sqrt(1 / a_v - 1 / (a_v + b_v) +
1 / c_v - 1 / (c_v + d_v))
ci_rr_vax <- exp(log(rr_vax) + c(-1, 1) * 1.96 * se_log_rr_vax)
se_rd_vax <- sqrt(
risk_vaccinated * (1 - risk_vaccinated) / (a_v + b_v) +
risk_unvaccinated * (1 - risk_unvaccinated) / (c_v + d_v)
)
ci_rd_vax <- rd_vax + c(-1, 1) * 1.96 * se_rd_vax
ci_risk_vaccinated <- prop.test(a_v, a_v + b_v, correct = FALSE)$conf.int
ci_risk_unvaccinated <- prop.test(c_v, c_v + d_v, correct = FALSE)$conf.int
mini_results <- data.frame(
Measure = c("接种者的风险", "未接种者的风险",
"风险差", "风险比"),
Estimate = c(risk_vaccinated, risk_unvaccinated, rd_vax, rr_vax),
Lower_95_CI = c(ci_risk_vaccinated[1], ci_risk_unvaccinated[1],
ci_rd_vax[1], ci_rr_vax[1]),
Upper_95_CI = c(ci_risk_vaccinated[2], ci_risk_unvaccinated[2],
ci_rd_vax[2], ci_rr_vax[2])
)
knitr::kable(
mini_results,
digits = 3,
col.names = c("指标", "估计值", "95% 置信区间下限", "95% 置信区间上限"),
caption = "模拟小型案例研究中的未调整估计值及其 95% 置信区间"
)| 指标 | 估计值 | 95% 置信区间下限 | 95% 置信区间上限 |
|---|---|---|---|
| 接种者的风险 | 0.179 | 0.143 | 0.221 |
| 未接种者的风险 | 0.298 | 0.231 | 0.375 |
| 风险差 | -0.119 | -0.202 | -0.036 |
| 风险比 | 0.600 | 0.432 | 0.833 |
两个风险估计采用得分置信区间。风险差区间采用简单的大样本正态近似,风险比区间采用大样本对数近似;这些教学用方法可能不适合稀疏数据。
risks <- c("未接种" = risk_unvaccinated, "已接种" = risk_vaccinated)
bar_positions <- barplot(
risks,
ylim = c(0, max(risks) * 1.30),
col = c(palette_ph["orange"], palette_ph["teal"]),
border = NA,
ylab = "12 周感染观察风险",
main = "不同疫苗接种状态的呼吸道感染风险",
las = 1
)
text(bar_positions, risks, labels = pct(risks), pos = 3, font = 2)在模拟数据集中,接种者的感染观察风险低于未接种者。
| 指标 | 公式 | 解释 |
|---|---|---|
| 患病率 | 现有病例数 / 界定的人群数 | 在指定时点或期间患有该病的比例 |
| 累积发病率 | 新发病例数 / 期初处于风险中的人群数 | 规定期间内发生结局的风险 |
| 发病密度 | 新发病例数 / 风险人时 | 新发事件发生的速率 |
| 风险差 | 风险的绝对增加或降低 | |
| 风险比 | 相对风险 | |
| 优势比 | 2×2 表中的 | 两组优势之比 |
| 均值的标准误估计 | 均值抽样变异性的估计 | |
| 比例的近似标准误 | 简单的大样本不确定性近似 | |
| 灵敏度 | 患病者中检测阳性的比例 | |
| 特异度 | 未患病者中检测阴性的比例 | |
| 阳性预测值(PPV) | 检测阳性者中实际患病的比例 | |
| 阴性预测值(NPV) | 检测阴性者中实际未患病的比例 |
对于小样本或接近 0 或 1 的比例,基本的瓦尔德区间表现可能较差。根据分析目标,得分区间、威尔逊区间或精确法通常更为合适。