简介:这是一份系统讲解病例对照研究核心知识的PPT课件,面向流行病学初学者、医学生及临床科研人员,帮助理解如何通过回顾性设计探求疾病与可疑暴露因素之间的关联并识别潜在危险因素。课件从基本原理出发,围绕不匹配、匹配和巢式三种研究类型展开,结合Herbst年轻女性阴道腺癌经典实例,完整演示从假设提出、病例与对照选择、资料收集整理到偏倚识别与控制的实施路径;同时讲清了匹配过度的不良影响、混杂因素的控制策略以及研究的优缺点,并在结尾强调结果解读需结合队列研究或实验研究验证因果联系。压缩包共1个ppt文件,大小168KB,内容精炼、逻辑连贯,适合用于课堂辅助教学或自学备考。目前已有88人学习,是快速入门病例对照研究的一份实用参考。
1. 为什么「从果到因」的研究还不过时
病例对照研究的诞生比随机对照试验早了半个世纪,但直到今天,它依然是罕见病病因探索、暴发调查和药物不良反应信号挖掘的首选设计。原因很直接:当疾病发生率低于千分之一,或者从暴露到发病要等上十年,队列研究的样本量和随访成本会膨胀到无法执行。病例对照研究反其道而行,先锁定已经发病的病例,再回头追溯暴露史,用一套精巧的比较逻辑绕开时间成本。它不追求计算发病率,而是用比值比估计关联强度,这让它在真实世界数据、医院信息系统和登记数据库中都有极强的落地空间。
这篇内容面向需要设计、评审或教学的研究者,也面向要把一份方法学 PPT 讲清楚的临床医生。把「病例如何定义、对照从哪来、匹配怎么配、偏倚怎么防」这四个问题想明白,比背下任何一条统计公式都重要。原则掌握不牢,后面任何高级校正都是白做。
2. 病例对照设计的核心原则:先定病例,再定对照
2.1 病例组的定义与纳入:先回答「什么算病历」
病例对照研究的第一步不是找对照,而是给病例下定义。这个定义必须独立于暴露因素,也就是说,你不能用「吸过烟的人」来定义肺癌病例。常见做法是用国际疾病分类编码加上明确的诊断标准,比如病理确诊、影像学标准或实验室指标,同时限定诊断时间和诊断机构。一个容易被忽视的细节是「现患病例」和「新发病例」的区别:纳入现患病例会引入存活偏倚,因为病情重、预后差的病人可能已经死亡而无法入组,导致研究样本只能代表存活者。设计阶段应优先选择新发病例,或者在分析阶段单独做敏感性分析。
病例的来源决定研究的代表性。以医院为基础收集病例,速度快、诊断可靠,但容易受到就医选择性的影响;以人群为基础(如肿瘤登记处)收集病例,代表性更好,但需要多中心协作,成本上升。混合来源是常见做法:医院病例做主要分析,登记数据做验证,二者结论一致时证据力度更强。
2.1.1 病例定义的实操清单
- 明确诊断标准的具体版本和操作定义,不要只写「按临床指南」。
- 限定发病时间窗,例如「2020 年 1 月 1 日至 2023 年 12 月 31 日新诊断」。
- 写明排除条件,例如既往同类疾病史、转移性肿瘤来源不明、年龄超出研究范围。
2.2 对照组的选取原则:和病例来自同一个源人群
对照组是病例对照研究中最容易出错、也最值得花时间推敲的部分。核心原则只有一条:对照必须来自「如果没有患目标疾病,原本可能成为病例」的那群人。这个被称为源人群的概念,决定了对照的代表性。以医院为基础的研究,理想对照应当是从同一医院、同一就诊时间段内收治的其他疾病患者中抽取;以人群为基础的研究,则从同一年龄、同一地区的常住居民中抽取。
选择对照时有一个常见误区,就是把「健康人」当作对照。健康对照往往更容易配合调查,但他们在就医行为、生活方式、健康意识上和病例存在系统性差异,这种差异可能不是暴露因素本身造成的。比如研究饮酒与肝癌的关联,医院内其他消化系统疾病患者作为对照,在回忆饮酒史上与病例的配合程度更接近,反而能减少回忆偏倚。原则是:对照的选择过程越接近病例的选择过程,偏倚越小。
2.2.1 对照选择的操作步骤
- 确定源人群的时间范围和地理范围。
- 从该人群中列出所有潜在对照(可先做频数匹配的备选池)。
- 排除患目标疾病或相关亚临床状态者。
- 记录排除原因和人数,评审者会看这一步。
2.3 纳入排除标准的落地表达
一份不需要被反复追问的纳入排除标准,应该写成一张可以直接执行的表。下面是一个以药物不良反应研究为例的样式:
| 对象 | 纳入标准 | 排除标准 |
|---|---|---|
| 病例 | 2020-2023 年间首次诊断肝损伤,RUCAM 评分 ≥ 6 | 既往肝病史、肝移植术后、合并乙肝/丙肝活动期 |
| 对照 | 同一医院同期因急性感染住院,无肝损伤 | 既往肝胆疾病史、用药史无法核实、无法完成访谈 |
这一张表同时界定了「谁是病例」「谁够格做对照」「二者从哪里来」。在实际数据落地时,我一般用结构化查询先抽候选集,再做人工复核。下面这段 Python 伪代码表达的是从医院信息系统里抽对照的筛选逻辑:
def select_controls(case_df, visit_df, match_var="age_group", ratio=1): # 从所有就诊记录里排除目标疾病患者 disease_ids = set(case_df["patient_id"]) pool = visit_df[~visit_df["patient_id"].isin(disease_ids)].copy() # 按匹配变量分层,每层内随机抽样 controls = [] for group, case_group in case_df.groupby(match_var): pool_group = pool[pool[match_var] == group] n = len(case_group) * ratio sampled = pool_group.sample(n=min(n, len(pool_group)), random_state=42) controls.append(sampled) return pd.concat(controls) # 参数说明: # case_df : 已通过纳入排除标准确认的病例表 # visit_df: 同一时间窗内全部住院/门诊记录 # ratio : 病例:对照 比例,常用 1:1 或 1:4 # random_state: 固定随机种子,保证抽样结果可复现这段代码的价值不在算法,而在它迫使你把「排除目标疾病患者」「同一匹配层内抽样」这两个原则显式化。很多失败的病例对照研究,失败点不在统计,而在对照抽取这一步混进了不该有的人、或漏掉了本该在池子里的人。
3. 匹配原则:什么时候配、拿什么配、配多少
3.1 频数匹配与个体匹配的取舍
匹配是为了让病例组和对照组在已知的混杂因素上分布一致,常见做法是对年龄和性别做匹配。匹配分两种:频数匹配只要求两组在匹配变量的构成比例上一致,比如都是 40-49 岁占 30%;个体匹配则是给每个病例逐一配上年龄相差不超过 3 岁、性别相同的对照。
选择哪种匹配,取决于研究目的和可用样本。频数匹配操作简单,且不会破坏后续分层分析的完整性,适合大样本;个体匹配在小样本研究里更精细,能直接控制掉匹配变量的影响,但代价是统计上必须用配对分析方法,不能用常规卡方检验,否则会低估标准误。我一般建议:匹配变量不超过 3 个,且只匹配确定无疑的强混杂因素,如年龄、性别、种族或地区。
3.2 病例对照比例的选取依据
1 个病例配 1 个对照是效率最低的方案。统计效率随对照数增加而提升,但到 1:4 以后边际收益明显下降。原因在于,配对设计的检验效能与对照数的关系是对数曲线,从 1:1 改成 1:2 能显著提升效能,从 1:3 到 1:4 提升就有限了。如果病例数特别少,比如不足 50 例,可以考虑 1:4 甚至 1:5,但要注意:每多配一个对照,实际工作量和数据质量风险都在上升。
3.2.1 用 R 做 1:M 个体匹配
个体匹配的基础实现并不复杂。下面是不依赖第三方包的匹配代码,逻辑清晰且便于逐行检查:
match_controls <- function(case_df, pool_df, caliper = 3) { case_df$matched <- NA for (i in seq_len(nrow(case_df))) { age_lower <- case_df$age[i] - caliper age_upper <- case_df$age[i] + caliper candidate <- which( pool_df$sex == case_df$sex[i] & pool_df$age >= age_lower & pool_df$age <= age_upper & !pool_df$used ) if (length(candidate) > 0) { pick <- candidate[1] pool_df$used[pick] <- TRUE case_df$matched[i] <- pool_df$id[pick] } } return(case_df) } # 参数说明: # caliper : 年龄容许偏差,常用 2-5 岁,过宽会导致匹配质量下降 # pool_df$used: 标记该对照是否已被取用,防止一个对照配多个病例 # 该实现为贪心顺序匹配,更严格的方案应先随机打乱病例顺序这段代码体现了个体匹配的两个要点:一是匹配变量必须是分类精确匹配加连续变量卡钳值;二是匹配顺序会影响结果,所以通常先随机排序再匹配。
3.3 过头匹配:匹配原则最常被突破的一条边界
过多匹配会引入一种叫做「过头匹配」的问题,表现为匹配变量本身与暴露相关。最经典的例子是研究绝经后激素替代治疗与冠心病的关系时,把高血压也作为匹配变量。高血压既与激素使用相关,又是冠心病的独立危险因素。匹配了高血压,等于强制两组在高血压分布上相等,暴露效应被部分吸收到匹配过程中,结果会向无关联方向偏移。判断一个变量能不能匹配,标准是:它必须是混杂因素,且不是暴露和疾病之间的中间环节。拿不准的时候,宁可把它放入分析阶段做校正,也不要轻易匹配。
4. 从设计到统计:偏倚控制与 OR 值的计算
4.1 三类偏倚的识别与规避
病例对照研究最容易受三类偏倚影响,设计阶段就得逐项检查。
奈曼偏倚,也叫现患-新发病例偏倚,发生在只纳入现患病例时。患病多年且存活者往往是病情较轻、预后较好的那一批,他们的暴露模式可能和快速死亡者完全不同。规避办法是限定新发病例,或同时报告现患病例的分析结果做对比。
伯克森偏倚,在医院对照中特别常见。住院患者本身就存在就诊选择,某些暴露与某些疾病更容易导致住院,这种联合就医倾向会造成暴露与疾病的虚假关联。避免方法是选择来自社区或门诊的对照,或者选择入院原因与目标疾病完全无关的对照。
回忆偏倚是病例对照研究的天生短板。病例因为病程经历,会对既往暴露做更深入的回忆,而对照组可能敷衍作答。减弱手段包括:使用客观记录替代自述暴露(比如处方记录、职业档案),对调查者实施盲法,病例和对照用同一份结构化问卷。
下面的表把三类偏倚的关键信息做了压缩,方便放进 PPT 或作为核查清单:
| 偏倚类型 | 产生环节 | 典型场景 | 控制策略 |
|---|---|---|---|
| 奈曼偏倚 | 病例纳入 | 只收存活病例 | 用新发病例 |
| 伯克森偏倚 | 对照选取 | 医院间比较 | 对照来自同一源人群 |
| 回忆偏倚 | 暴露测量 | 自我报告用药史 | 用客观记录验证 |
4.2 四格表与 OR 值的计算
病例对照研究不能直接计算发病率,因为病例组和对照组的抽样比例是研究者自己定的。所以效应量用比值比表示,反映暴露与疾病之间关联的方向和强度。OR 值大于 1 提示正关联,小于 1 提示保护作用,等于 1 则无关联。
先整理四格表:
| 病例组 | 对照组 | |
|---|---|---|
| 有暴露 | a | b |
| 无暴露 | c | d |
OR = (a × d) / (b × c),95% 置信区间用 Woolf 法计算。用 Python 可以直接算:
import numpy as np from scipy import stats table = np.array([[45, 30], # a: 病例有暴露, b: 对照有暴露 [55, 70]]) # c: 病例无暴露, d: 对照无暴露 odds_ratio = (table[0,0] * table[1,1]) / (table[0,1] * table[1,0]) # Woolf 法计算标准误和置信区间 log_or = np.log(odds_ratio) se_log_or = np.sqrt(1/table[0,0] + 1/table[0,1] + 1/table[1,0] + 1/table[1,1]) ci_lower = np.exp(log_or - 1.96 * se_log_or) ci_upper = np.exp(log_or + 1.96 * se_log_or) # 卡方检验 chi2, p_value, _, _ = stats.chi2_contingency(table, correction=False) print(f"OR = {odds_ratio:.2f}, 95% CI = ({ci_lower:.2f}, {ci_upper:.2f}), p = {p_value:.3f}")上面的代码里,chi2_contingency默认不做连续性校正,当某个格子的期望频数小于 5 时应改用 Fisher 精确检验,把correction=False去掉或者直接用fisher_exact。置信区间跨越 1 时,即使点估计大于 1,也不能下阳性结论。
4.3 分层分析和配对资料的检验
在实际分析中,匹配变量和潜在的混杂因素不可能全部进入匹配阶段,剩余混杂靠分析阶段处理。最常见的手段是 Mantel-Haenszel 分层分析,把数据按年龄或性别分层,计算每一层内的 OR 值,再合并成一个总的调整 OR 值。如果各层的 OR 值差异明显,提示存在效应修饰,此时单独报告每层的 OR 更合适。
此外,还需要区分成组设计与配对设计的分析方法。频数匹配的样本用普通的卡方检验没有问题;但个体匹配的数据一旦只用普通卡方检验,就会把配对信息扔掉,相当于降低了统计效率。配对资料的正确做法是用 McNemar 检验,关注的是对子之间不一致的那些结果:
# 配对四格表: 该表记录了 1:1 配对的对子数 # 行列分别表示病例和对照的暴露状态 mcnemar_table <- matrix(c(50, 20, 30, 100), nrow = 2, byrow = TRUE, dimnames = list(病例 = c("暴露", "未暴露"), 对照 = c("暴露", "未暴露"))) mcnemar.test(mcnemar_table) # 配对 OR = 不一致对子之比: 20 对病例暴露/对照未暴露, 30 对相反 # 该检验只利用 b 和 c 两个格子,a 与 d 不参与统计量计算这里最关键的解释是:McNemar检验里的矩阵是「对子」的数量,不是人数。写错矩阵会直接计算出错误结果。
5. 把基本原则做成一份可评审的 PPT
5.1 每一页对应一条原则
一份方法学 PPT 的目标不是展示文献回顾有多全面,而是让评审者在十分钟内确认:研究的内部效度站得住。我会把每一页和一条原则绑定,一页讲不透的内容拆成两页,但绝不在同一页里塞两个原则。
建议页序是:病例定义与来源、对照定义与来源、匹配变量表、潜在偏倚及对策表、样本量与效能计算、统计分析方法与分析集定义。其中匹配变量表和偏倚对策表用第 4 章给出的表格直接改,换成自己的数据。必要时加一页「时间轴图」,画出暴露测量时间点、诊断时间点和入组时间点,这比任何文字描述都更能说明时序关系。
5.2 自查清单:评审者会追问的六个问题
- 病例是现患还是新发?排除标准写清楚了没有?
- 对照和病例是否来自同一个源人群?
- 匹配了哪些变量?匹配变量本身是不是暴露与疾病的中间环节?
- 暴露信息是盲法采集的吗?有没有客观记录交叉验证?
- 样本量计算用的 OR 值是多少?依据是什么?
- 如果退掉了部分病例或对照,退掉的比例和原因分别是什么?
每个问题背后都对应一个明确的设计环节。清单的意义在于:任何一个问题回答不清楚,评审意见就会写「设计存在潜在偏倚,结论需谨慎解释」。
5.3 一页结论页的画法
最后一页可以用一张四方格图收尾,左边写「设计的不可妥协项」:病例独立于暴露定义、对照来自源人群、匹配变量不超过三个且均为强混杂因素;右边写「分析中补救的余地」:分层分析、多变量校正、敏感性分析只能处理已测量混杂,处理不了设计阶段引入的选择偏倚。左侧的内容如果做不干净,右侧就无从谈起。
本文还有配套的精品资源,点击获取