8  Chapter 8: 假設檢定:兩樣本推論 (hypothesis testing: two-sample inference)

兩組資料的對話:差異、配對與效果大小

8.1 本章學習目標

上一章我們處理一個樣本與一個基準值的比較。臨床與公衛研究更常見的問題是兩組比較:新治療與標準治療哪個好?介入前後是否改善?兩種照護模式的再住院率是否不同?這些都屬於兩樣本推論 (two-sample inference)。

讀完本章後,你應該能夠:

  1. 區分獨立樣本 (independent samples) 與配對樣本 (paired samples)。
  2. 執行兩獨立樣本 t 檢定 (two-sample t-test) 與 Welch t 檢定 (Welch’s t-test)。
  3. 執行配對 t 檢定 (paired t-test)。
  4. 比較兩個母群體比例 (two population proportions)。
  5. 建立平均差 (mean difference)、比例差 (risk difference) 與相對風險 (relative risk) 的估計與信賴區間。
  6. 從效果大小、信賴區間與臨床意義一起解讀兩組比較。

8.2 兩組比較先問:獨立還是配對?

兩組資料最重要的第一個問題不是「要按哪個軟體按鈕」,而是資料結構。若兩組來自不同個體,例如標準照護組與介入照護組各自收案不同病人,這通常是獨立樣本。若兩個測量值來自同一個人,例如治療前與治療後血壓,這是配對樣本。

獨立樣本與配對樣本的分析方法不同。配對資料的核心是每個人的前後差異,而不是把治療前與治療後當成兩群陌生人。若忽略配對,會浪費資訊,甚至得到錯誤結論。統計方法很重視資料關係;資料之間若本來有牽手,請不要硬把它們拆散。

8.3 兩獨立樣本平均值比較

兩獨立樣本平均值比較常用於比較兩組病人的連續結果,例如收縮壓、LDL-C、住院天數或疼痛分數。令 \(\mu_1\)\(\mu_2\) 為兩組母群體平均值,常見雙尾假設為:

\[ H_0: \mu_1 - \mu_2 = 0 \]

\[ H_1: \mu_1 - \mu_2 \ne 0 \]

若兩組變異數可視為相等,可使用 pooled two-sample t-test。但醫療資料中兩組變異數常不同,Welch t 檢定較穩健,通常是很好的預設選擇。

8.4 範例 1:兩組照護模式的收縮壓

假設某研究比較標準照護與介入照護在 12 週後的收縮壓。兩組病人互相獨立。

standard <- c(148, 142, 151, 139, 145, 150, 136, 144, 147, 153, 141, 149, 146, 138, 152, 143)
intervention <- c(136, 132, 141, 129, 135, 138, 131, 134, 137, 140, 133, 139, 130, 128, 142, 136)

bp_groups <- data.frame(
  sbp = c(standard, intervention),
  group = c(rep("標準照護", length(standard)), rep("介入照護", length(intervention))),
  stringsAsFactors = FALSE
)

# 確保組別排序符合描述
bp_groups$group <- factor(bp_groups$group, levels = c("標準照護", "介入照護"))

bp_groups %>%
  group_by(group) %>%
  summarise(
    count = n(),
    mean = mean(sbp),
    std = sd(sbp),
    .groups = "drop"
  ) %>%
  mutate(across(c(mean, std), ~ round(., 2)))
# A tibble: 2 × 4
  group    count  mean   std
  <fct>    <int> <dbl> <dbl>
1 標準照護    16  145.  5.16
2 介入照護    16  135.  4.33
p1 <- ggplot(bp_groups, aes(x = group, y = sbp, fill = group)) +
  geom_boxplot(alpha = 0.7, show.legend = FALSE) +
  geom_jitter(color = "#333333", alpha = 0.7, width = 0.12, height = 0) +
  scale_fill_brewer(palette = "Set2") +
  labs(
    title = "兩獨立樣本:12 週後收縮壓",
    x = "照護組別",
    y = "12 週後收縮壓 (mmHg)"
  )

p1
Figure 8.1: 兩獨立樣本:標準照護與介入照護在 12 週後的收縮壓分布。
welch_result <- t.test(intervention, standard, var.equal = FALSE)
mean_diff <- mean(intervention) - mean(standard)
se_welch <- sqrt(var(intervention) / length(intervention) + var(standard) / length(standard))
df_welch <- (var(intervention) / length(intervention) + var(standard) / length(standard))^2 / (
  (var(intervention) / length(intervention))^2 / (length(intervention) - 1) +
  (var(standard) / length(standard))^2 / (length(standard) - 1)
)

