R 统计分析:t.test() 与 lm()
在 R 中比较两组数据的均值差异,最常用的方法是 t.test() 函数。但很多人不知道,线性模型函数 lm() 其实也能完成同样的任务,而且结果完全一致。掌握这两种方法的关系,能让你更灵活地处理统计问题。
理解 t 检验的本质
t 检验用于判断两个独立样本(或配对样本)的均值是否存在显著差异。R 中的 t.test() 默认执行的是双样本、方差不齐的 Welch t 检验。
而线性模型 lm(y ~ x) 在 x 是一个二分类因子时,本质上就是在估计两组之间的均值差,并进行假设检验。其 t 统计量和 p 值与 t.test() 完全等价。
步骤一:准备示例数据
创建一个包含两组数值的数据框:
# 设置随机种子确保结果可复现
set.seed(123)
# 生成两组数据:A 组 30 个观测,均值 5;B 组 30 个观测,均值 6
group_A <- rnorm(30, mean = 5, sd = 1.2)
group_B <- rnorm(30, mean = 6, sd = 1.2)
# 合并为长格式数据框(适合 lm)
data <- data.frame(
value = c(group_A, group_B),
group = factor(c(rep("A", 30), rep("B", 30)))
)
此时 data 包含两列:value(数值)和 group(因子,取值 "A" 或 "B")。
步骤二:用 t.test() 进行两样本 t 检验
运行标准的双样本 t 检验:
t_result <- t.test(value ~ group, data = data)
print(t_result)
输出中重点关注:
t = -3.12(t 统计量)df = 57.8(自由度,注意不是整数,说明用了 Welch 校正)p-value = 0.0028(双侧 p 值)mean in group A = 4.93,mean in group B = 5.89(两组均值)
这说明在 0.05 显著性水平下,两组均值差异显著。
步骤三:用 lm() 实现等价的 t 检验
拟合一个以 group 为预测变量的线性模型:
lm_result <- lm(value ~ group, data = data)
summary(lm_result)
查看输出中的系数表(Coefficients):
| | Estimate | Std. Error | t value | Pr(>|t|) |
| :------------- | -------: | ---------: | ------: | -------: |
| (Intercept) | 4.93 | 0.21 | 23.48 | <2e-16 |
| groupB | 0.96 | 0.31 | 3.12 | 0.0028 |
关键点:
(Intercept)对应 group A 的均值(因为 A 是参考组)。groupB的系数(0.96)表示 B 组比 A 组高 0.96,即均值差。groupB的 t 值(3.12)和 p 值(0.0028)与t.test()完全一致(符号相反是因为 t.test 默认 A-B,而 lm 是 B-A)。
注意:
lm()默认使用方差齐性假设(即 pooled variance),而t.test()默认使用 Welch 校正(方差不齐)。要让两者完全等价,需在t.test()中设置var.equal = TRUE。
步骤四:强制方差齐性使结果完全一致
重新运行 t 检验并指定方差相等:
t_equal <- t.test(value ~ group, data = data, var.equal = TRUE)
print(t_equal)
此时输出:
t = -3.12df = 58(整数,因为假设方差齐)p-value = 0.0028
再看 lm() 的结果,其残差标准误基于合并方差计算,因此:
lm()的 t 值 = 3.12- 自由度 = 60 - 2 = 58
- p 值 = 0.0028
两者现在在数值上完全对应。
步骤五:理解背后的数学关系
当 x 是二分类因子(0/1 编码)时,线性模型:
$$ y_i = \beta_0 + \beta_1 x_i + \varepsilon_i $$
其中:
- $\beta_0$ 是参照组(x=0)的均值
- $\beta_1$ 是两组均值之差
t 检验的原假设 $H_0: \mu_1 = \mu_2$ 等价于 $H_0: \beta_1 = 0$。
在方差齐性假设下,lm() 对 $\beta_1$ 的 t 检验与经典 Student's t 检验完全等价。
何时用哪个?
| 场景 | 推荐函数 | 理由 |
|---|---|---|
| 快速比较两组均值 | t.test() |
语法简单,直接输出均值、置信区间 |
| 后续要做多组比较(如 ANOVA)或加入协变量 | lm() |
可无缝扩展到更复杂模型 |
| 需要统一建模框架(如报告回归系数) | lm() |
结果结构与其他回归一致 |
| 不确定方差是否齐性 | t.test()(默认) |
自动使用 Welch 校正,更稳健 |
扩展:配对 t 检验也能用 lm() 吗?
可以,但需要技巧。配对 t 检验本质是对差值做单样本 t 检验。
假设有前后测数据 pre 和 post:
# 方法1:直接用 t.test
t.test(post, pre, paired = TRUE)
# 方法2:用 lm 对差值建模
diff <- post - pre
lm(diff ~ 1) # 拟合截距模型
lm(diff ~ 1) 的系数就是平均差值,其 t 检验与配对 t 检验一致。
但注意:不能直接用 lm(post ~ pre) 来替代配对 t 检验——那是回归,不是配对比较。
验证你的结果是否一致
编写一个验证脚本确保两种方法输出匹配:
# 提取 t.test 的 t 值和 p 值(方差齐)
tt <- t.test(value ~ group, data = data, var.equal = TRUE)
t_ttest <- tt$statistic
p_ttest <- tt$p.value
# 提取 lm 的对应值
lm_mod <- lm(value ~ group, data = data)
coefs <- summary(lm_mod)$coefficients
t_lm <- coefs["groupB", "t value"]
p_lm <- coefs["groupB", "Pr(>|t|)"]
# 比较(允许微小浮点误差)
all.equal(abs(t_ttest), abs(t_lm)) # 应返回 TRUE
all.equal(p_ttest, p_lm) # 应返回 TRUE
```
如果返回 `TRUE`,说明你的实现正确。
---
## 处理因子参考水平
`lm()` 的结果依赖于因子的**参考水平**。默认按字母顺序,"A" 是参照组。
**改变**参考组以调整解释方向:
```r
# 将 "B" 设为参照组
data$group <- relevel(data$group, ref = "B")
lm_new <- lm(value ~ group, data = data)
summary(lm_new)
现在 (Intercept) 是 B 组均值,groupA 系数是 A - B 的差值,t 值符号会反转,但 p 值不变。
这在解释结果时非常有用,尤其当你想让“对照组”作为基准。
总结关键对应关系
t.test(..., var.equal = TRUE)的 均值差 =lm()中非截距项的 Estimatet.test的 t 统计量(绝对值) =lm()系数表中的 t value- 两者 p 值 完全相同(在方差齐性假设下)
t.test的 自由度 =lm()的 残差自由度(n - 2)
掌握这种对应关系,你就能在简单检验和复杂模型之间自由切换,而不必重复学习两套逻辑。

暂无评论,快来抢沙发吧!