本教程面向流行病学、公共卫生、临床研究及健康数据分析的初学者,也可作为研究设计与基础统计的速查资料。读完后,你应当能够:
所有代码块均可逐段运行。点击 HTML
页面左侧目录可快速跳转;代码示例只使用 R 自带的
base、stats、graphics 和
datasets,不要求额外分析包。
本教程用于学习与研究规划,不能替代临床判断、当地公共卫生规范、统计咨询或伦理审查。面对真实数据,应预先写明研究方案,并依据抽样设计、重复测量、聚类、删失及缺失机制选择合适方法。
流行病学(epidemiology)研究特定人群中健康相关状态或事件的分布、决定因素及其应用。它不仅回答“有多少人患病”,还回答“谁更常患病、何时发生、在哪里发生、为什么发生,以及怎样预防或控制”。
流行病学常分为:
一个清楚的研究问题通常可用 PECO/PICO(T) 表达:
| 元素 | 含义 | 示例 |
|---|---|---|
| P | Population,人群 | 2025 年某省 18–65 岁医护人员 |
| E/I | Exposure/Intervention,暴露或干预 | 每周夜班 ≥3 次 / 疫苗接种 |
| C | Comparator,对照 | 每周夜班 <3 次 / 未接种 |
| O | Outcome,结局 | 新发抑郁症状 / 实验室确诊感染 |
| T | Time,时间 | 随访 12 个月 |
研究对象(study sample)来自目标人群(target population)。内在效度关注样本中的结论是否可信;外在效度关注结论能否推广到目标人群或其他场景。
报告任何频率时都应写明:病例定义、分母、时间区间、地理/机构范围和倍数单位(每百人、每千人或每十万人)。
点患病率:
期间患病率:
患病率反映“现有负担”,同时受发病速度和病程影响。在稳态、罕见疾病且人群相对封闭时,常用近似关系:
其中 为发病率, 为平均病程。治疗延长生存可能提高患病率,却不代表新发病例增加。
累积发病率(risk):
发病密度(incidence rate):
累积发病率是一个概率,必须配合随访时长;发病密度是发生速度,适用于进入、退出和随访时长不一致的开放队列。一个人在发生首次结局、失访、死亡或研究结束时停止贡献该结局的人时。
# 一个小型队列:每行是一名参与者
cohort_small <- data.frame(
id = 1:8,
# 未发病者均完整随访 2 年;病例在发病时停止贡献人时。
follow_years = c(2.0, 1.5, 2.0, 0.5, 2.0, 2.0, 0.8, 2.0),
new_case = c(0, 1, 0, 1, 0, 0, 1, 0)
)
risk_2y <- sum(cohort_small$new_case) / nrow(cohort_small)
incidence_rate <- sum(cohort_small$new_case) / sum(cohort_small$follow_years)
c(two_year_risk = risk_2y,
cases_per_person_year = incidence_rate,
cases_per_1000_person_years = incidence_rate * 1000)## two_year_risk cases_per_person_year cases_per_1000_person_years
## 0.3750 0.2344 234.3750
这里所有未发病者都完成了 2 年随访,因此可直接用期初人数计算 2 年风险;病例只贡献至发病时的人时。真实队列存在失访或删失时,通常应使用生命表或 Kaplan–Meier 等方法估计固定时点风险,而不能简单把病例数除以期初人数。
两个地区年龄结构不同,粗死亡率不可直接比较。直接标化把各地区的年龄别率应用到同一个标准人口。
std <- data.frame(
age = c("0-39", "40-64", "65+"),
cases_A = c(12, 80, 220), pop_A = c(6000, 3000, 1000),
cases_B = c(6, 60, 300), pop_B = c(3000, 3000, 4000),
standard_population = c(5000, 3000, 2000)
)
std$rate_A <- std$cases_A / std$pop_A
std$rate_B <- std$cases_B / std$pop_B
crude_A <- sum(std$cases_A) / sum(std$pop_A)
crude_B <- sum(std$cases_B) / sum(std$pop_B)
adjusted_A <- weighted.mean(std$rate_A, std$standard_population)
adjusted_B <- weighted.mean(std$rate_B, std$standard_population)
round(1000 * c(crude_A = crude_A, crude_B = crude_B,
age_adjusted_A = adjusted_A, age_adjusted_B = adjusted_B), 2)## crude_A crude_B age_adjusted_A age_adjusted_B
## 31.2 36.6 53.0 22.0
间接标化则把标准人群年龄别率用于研究人群,得到预期病例/死亡数;标准化死亡比 。标化率是比较工具,不一定是某个真实人群的实际率。
| 结局 + | 结局 − | 合计 | |
|---|---|---|---|
| 暴露 + | |||
| 暴露 − |
常见效应量:
暴露组风险:;未暴露组风险:
风险比:
风险差:
优势比:
暴露者归因分值(有害暴露):
人群归因分值:,其中 是全人群在消除暴露情景下的反事实风险
比值型指标(RR、OR)描述相对强度;差值型指标(RD)描述绝对影响,往往更直接地支持临床和政策决策。归因分值需要因果解释成立;只有在可交换性等额外条件下,观察到的未暴露组风险才可用于估计 。对一个明确的随访时点,若干预有益,可报告相对风险降低 、绝对风险降低 和获益所需治疗人数 ;若风险增加,则报告伤害所需人数 。NNTB/NNH 通常向上取至下一个整数;方向和时间范围都不能被 隐去。
# 行:暴露/未暴露;列:病例/非病例
tab <- matrix(c(40, 160,
20, 180),
nrow = 2, byrow = TRUE,
dimnames = list(exposure = c("Exposed", "Unexposed"),
outcome = c("Case", "Non-case")))
tab## outcome
## exposure Case Non-case
## Exposed 40 160
## Unexposed 20 180
a <- tab[1, 1]; b <- tab[1, 2]
c_ <- tab[2, 1]; d <- tab[2, 2]
r1 <- a / (a + b)
r0 <- c_ / (c_ + d)
rr <- r1 / r0
rd <- r1 - r0
or <- (a * d) / (b * c_)
afe <- (rr - 1) / rr
c(risk_exposed = r1, risk_unexposed = r0,
risk_ratio = rr, risk_difference = rd,
odds_ratio = or, attributable_fraction_exposed = afe)## risk_exposed risk_unexposed risk_ratio
## 0.20 0.10 2.00
## risk_difference odds_ratio attributable_fraction_exposed
## 0.10 2.25 0.50
大型样本下可在对数尺度为 RR 和 OR 构造近似置信区间:
se_log_rr <- sqrt(1/a - 1/(a+b) + 1/c_ - 1/(c_+d))
rr_ci <- exp(log(rr) + c(-1, 1) * 1.96 * se_log_rr)
se_log_or <- sqrt(1/a + 1/b + 1/c_ + 1/d)
or_ci <- exp(log(or) + c(-1, 1) * 1.96 * se_log_or)
result <- data.frame(
measure = c("RR", "OR"), estimate = c(rr, or),
lower_95 = c(rr_ci[1], or_ci[1]), upper_95 = c(rr_ci[2], or_ci[2])
)
result[c("estimate", "lower_95", "upper_95")] <-
round(result[c("estimate", "lower_95", "upper_95")], 3)
result若任何格子为 0,小样本近似可能失效;应考虑精确方法或有依据的连续性校正。置信区间体现与数据和模型相容的效应范围,并不是“真值有 95% 概率落在该区间”的频率学派解释。
优秀报告通常同时给出相对效应、绝对效应、95% 置信区间,以及一个有意义的基线风险。例如“RR=0.70”不如“风险从 10% 降至 7%,RR=0.70,RD=-3 个百分点”完整。
| 设计 | 如何选人/测量 | 最自然的指标 | 优点 | 主要局限 |
|---|---|---|---|---|
| 生态研究 | 比较群体层面的暴露与结局 | 群体率相关/率比 | 快速、适合政策或环境暴露 | 生态谬误、群体混杂 |
| 横断面研究 | 同一时点测暴露和结局 | 患病率、患病率比 | 描述负担、成本较低 | 时间顺序不清、幸存者偏倚 |
| 病例对照研究 | 按结局选病例和对照,回顾暴露 | OR | 适合罕见病、潜伏期长、可研究多个暴露 | 不能直接算风险;选择/回忆偏倚 |
| 队列研究 | 按暴露状态随访新发结局 | 风险、率、RR、RD、HR | 时间顺序明确,可研究多个结局 | 失访、时间变化混杂、成本高 |
| 随机对照试验 | 随机分配干预并随访 | RR、RD、HR | 随机化可平衡已知和未知混杂 | 伦理、依从性、推广性与成本 |
| 准实验/自然实验 | 利用政策或外生变化 | 差分、断点、工具变量效应 | 当随机化不可行时增强因果解释 | 设计假设通常不可直接验证 |
适合问题:“2026 年某市成年人高血压患病率是多少?”抽样框与加权决定估计能否代表目标人群。对持续时间长的疾病,横断面样本更容易捕捉长期存活病例,因此不适合简单推断病因时间顺序。
病例应来自明确定义的来源人群;对照应代表该来源人群产生病例的暴露分布。对照不是“没有任何疾病的人”,而是若其成为病例也会被研究纳入的人。病例对照研究中不能从抽取后的病例、对照比例直接计算发病风险。采用风险集(发病密度)抽样时,OR 可在不要求结局罕见的情况下估计发病率比;采用期末非病例作对照的累积抽样时,OR 估计疾病优势比,只有结局罕见且其他假设合适时才近似风险比。
常见变体包括巢式病例对照(nested case-control)和病例—队列(case-cohort),可节约昂贵的生物标志物测量。
队列可以是前瞻性或回顾性;两者都要求暴露信息在结局发生之前。设计时要定义:时间零点、纳入/排除标准、结局判定、随访机制和删失规则。避免让暴露组获得一段“必然存活”的时间而对照组没有,从而产生不死时间偏倚。
随机化的目标是产生可交换组;分配隐藏防止入组选择,盲法降低测量和行为差异。主要分析通常遵循意向治疗(intention-to-treat),保留随机化带来的可比性;按方案分析回答的是不同问题,易受依从性相关混杂影响。
集群随机试验需考虑组内相关;交叉试验需考虑洗脱期和残留效应;阶梯楔形设计需处理日历时间趋势。
当纳入、留存或分析的概率同时与暴露和结局相关时,比较组可被扭曲。例子包括:
改善方法主要来自设计:清楚来源人群、提高参与与随访、对两组用相同纳入规则、记录未参与原因。分析中可采用逆概率加权或敏感性分析,但依赖额外假设。
非差异性误分类并非总是把效应推向无效值;这一经验规则主要适用于某些二分类、独立误分类的简单情形。使用验证过的量表、统一培训、校准仪器、盲法判定和验证子研究可减少问题。
混杂因素通常满足:与暴露相关、是结局的原因或代理原因、且不在目标暴露的因果路径上。混杂是暴露组与对照组在“本来会发生什么”上的不可比性。
控制方法:
# 2 x 2 x 2 表:暴露 x 结局 x 基线风险层
strata_tab <- array(
0, dim = c(2, 2, 2),
dimnames = list(
exposure = c("Exposed", "Unexposed"),
outcome = c("Case", "Non-case"),
baseline_risk = c("Low", "High")
)
)
strata_tab[, , "Low"] <- matrix(c(9, 81, 10, 180), nrow = 2, byrow = TRUE)
strata_tab[, , "High"] <- matrix(c(60, 40, 29, 71), nrow = 2, byrow = TRUE)
rr_from_table <- function(x) {
(x[1, 1] / sum(x[1, ])) / (x[2, 1] / sum(x[2, ]))
}
crude_tab <- apply(strata_tab, c(1, 2), sum)
c(crude_RR = rr_from_table(crude_tab),
low_stratum_RR = rr_from_table(strata_tab[, , "Low"]),
high_stratum_RR = rr_from_table(strata_tab[, , "High"]))## crude_RR low_stratum_RR high_stratum_RR
## 2.700 1.900 2.069
##
## Mantel-Haenszel chi-squared test with continuity correction
##
## data: strata_tab
## Mantel-Haenszel X-squared = 20, df = 1, p-value = 0.000009
## alternative hypothesis: true common odds ratio is not equal to 1
## 95 percent confidence interval:
## 1.902 5.105
## sample estimates:
## common odds ratio
## 3.116
这个示例中两个层内 RR 接近 2,而粗 RR 更大,提示混杂。实际判断不应机械使用“变化 10%”规则;应依据因果知识、DAG、测量质量和目标效应决定调整集。
| 真实有病 | 真实无病 | |
|---|---|---|
| 检测阳性 | 真阳性 TP | 假阳性 FP |
| 检测阴性 | 假阴性 FN | 真阴性 TN |
灵敏度 ;特异度
阳性预测值 ;阴性预测值
阳性似然比 ;阴性似然比
灵敏度与特异度通常相对不依赖患病率,但会受疾病谱、验证方式和阈值影响。预测值强烈依赖被测人群的检测前概率(pre-test probability)。
diagnostic_metrics <- function(tp, fp, fn, tn) {
sensitivity <- tp / (tp + fn)
specificity <- tn / (tn + fp)
ppv <- tp / (tp + fp)
npv <- tn / (tn + fn)
c(sensitivity = sensitivity, specificity = specificity,
PPV = ppv, NPV = npv,
LR_positive = sensitivity / (1 - specificity),
LR_negative = (1 - sensitivity) / specificity)
}
round(diagnostic_metrics(tp = 90, fp = 180, fn = 10, tn = 720), 3)## sensitivity specificity PPV NPV LR_positive LR_negative
## 0.900 0.800 0.333 0.986 4.500 0.125
由贝叶斯公式:
predictive_values <- function(prevalence, sensitivity = 0.90, specificity = 0.80) {
ppv <- sensitivity * prevalence /
(sensitivity * prevalence + (1 - specificity) * (1 - prevalence))
npv <- specificity * (1 - prevalence) /
((1 - sensitivity) * prevalence + specificity * (1 - prevalence))
data.frame(prevalence, PPV = ppv, NPV = npv)
}
pv <- predictive_values(c(0.01, 0.05, 0.10, 0.30, 0.50))
round(pv, 3)低患病率人群即使使用不错的检测,也可能出现大量假阳性。这正是筛查阳性通常需要确认性检测的原因。
降低阳性阈值通常提高灵敏度、降低特异度。ROC 曲线画出所有阈值下的灵敏度对 特异度;AUC 描述随机抽取一名病例和一名非病例时,病例检测值更高的概率。AUC 不直接告诉你某一临床阈值是否合适,也不体现误诊代价或患病率。
set.seed(20260810)
status <- rep(c(0, 1), each = 250) # 0=无病,1=有病
marker <- c(rnorm(250, 0, 1), rnorm(250, 1.5, 1))
# 加入正负无穷阈值,确保 ROC 包含 (0,0) 和 (1,1) 两个端点。
thresholds <- c(Inf, sort(unique(marker), decreasing = TRUE), -Inf)
threshold_result <- do.call(rbind, lapply(thresholds, function(t) {
positive <- marker >= t
data.frame(
threshold = t,
sensitivity = mean(positive[status == 1]),
specificity = mean(!positive[status == 0])
)
}))
par(family = cjk_font)
plot(1 - threshold_result$specificity, threshold_result$sensitivity,
type = "l", lwd = 2, xlim = c(0, 1), ylim = c(0, 1),
xlab = "1 - 特异度(假阳性率)", ylab = "灵敏度",
main = "模拟检测的 ROC 曲线")
abline(0, 1, lty = 2, col = "gray50")筛查项目成立不仅要求检测准确,还要求疾病重要、存在可识别的早期阶段、早期干预优于症状后干预、后续诊断与治疗可及,并且总体获益超过过度诊断、假阳性、焦虑和资源占用等伤害。比较筛查后“确诊者生存时间”尤其要警惕领先时间偏倚、病程长度偏倚和过度诊断。
公共卫生监测是持续、系统地收集、分析、解释并传播健康数据以指导行动。常见形式:
评价维度包括简单性、灵活性、可接受性、数据质量、灵敏度、阳性预测值、代表性、及时性和稳定性。病例定义在监测中应保持一致;定义变化会制造人为趋势。
病例定义用于一致分类,不等同于临床诊断标准。早期调查可用敏感定义找全病例,分析阶段可采用更特异的“可能/确诊”分层。
set.seed(42)
onset <- as.Date("2026-06-01") + round(c(
rnorm(45, mean = 3, sd = 0.8),
rnorm(12, mean = 7, sd = 1.2)
))
onset <- onset[onset >= as.Date("2026-06-01")]
date_levels <- seq(min(onset), max(onset), by = "day")
daily_cases <- table(factor(onset, levels = date_levels))
par(family = cjk_font, mar = c(6, 4, 4, 2) + 0.1)
barplot(daily_cases, las = 2, col = "steelblue",
names.arg = format(date_levels, "%m-%d"), cex.names = 0.8,
xlab = "发病日期", ylab = "病例数",
main = "模拟暴发的流行曲线")单峰、陡升缓降可提示点源暴露;持续平台可能提示持续共同来源;多个相隔约一个潜伏期的波峰可能提示人际传播。但曲线形状还受病例发现、报告延迟和样本量影响,不能单独证明传播模式。
food <- data.frame(
item = c("鸡肉", "沙拉", "甜点", "饮用水"),
ill_ate = c(35, 28, 22, 30),
total_ate = c(50, 70, 40, 80),
ill_not_ate = c(5, 12, 18, 10),
total_not_ate = c(50, 30, 60, 20)
)
food$attack_rate_ate <- food$ill_ate / food$total_ate
food$attack_rate_not_ate <- food$ill_not_ate / food$total_not_ate
food$risk_ratio <- food$attack_rate_ate / food$attack_rate_not_ate
food[ , c("item", "attack_rate_ate", "attack_rate_not_ate", "risk_ratio")]高 RR、合理潜伏期和生物学机制共同支持可疑食物,但共食模式可能造成混杂。例如几乎所有吃鸡肉者也吃沙拉,就需要分层或多变量分析,并结合食品追踪和实验室证据。
传播动力学中,基本再生数 指全易感人群中一个典型感染者产生的平均继发感染数;有效再生数 随免疫、行为和干预变化。它们依赖模型、场景与时间,不是病原体固定常数。
潜在结局框架把每个人在暴露 与 下的潜在结局记为 和 。个体因果效应无法同时观察,但可定义总体平均处理效应:
研究前应写清:目标人群、处理策略、对照策略、结局、随访期限、汇总尺度和如何处理竞争事件。否则“暴露的影响”过于含糊。
这些条件大多不能完全由数据检验,必须结合领域知识、设计和敏感性分析论证。
观察性研究可先写一个理想“目标试验”:资格标准、处理策略、分配程序、时间零点、随访、结局和分析方案,再尽量用观察数据模拟。务必让资格判断、暴露分配和随访起点对齐,否则容易引入不死时间偏倚。
## R version 4.6.1 (2026-06-24)
## Platform: aarch64-apple-darwin23
## Running under: macOS Tahoe 26.5.1
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
##
## locale:
## [1] C.UTF-8/C.UTF-8/C.UTF-8/C/C.UTF-8/C.UTF-8
##
## time zone: America/Edmonton
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] survival_3.8-6
##
## loaded via a namespace (and not attached):
## [1] digest_0.6.39 R6_2.6.1 fastmap_1.2.0 Matrix_1.7-5 xfun_0.60
## [6] lattice_0.22-9 splines_4.6.1 cachem_1.1.0 knitr_1.51 htmltools_0.5.9
## [11] rmarkdown_2.31 lifecycle_1.0.5 cli_3.6.6 grid_4.6.1 sass_0.4.10
## [16] jquerylib_0.1.4 compiler_4.6.1 tools_4.6.1 evaluate_1.0.5 bslib_0.12.0
## [21] yaml_2.3.12 rlang_1.3.0 jsonlite_2.0.0
set.seed(123)
dat <- data.frame(
id = c(1:9, 9),
age = c(24, 31, 45, 52, NA, 67, 39, 150, 28, 28),
exposed = c(0, 1, 1, 0, 1, 0, 1, 0, 1, 1),
outcome = c(0, 0, 1, 0, 1, 1, 0, 1, NA, NA)
)
c(n_rows = nrow(dat), n_unique_id = length(unique(dat$id)),
duplicate_id = sum(duplicated(dat$id)))## n_rows n_unique_id duplicate_id
## 10 9 1
## id age exposed outcome
## 0 1 0 2
发现异常后应回到来源核验,而不是静默删除。预先区分“不适用”“拒答”“未知”“未测量”等缺失原因。
连续变量不应只因惯例报告均值;先看分布。偏态数据可用中位数和四分位距。分类变量同时报告分子和分母,尤其当缺失导致分母变化时。
## 'data.frame': 88 obs. of 5 variables:
## $ agegp : Ord.factor w/ 6 levels "25-34"<"35-44"<..: 1 1 1 1 1 1 1 1 1 1 ...
## $ alcgp : Ord.factor w/ 4 levels "0-39g/day"<"40-79"<..: 1 1 1 1 2 2 2 2 3 3 ...
## $ tobgp : Ord.factor w/ 4 levels "0-9g/day"<"10-19"<..: 1 2 3 4 1 2 3 4 1 2 ...
## $ ncases : num 0 0 0 0 0 0 0 0 0 0 ...
## $ ncontrols: num 40 10 6 5 27 7 4 7 2 1 ...
# 按年龄组汇总病例与对照
age_summary <- aggregate(cbind(ncases, ncontrols) ~ agegp,
data = esoph, FUN = sum)
age_summary$case_fraction <- with(age_summary, ncases / (ncases + ncontrols))
age_summarypar(family = cjk_font)
barplot(age_summary$case_fraction,
names.arg = age_summary$agegp,
col = "#4C78A8", ylim = c(0, 1),
xlab = "年龄组", ylab = "样本中的病例比例",
main = "esoph 内置病例对照数据(描述用途)")这里的“病例比例”由研究抽样决定,不是人群食管癌风险或患病率。
R 内置 esoph
数据按年龄、饮酒和吸烟分组,记录食管癌病例与对照数。原始分组变量是有序因子;下面显式转为无序因子,使默认处理对比下的指数化系数表示各类别相对于首个参考类别的条件
OR。
esoph_model <- transform(
esoph,
agegp = factor(agegp, levels = levels(agegp), ordered = FALSE),
alcgp = factor(alcgp, levels = levels(alcgp), ordered = FALSE),
tobgp = factor(tobgp, levels = levels(tobgp), ordered = FALSE)
)
fit <- glm(cbind(ncases, ncontrols) ~ agegp + alcgp + tobgp,
family = binomial(), data = esoph_model)
summary(fit)##
## Call:
## glm(formula = cbind(ncases, ncontrols) ~ agegp + alcgp + tobgp,
## family = binomial(), data = esoph_model)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -6.895 1.086 -6.35 2.2e-10 ***
## agegp35-44 1.981 1.104 1.79 0.07279 .
## agegp45-54 3.776 1.068 3.54 0.00041 ***
## agegp55-64 4.335 1.065 4.07 4.7e-05 ***
## agegp65-74 4.896 1.076 4.55 5.4e-06 ***
## agegp75+ 4.827 1.121 4.30 1.7e-05 ***
## alcgp40-79 1.435 0.250 5.74 9.6e-09 ***
## alcgp80-119 1.981 0.285 6.96 3.5e-12 ***
## alcgp120+ 3.603 0.385 9.36 < 2e-16 ***
## tobgp10-19 0.438 0.228 1.92 0.05504 .
## tobgp20-29 0.513 0.273 1.88 0.06040 .
## tobgp30+ 1.641 0.344 4.77 1.9e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 367.953 on 87 degrees of freedom
## Residual deviance: 82.337 on 76 degrees of freedom
## AIC: 221.4
##
## Number of Fisher Scoring iterations: 6
# 系数转换为 OR 和 Wald 95% CI
b <- coef(fit)
se <- sqrt(diag(vcov(fit)))
or_table <- data.frame(
term = names(b),
OR = exp(b),
lower_95 = exp(b - 1.96 * se),
upper_95 = exp(b + 1.96 * se),
row.names = NULL
)
# 去掉截距行,但保留变量名称,便于识别各 OR 的参考比较。
or_display <- transform(or_table[-1, ],
OR = round(OR, 3),
lower_95 = round(lower_95, 3),
upper_95 = round(upper_95, 3))
row.names(or_display) <- NULL
or_display解释分类变量系数时要明确参考组。模型调整后的 OR 仍依赖:无重要未测混杂、变量编码和函数形式合理、选择机制可忽略、数据足以支持参数等假设。不要把 OR 写成“风险增加了多少倍”。
事件计数可用 Poisson 回归,并把人时的对数作为 offset。指数化系数为发病率比。
rate_dat <- data.frame(
exposed = rep(c(0, 1), each = 3),
age_group = factor(rep(c("18-39", "40-64", "65+"), 2)),
cases = c(4, 18, 40, 8, 30, 55),
person_years = c(2200, 1800, 900, 2100, 1700, 800)
)
pois_fit <- glm(cases ~ exposed + age_group + offset(log(person_years)),
family = poisson(), data = rate_dat)
coef_table <- summary(pois_fit)$coefficients
c(adjusted_IRR_exposure = exp(coef(pois_fit)["exposed"]),
lower_95 = exp(coef(pois_fit)["exposed"] -
1.96 * coef_table["exposed", "Std. Error"]),
upper_95 = exp(coef(pois_fit)["exposed"] +
1.96 * coef_table["exposed", "Std. Error"]))## adjusted_IRR_exposure.exposed lower_95.exposed upper_95.exposed
## 1.647 1.194 2.272
# 粗略检查过度离散:远大于 1 时,Poisson 标准误可能偏小
sum(residuals(pois_fit, type = "pearson")^2) / df.residual(pois_fit)## [1] 0.15
如果方差显著大于均值,应检查模型错设、聚类、零膨胀,并考虑稳健标准误、准 Poisson 或负二项模型。重复测量、家庭/学校聚类和复杂抽样也需要相应方差估计。
简单完整病例分析可能损失精度并产生选择偏倚;均值填补会低估变异并破坏关系。多重插补通常比单次填补合理,但插补模型要包含结局、暴露、混杂、缺失预测因子及数据结构。对 MNAR 应做模式混合、选择模型或界限等敏感性分析。务必按变量和组别报告缺失比例与处理策略。
# 比较两组风险:对照 10%,希望检测到干预组 7%
power.prop.test(p1 = 0.10, p2 = 0.07, power = 0.80,
sig.level = 0.05, alternative = "two.sided")##
## Two-sample comparison of proportions power calculation
##
## n = 1355
## p1 = 0.1
## p2 = 0.07
## sig.level = 0.05
## power = 0.8
## alternative = two.sided
##
## NOTE: n is number in *each* group
这是独立、等大小、简单随机样本的近似。实际还要考虑失访、设计效应、事件数、非劣效界值、多重结局与模型参数。样本量规划应围绕主要 estimand 的精度或临床重要差异,而不只是追求 。
常见问题包括传播途径、潜伏期、二代攻击率、疫苗有效性、变异株严重程度和干预影响。
示例问题:在 2026 年流感季、某省 65 岁以上居民中,本季疫苗相对未接种是否降低实验室确诊流感住院的 90 天风险?
主题包括心血管病、癌症、糖尿病、营养、体力活动、睡眠与多病共存。长期暴露会变化,自报行为有测量误差,疾病亚临床阶段还可能改变行为并造成反向因果。
示例问题:无糖尿病成年人中,基线超加工食品摄入最高与最低五分位相比,10 年 2 型糖尿病发病风险有何差异?
研究空气污染、极端温度、噪声、化学品、职业暴露和气候事件。需要处理空间相关、暴露滞后、混合暴露、迁移和健康工人效应。
示例问题:城市每日 PM2.5 每增加 10 ,随后 0–3 天心血管急诊就诊率如何变化?可考虑时间序列或病例交叉设计,并控制季节、长期趋势、温度和星期。
主题包括孕期暴露、出生结局、发育、疫苗、学校环境和代际影响。选择活产儿可能形成碰撞点偏倚;胎龄既可能是结局、中介,也可能影响测量。
示例问题:孕早期野火烟雾暴露是否增加早产风险?暴露窗口、居住迁移和季节混杂必须预先定义。
研究收入、种族化过程、住房、教育、移民状态、歧视、邻里与政策等社会决定因素。个体“种族”常是社会结构与暴露的代理,不应被简单解释为生物机制。报告总体平均效应之外,还应评价利益和伤害在群体间如何分布。
示例问题:实施带薪病假政策后,低收入与高收入行业呼吸道感染缺勤差距是否缩小?可用差分中的差分,但需评估平行趋势和同期政策。
常见问题包括药物安全、比较效果、诊疗质量、等待时间、再入院和医疗可及性。处方适应证会造成适应证混杂;新使用者主动对照设计常比“现用者 vs 不用者”更可比。
示例问题:新开始药物 A 与药物 B 的房颤患者中,1 年内严重出血风险是否不同?应对齐首次用药、资格判断和随访起点,并明确换药、停药与死亡的处理。
某封闭工厂年初有 2,000 名无目标疾病员工。年内新发 80 例,所有人均完整随访一年。年末另有 40 名年初已患病者仍在职。
一年累积发病率为 。年末点患病率为 。发病率分母只包括期初无病、处于风险中的人;患病率分母包含时点上全部被观察人群,分子含所有现患病例。
一项队列研究中,暴露组 500 人出现 75 例,未暴露组 800 人出现 64 例。计算两组风险、RR、RD 和暴露者归因分值,并用一句话解释。
,,,。若因果假设成立,暴露与每 100 人约 7 个额外病例相关;暴露者病例中约 可归因于暴露。后一句是因果解释,需要无偏倚、无未控制混杂等条件。
某检测灵敏度 95%、特异度 90%。在患病率 1% 的 10,000 人中,预计 TP、FN、FP、TN 各是多少?阳性预测值是多少?
预计有病 100 人:TP=95、FN=5;无病 9,900 人:FP=990、TN=8,910。。多数阳性是假阳性,说明低患病率筛查需要确认检测和充分沟通。
为以下问题选择一个主要设计并说明最重要的偏倚:
可能答案:①病例对照,警惕回忆及对照选择偏倚;②概率抽样横断面,警惕无应答和病例定义差异;③学校集群随机试验,处理聚类、污染与依从性;④受控中断时间序列或合成对照,警惕同期干预、趋势和测量变化。实际选择还取决于数据、伦理和可行性。
| 中文 | English | 简要含义 |
|---|---|---|
| 目标人群 | target population | 希望将结论推广到的人群 |
| 来源人群 | source population | 实际产生研究对象/病例的人群 |
| 患病率 | prevalence | 某时点或期间现患病例比例 |
| 累积发病率/风险 | cumulative incidence/risk | 一段时间内发生新结局的概率 |
| 发病密度 | incidence rate | 单位人时的新事件速度 |
| 风险比 | risk ratio | 两组风险之比 |
| 风险差 | risk difference | 两组风险之差 |
| 优势比 | odds ratio | 两组 odds 之比 |
| 危险比 | hazard ratio | 两组瞬时危险之比,常由 Cox 模型估计 |
| 置信区间 | confidence interval | 与数据和模型相容的参数范围 |
| 随机误差 | random error | 抽样波动造成的不精确 |
| 选择偏倚 | selection bias | 选择机制扭曲比较 |
| 信息偏倚 | information bias | 测量或分类错误导致系统差异 |
| 混杂 | confounding | 组间潜在结局不可比造成的混合效应 |
| 效应修饰 | effect modification | 效应在不同层真实不同 |
| 碰撞点 | collider | 两个变量的共同后果 |
| 中介 | mediator | 位于暴露到结局因果路径上的变量 |
| 可交换性 | exchangeability | 比较组潜在结局可比 |
| 正值性 | positivity | 各协变量层均可能接受各处理 |
| 灵敏度 | sensitivity | 有病者中检测阳性的比例 |
| 特异度 | specificity | 无病者中检测阴性的比例 |
| 阳性预测值 | positive predictive value | 阳性者中真实有病比例 |
| 人时 | person-time | 所有个体处于风险时间之和 |
| 删失 | censoring | 结局时间只知超过某时点 |
| 竞争风险 | competing risk | 阻止目标结局发生的其他事件 |
| 估计目标 | estimand | 要回答的精确定量问题 |
| 敏感性分析 | sensitivity analysis | 检查假设变化对结果的影响 |
建议的学习顺序:先掌握分母、时间和 表,再学习研究设计与偏倚;之后进入回归、生存分析、复杂抽样和因果推断。每次看到一个结果,先问“谁进入了分母、暴露何时测量、比较组是否可比”,通常比先问用了哪一种高级模型更重要。