welch_test_df <- data.frame(
  quantity = c("平均差:介入-標準", "Welch t 統計量", "自由度", "p 值", "95% CI 下限", "95% CI 上限"),
  value = c(mean_diff, as.numeric(welch_result$statistic), df_welch, welch_result$p.value, welch_result$conf.int[1], welch_result$conf.int[2]),
  stringsAsFactors = FALSE
)
welch_test_df$value <- round(welch_test_df$value, 4)

welch_test_df
           quantity    value
1 平均差:介入-標準 -10.1875
2    Welch t 統計量  -6.0526
3            自由度  29.1220
4              p 值   0.0000
5       95% CI 下限 -13.6294
6       95% CI 上限  -6.7456

平均差為負值,代表介入組平均收縮壓較低。p 值與信賴區間都提供證據支持兩組平均值不同。不過,接著仍要問:下降幅度是否達到臨床上重要?是否有副作用、成本或可行性問題?統計顯著是門票,不是整場演出。

8.5 Pooled t 檢定與 Welch t 檢定

Pooled t 檢定假設兩組母群體變異數相同,會把兩組變異合併估計共同變異。Welch t 檢定不要求兩組變異數相等,並使用調整後自由度。實務上,若不確定變異數是否相等,Welch t 檢定通常較安全。

假設變異數相等與否不應只靠「看起來差不多」。更重要的是研究設計與資料來源是否支持這個假設。醫療資料裡,不同病情嚴重度、不同治療路徑常讓變異不一樣。

pooled_result <- t.test(intervention, standard, var.equal = TRUE)

pooled_vs_welch <- data.frame(
  method = c("Pooled t-test", "Welch t-test"),
  t_statistic = c(as.numeric(pooled_result$statistic), as.numeric(welch_result$statistic)),
  p_value = c(pooled_result$p.value, welch_result$p.value),
  stringsAsFactors = FALSE
)
pooled_vs_welch$t_statistic <- round(pooled_vs_welch$t_statistic, 4)
pooled_vs_welch$p_value <- round(pooled_vs_welch$p_value, 4)

pooled_vs_welch
         method t_statistic p_value
1 Pooled t-test     -6.0526       0
2  Welch t-test     -6.0526       0

8.6 配對樣本平均值比較

配對 t 檢定適用於同一個體的前後測量,或自然配對的資料,例如左右眼、同一病人兩種檢驗方法、同一社區介入前後。配對 t 檢定的關鍵不是比較兩組原始平均值,而是比較「差值」的平均是否為 0。

\(D_i = X_{after,i} - X_{before,i}\),假設為:

\[ H_0: \mu_D = 0 \]

\[ H_1: \mu_D \ne 0 \]

若差值平均顯著小於 0,代表治療後數值下降。

8.7 範例 2:同一批病人治療前後收縮壓

before <- c(152, 148, 155, 146, 150, 158, 149, 153, 147, 156, 151, 154)
after <- c(140, 138, 144, 139, 141, 146, 137, 142, 136, 145, 139, 143)
paired_df <- data.frame(id = 1:length(before), before = before, after = after)
paired_df$difference <- paired_df$after - paired_df$before

paired_df %>%
  select(before, after, difference) %>%
  summarise(across(everything(), list(mean = mean, std = sd))) %>%
  pivot_longer(everything(), names_to = c("variable", ".value"), names_pattern = "(.*)_(.*)") %>%
  mutate(across(c(mean, std), ~ round(., 2)))
# A tibble: 3 × 3
  variable    mean   std
  <chr>      <dbl> <dbl>
1 before     152.   3.75
2 after      141.   3.21
3 difference -10.8  1.48
paired_long <- paired_df %>%
  pivot_longer(cols = c(before, after), names_to = "time", values_to = "sbp") %>%
  mutate(time = factor(time, levels = c("before", "after"), labels = c("治療前", "治療後")))

paired_summary <- paired_long %>%
  group_by(time) %>%
  summarise(
    mean_sbp = mean(sbp),
    se_sbp = sd(sbp) / sqrt(n()),
    lower = mean_sbp - qt(0.975, df = n() - 1) * se_sbp,
    upper = mean_sbp + qt(0.975, df = n() - 1) * se_sbp,
    .groups = "drop"
  )

p2 <- ggplot(paired_long, aes(x = time, y = sbp)) +
  geom_line(aes(group = id), color = "#999999", alpha = 0.65) +
  geom_line(data = paired_summary, aes(x = time, y = mean_sbp, group = 1), color = "#d62828", linewidth = 1.2) +
  geom_errorbar(data = paired_summary, aes(x = time, y = mean_sbp, ymin = lower, ymax = upper), width = 0.1, color = "#d62828", linewidth = 1) +
  geom_point(data = paired_summary, aes(x = time, y = mean_sbp), color = "#d62828", size = 3) +
  labs(
    title = "配對樣本:同一批病人治療前後收縮壓",
    x = "時間",
    y = "收縮壓 (mmHg)"
  )

p2
Figure 8.2: 配對樣本:同一批病人治療前後收縮壓。灰線連接同一位病人。
paired_result <- t.test(after, before, paired = TRUE)
diff_mean <- mean(paired_df$difference)
diff_se <- sd(paired_df$difference) / sqrt(length(paired_df$difference))

paired_test_df <- data.frame(
  quantity = c("平均差:後-前", "配對 t 統計量", "p 值", "95% CI 下限", "95% CI 上限"),
  value = c(diff_mean, as.numeric(paired_result$statistic), paired_result$p.value, paired_result$conf.int[1], paired_result$conf.int[2]),
  stringsAsFactors = FALSE
)
paired_test_df$value <- round(paired_test_df$value, 4)

paired_test_df
       quantity    value
1 平均差:後-前 -10.7500
2 配對 t 統計量 -25.0807
3          p 值   0.0000
4   95% CI 下限 -11.6934
5   95% CI 上限  -9.8066

配對分析通常比把前後資料當獨立樣本更有效率,因為每位病人都作為自己的對照。個體間差異被抵消後,治療前後差異更容易被看見。

8.8 兩比例比較

若結果是二元事件,例如再住院、有無感染、是否達標,常比較兩組比例。常見效果量包括比例差、相對風險與勝算比。本章先聚焦比例差與相對風險。

比例差又稱風險差 (risk difference):

\[ \hat{p}_2 - \hat{p}_1 \]

相對風險 (relative risk, RR) 為:

\[ RR = \frac{\hat{p}_2}{\hat{p}_1} \]

比例差回答「絕對差多少」,相對風險回答「相對於原本是多少倍」。臨床與公衛決策常需要兩者一起看。

8.9 範例 3:兩組 30 天再住院率

prop_df <- data.frame(
  group = c("標準照護", "介入照護"),
  n = c(180, 175),
  readmit = c(42, 26),
  stringsAsFactors = FALSE
)
prop_df$rate <- prop_df$readmit / prop_df$n

# 鎖定組別排序
prop_df$group <- factor(prop_df$group, levels = c("標準照護", "介入照護"))

prop_df
     group   n readmit      rate
1 標準照護 180      42 0.2333333
2 介入照護 175      26 0.1485714
p3 <- ggplot(prop_df, aes(x = group, y = rate)) +
  geom_col(fill = "#8ab17d", width = 0.6) +
  geom_text(aes(label = sprintf("%.1f%%", rate * 100)), vjust = -0.5, size = 4) +
  scale_y_continuous(limits = c(0, 0.32)) +
  labs(
    title = "兩組 30 天再住院率",
    x = "照護組別",
    y = "30 天再住院率"
  )

p3
Figure 8.3: 兩組 30 天再住院率。
x1 <- prop_df$readmit[1]
n1 <- prop_df$n[1]
x2 <- prop_df$readmit[2]
n2 <- prop_df$n[2]

p1 <- x1 / n1
p2 <- x2 / n2
pooled_p <- (x1 + x2) / (n1 + n2)
se_null <- sqrt(pooled_p * (1 - pooled_p) * (1 / n1 + 1 / n2))
z_stat <- (p2 - p1) / se_null
p_value <- 2 * min(pnorm(z_stat), 1 - pnorm(z_stat))

diff_prop <- p2 - p1
se_diff_prop <- sqrt(p1 * (1 - p1) / n1 + p2 * (1 - p2) / n2)
ci_prop <- c(diff_prop - qnorm(0.975) * se_diff_prop, diff_prop + qnorm(0.975) * se_diff_prop)
relative_risk <- p2 / p1

prop_test_df <- data.frame(
  quantity = c("比例差:介入-標準", "比例差 CI 下限", "比例差 CI 上限", "z 統計量", "p 值", "相對風險"),
  value = c(diff_prop, ci_prop[1], ci_prop[2], z_stat, p_value, relative_risk),
  stringsAsFactors = FALSE
)
prop_test_df$value <- round(prop_test_df$value, 4)

prop_test_df
           quantity   value
1 比例差:介入-標準 -0.0848
2    比例差 CI 下限 -0.1660
3    比例差 CI 上限 -0.0036
4          z 統計量 -2.0290
5              p 值  0.0425
6          相對風險  0.6367

比例差為負值,表示介入照護組再住院率較低。相對風險小於 1,也支持介入組風險較低。實務上,比例差常更直觀,因為它可以轉成每 100 位病人少幾位再住院。

8.10 效果量與信賴區間

兩樣本推論的重點不只是 p 值,而是效果大小與不確定性。平均差、比例差、相對風險都可以搭配信賴區間呈現。

effect_df <- data.frame(
  outcome = c("收縮壓平均差\n介入-標準", "再住院率差\n介入-標準"),
  estimate = c(mean_diff, diff_prop),
  lower = c(welch_result$conf.int[1], ci_prop[1]),
  upper = c(welch_result$conf.int[2], ci_prop[2]),
  stringsAsFactors = FALSE
)

# 鎖定因子排序以利圖形顯示順序
effect_df$outcome <- factor(effect_df$outcome, levels = effect_df$outcome)

p4 <- ggplot(effect_df, aes(y = outcome, x = estimate)) +
  geom_errorbar(aes(xmin = lower, xmax = upper), width = 0.1, color = "#264653", linewidth = 1) +
  geom_point(color = "#d62828", size = 3) +
  geom_vline(xintercept = 0, color = "#666666", linetype = "dashed", linewidth = 1) +
  labs(
    title = "兩樣本推論的效果量",
    x = "效果估計與 95% 信賴區間",
    y = ""
  )

p4
Figure 8.4: 兩樣本推論的效果量與 95% 信賴區間。垂直虛線為沒有差異。

這張圖的尺度不同,主要用來示範概念:效果量最好搭配信賴區間一起報告。若信賴區間很寬,代表不確定性大;若信賴區間跨過 0,代表資料仍與沒有差異相容。

8.11 常見陷阱

  1. 把配對資料當獨立資料分析:會浪費資訊,甚至錯估標準誤。
  2. 只報 p 值不報平均差或比例差:讀者不知道差異大小。
  3. 忽略基準風險:相同相對風險在不同基準風險下,絕對影響不同。
  4. 未檢查變異數與分布形狀:極端偏態或離群值會影響 t 檢定。
  5. 把統計顯著當成治療值得採用:仍需考慮副作用、成本、可行性與病人偏好。

8.12 本章重點整理

  • 兩組比較前先判斷資料是獨立樣本或配對樣本。
  • Welch t 檢定適合兩獨立樣本平均值比較,且不要求兩組變異數相等。
  • 配對 t 檢定分析的是同一個體的差值。
  • 兩比例比較可用比例差、相對風險與 p 值描述。
  • 效果大小與信賴區間比單獨 p 值提供更多臨床資訊。
  • 統計顯著不等於臨床顯著,兩者應分開討論。

8.13 小練習

  1. 說明獨立樣本與配對樣本的差異,並各舉一個醫學例子。
  2. 某研究兩組平均 LDL-C 分別為 108 與 121 mg/dL,你會如何定義平均差方向?
  3. 若兩組再住院率分別為 12% 與 18%,計算比例差與相對風險。
  4. 說明為何配對 t 檢定通常比未配對分析更有效率。
  5. 解釋「p 值顯著但比例差只有 1%」在臨床上可能代表什麼。
p_standard <- 0.18
p_intervention <- 0.12
practice_risk_difference <- p_intervention - p_standard
practice_relative_risk <- p_intervention / p_standard

practice_results <- data.frame(
  quantity = c("比例差:介入-標準", "相對風險:介入/標準"),
  value = c(practice_risk_difference, practice_relative_risk),
  stringsAsFactors = FALSE
)
practice_results$value <- round(practice_results$value, 4)

practice_results
             quantity   value
1   比例差:介入-標準 -0.0600
2 相對風險:介入/標準  0.6667

8.14 Glossary

中文術語 English term 說明
兩樣本推論 two-sample inference 比較兩組母群體參數的推論方法。
獨立樣本 independent samples 兩組觀察值來自不同個體且彼此獨立。
配對樣本 paired samples 兩個觀察值具有自然配對關係,例如同一人前後測。
兩獨立樣本 t 檢定 two-sample t-test 比較兩獨立樣本平均值的 t 檢定。
Welch t 檢定 Welch's t-test 不假設兩組變異數相等的兩樣本 t 檢定。
Pooled t 檢定 pooled t-test 假設兩組變異數相等並合併變異估計的 t 檢定。
配對 t 檢定 paired t-test 比較配對差值平均是否為 0 的 t 檢定。
平均差 mean difference 兩組平均值之差。
比例差 risk difference 兩組事件比例之差,也稱風險差。
相對風險 relative risk 兩組事件比例的比值。
效果量 effect size 差異或關聯大小的量化指標。
信賴區間 confidence interval 量化估計不確定性的區間。
統計顯著性 statistical significance 結果達到預設顯著水準的狀態。
臨床顯著性 clinical significance 結果是否具有實際臨床重要性。