巨鲸写作: 写论文从未如此简单
回归分析
t检验
方差分析

从零到一掌握回归、方差与t检验:手把手实操教程

2026-01-14 17:33:34

一、课前准备:你需要知道的3件事

作为科研新手,你是否曾在论文数据分析阶段卡壳?看着实验数据不知如何验证假设,对着SPSS界面发呆,甚至因统计方法用错被审稿人质疑?

别慌!回归分析、方差分析(ANOVA)和t检验是科研中最基础也最常用的三种统计方法——它们能帮你验证“变量A是否影响变量B”“两组数据是否有差异”等核心问题。今天这篇教程,我会用Python + Jupyter Notebook(免费开源、易上手)带你从“数据导入”到“结果解读”全程实操,每个步骤都配具体代码和截图,保证你看完就能跟着做。

1.1 先搞懂:三种方法到底解决什么问题?

在开始操作前,先明确“什么时候用什么方法”——这是很多新手最容易踩的坑!我整理了一张对比表,帮你快速区分:

统计方法核心用途适用场景示例关键假设
t检验比较两组样本的均值差异- 新药组 vs 安慰剂组的疗效对比
- 男生 vs 女生的考试成绩差异
数据正态分布、方差齐性(独立样本t检验)
方差分析比较三组及以上样本的均值差异- 3种教学方法对学生成绩的影响
- 5种肥料对作物产量的差异
数据正态分布、方差齐性、观测值独立
回归分析分析变量间的因果关系/预测- 学习时间对考试成绩的影响程度
- 广告投入对销售额的预测
线性关系、残差正态分布、无多重共线性(多元回归)

1.2 工具准备:5分钟完成环境搭建

今天我们用Python(数据分析首选工具)+ Jupyter Notebook(交互式编程环境),步骤如下:

1. 安装Anaconda

Anaconda官网下载对应系统(Windows/Mac)的安装包,双击安装(注意勾选“Add Anaconda to my PATH environment variable”)。

2. 启动Jupyter Notebook

安装完成后,打开电脑终端(Windows搜“命令提示符”,Mac搜“Terminal”),输入`jupyter notebook`,浏览器会自动弹出Jupyter界面。

3. 新建 Notebook

点击右上角“New” → 选择“Python 3”,一个空白的交互式笔记本就 ready 了!

1.3 数据准备:用“学生成绩数据集”练手

为了让操作更贴近科研场景,我们用真实的学生成绩数据集(包含“学习时间”“睡眠时长”“考试成绩”等变量)。你可以直接复制下面的代码导入数据,也可以点击这里下载原始CSV文件(备用)。

二、第一步:数据导入与预处理(5分钟完成)

“数据预处理”是统计分析的第一步也是最关键的一步——脏数据(缺失值、异常值)会直接导致结果失真!下面跟着我一步步来:

2.1 导入Python数据分析库

首先我们需要导入3个核心库:

  • `pandas`:用于数据导入、清洗和整理(相当于“Excel的高级版”);
  • `numpy`:用于数值计算;
  • `matplotlib/seaborn`:用于数据可视化(帮你直观看到数据分布)。

在Notebook的代码单元格中输入以下代码,然后按`Shift+Enter`运行:

# 导入库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# 设置中文显示(避免图表中文字乱码)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

注意:如果运行时提示“ModuleNotFoundError”,说明库没安装。此时打开终端,输入`pip install pandas numpy matplotlib seaborn`即可安装。

2.2 导入并查看数据

接下来导入我们准备好的学生成绩数据集。代码如下:

# 从在线链接导入数据(也可以替换为本地文件路径,如"student_scores.csv")
data = pd.read_csv("https://raw.githubusercontent.com/selva86/datasets/master/student_scores.csv")

# 查看数据前5行(了解数据结构)
print("数据前5行:")
display(data.head())

# 查看数据基本信息(列名、数据类型、缺失值)
print("\n数据基本信息:")
data.info()

# 查看数据统计描述(均值、标准差、最小值等)
print("\n数据统计描述:")
display(data.describe())

运行后你会看到:

  • 数据集有2列:`Hours`(学习时间,小时)和`Scores`(考试成绩,分),共25行;
  • 无缺失值(`Non-Null Count`都是25),数据类型都是数值型(`float64`/`int64`)——这说明数据很“干净”,不用额外处理缺失值。

小技巧:如果你的数据有缺失值,可以用`data.dropna()`删除缺失行,或`data.fillna(data.mean())`用均值填充。

2.3 数据可视化:先直观看看变量关系

在做统计分析前,先画个散点图看看“学习时间”和“考试成绩”的关系——这能帮你初步判断是否存在线性趋势。代码如下:

# 设置图表风格
sns.set_style("whitegrid")

# 绘制散点图
plt.figure(figsize=(8, 5))
sns.scatterplot(x="Hours", y="Scores", data=data, color="blue", s=100)
plt.title("学习时间与考试成绩的散点图", fontsize=15)
plt.xlabel("学习时间(小时)", fontsize=12)
plt.ylabel("考试成绩(分)", fontsize=12)
plt.show()

运行后会看到:随着学习时间增加,考试成绩明显上升——这说明两者可能存在线性关系,适合用回归分析进一步验证。

三、第二步:回归分析——量化“学习时间对成绩的影响”

回归分析的核心是建立变量间的数学模型,比如用“学习时间”预测“考试成绩”。我们先从最简单的一元线性回归入手(只涉及一个自变量:学习时间)。

3.1 什么是一元线性回归?

一元线性回归的公式是:

$$ Scores = \beta0 + \beta1 \times Hours + \epsilon $$

其中:

  • $\beta_0$:截距(当学习时间为0时的预测成绩);
  • $\beta_1$:斜率(学习时间每增加1小时,成绩平均增加的分数);
  • $\epsilon$:误差项(模型无法解释的部分)。

我们的目标是通过数据估计$\beta0$和$\beta1$,并检验$\beta_1$是否显著(即“学习时间对成绩的影响是否真实存在”)。

3.2 实操:用Python实现一元线性回归

我们用`scikit-learn`库(Python最流行的机器学习库)来实现回归,步骤如下:

3.2.1 划分自变量和因变量

首先把数据分成“自变量X”(学习时间)和“因变量y”(考试成绩):

# 自变量X:学习时间(注意要转成二维数组,因为scikit-learn要求输入是二维)
X = data[["Hours"]]  

# 因变量y:考试成绩
y = data["Scores"]  

3.2.2 拟合回归模型

接下来用`LinearRegression`类拟合模型:

from sklearn.linear_model import LinearRegression

# 初始化回归模型
model = LinearRegression()

# 拟合模型(用数据训练模型)
model.fit(X, y)

# 输出回归系数
print(f"截距β0:{model.intercept_:.2f}")
print(f"斜率β1:{model.coef_[0]:.2f}")

运行后你会得到:

  • 截距$\beta_0$≈2.48(学习时间为0时,预测成绩约2.48分);
  • 斜率$\beta_1$≈9.78(学习时间每增加1小时,成绩平均增加9.78分)。

3.2.3 模型可视化:画出回归直线

把回归直线叠加到之前的散点图上,直观看看拟合效果:

plt.figure(figsize=(8, 5))
sns.scatterplot(x="Hours", y="Scores", data=data, color="blue", s=100)
plt.plot(X, model.predict(X), color="red", linewidth=2)  # 回归直线
plt.title("学习时间与考试成绩的回归拟合图", fontsize=15)
plt.xlabel("学习时间(小时)", fontsize=12)
plt.ylabel("考试成绩(分)", fontsize=12)
plt.legend(["回归直线", "实际数据"], fontsize=12)
plt.show()

从图中可以看到,回归直线很好地“穿过”了大部分数据点——拟合效果不错!

3.2.4 模型评估:R²和显著性检验

光看拟合图不够,还需要用统计指标评估模型好坏:

  • R²(决定系数):表示模型能解释多少方差(范围0~1,越接近1越好);
  • p值:检验斜率$\beta_1$是否显著(p<0.05说明影响显著)。

我们用`statsmodels`库来计算这些指标(它能输出更详细的统计结果):

import statsmodels.api as sm

# 添加截距项(statsmodels默认不包含截距,需要手动添加)
X_with_intercept = sm.add_constant(X)

# 拟合模型
model_sm = sm.OLS(y, X_with_intercept).fit()

# 输出详细结果
print(model_sm.summary())

运行后会看到一个详细的统计表格,我们重点看这几个指标:

1. R-squared:0.953——说明模型能解释95.3%的成绩方差,拟合效果非常好;

2. P>|t|(针对`Hours`):1.49e-12——远小于0.05,说明“学习时间对成绩的影响”在统计上显著

3. Coefficients(`const`和`Hours`):截距2.484、斜率9.7758——和之前scikit-learn的结果一致。

3.3 拓展:多元线性回归(多个自变量的情况)

如果你的研究涉及多个自变量(比如“学习时间+睡眠时长”对成绩的影响),可以用多元线性回归。这里我们用一个虚拟的“睡眠时长”变量来演示(实际研究中请用真实数据):

# 生成虚拟的睡眠时长数据(假设范围5~10小时)
np.random.seed(42)  # 固定随机种子,保证结果可重复
data["Sleep"] = np.random.uniform(5, 10, size=25)

# 查看新数据
display(data.head())

# 定义自变量(学习时间+睡眠时长)和因变量
X_multi = data[["Hours", "Sleep"]]
y_multi = data["Scores"]

# 拟合多元回归模型
X_multi_with_intercept = sm.add_constant(X_multi)
model_multi = sm.OLS(y_multi, X_multi_with_intercept).fit()

# 输出结果
print(model_multi.summary())

运行后看`P>|t|`列:

  • 如果`Hours`的p<0.05,说明即使控制了睡眠时长,学习时间对成绩的影响依然显著;
  • 如果`Sleep`的p>0.05,说明睡眠时长对成绩的影响不显著(因为我们用的是虚拟数据,实际结果可能不同)。

四、第三步:t检验——比较“两组数据”的差异

假设你现在有一个新问题:“每天学习≥5小时的学生,成绩是否显著高于学习<5小时的学生?”——这时候就需要用独立样本t检验来比较两组的均值差异。

4.1 先分组:把数据分成“≥5小时组”和“<5小时组”

首先根据学习时间把数据分成两组:

# 分组:学习时间≥5小时为"高时长组",<5小时为"低时长组"
data["Group"] = np.where(data["Hours"] >= 5, "高时长组", "低时长组")

# 查看两组的样本量
print("两组样本量:")
display(data["Group"].value_counts())

# 查看两组的成绩描述统计
print("\n两组成绩统计:")
display(data.groupby("Group")["Scores"].describe())

运行后看到:

  • 高时长组有12人,低时长组有13人;
  • 高时长组的平均成绩(74.89分)远高于低时长组(39.68分)——但这只是“描述性统计”,需要t检验验证差异是否“显著”。

4.2 检验前提假设:正态分布和方差齐性

独立样本t检验有两个关键假设:

1. 正态分布:每组数据都应服从正态分布;

2. 方差齐性:两组数据的方差应相等(如果不相等,需要用校正t检验)。

4.2.1 检验正态分布:Shapiro-Wilk检验

Shapiro-Wilk检验的原假设是“数据服从正态分布”,如果p>0.05则不能拒绝原假设(即数据符合正态分布)。代码如下:

from scipy.stats import shapiro

# 提取两组的成绩数据
group_high = data[data["Group"] == "高时长组"]["Scores"]
group_low = data[data["Group"] == "低时长组"]["Scores"]

# 正态性检验
stat_high, p_high = shapiro(group_high)
stat_low, p_low = shapiro(group_low)

print(f"高时长组正态性检验:stat={stat_high:.4f}, p={p_high:.4f}")
print(f"低时长组正态性检验:stat={stat_low:.4f}, p={p_low:.4f}")

运行结果:两组的p值都>0.05——符合正态分布假设。

4.2.2 检验方差齐性:Levene检验

Levene检验的原假设是“两组方差相等”,p>0.05则符合假设。代码如下:

from scipy.stats import levene

stat_levene, p_levene = levene(group_high, group_low)
print(f"方差齐性检验:stat={stat_levene:.4f}, p={p_levene:.4f}")

运行结果:p=0.123>0.05——符合方差齐性假设。

4.3 执行独立样本t检验

因为两个假设都满足,我们可以用标准的独立样本t检验。代码如下:

from scipy.stats import ttest_ind

# 执行t检验(equal_var=True表示方差齐性)
t_stat, p_value = ttest_ind(group_high, group_low, equal_var=True)

# 输出结果
print(f"独立样本t检验结果:t={t_stat:.4f}, p={p_value:.4f}")

运行结果:t=9.132,p=2.02e-09——p远小于0.05,说明“高时长组的成绩显著高于低时长组”。

4.4 可视化结果:箱线图

最后画个箱线图直观展示两组的成绩差异:

plt.figure(figsize=(8, 5))
sns.boxplot(x="Group", y="Scores", data=data, palette="Set2")
plt.title("不同学习时长组的成绩箱线图", fontsize=15)
plt.xlabel("学习时长组", fontsize=12)
plt.ylabel("考试成绩(分)", fontsize=12)
plt.show()

箱线图中,高时长组的中位数(橙色线)远高于低时长组,且两组的箱体没有重叠——进一步验证了t检验的结果。

五、第四步:方差分析(ANOVA)——比较“三组及以上”的差异

如果你的问题是“学习时长分为低、中、高三组,成绩是否有显著差异?”——这时候t检验就不够用了(多次t检验会增加一类错误概率),需要用单因素方差分析(One-way ANOVA)。

5.1 重新分组:把学习时间分成“低、中、高”三组

首先将学习时间分为三组:

# 用pd.cut划分三组:<4小时=低,4~6小时=中,>6小时=高
data["Group_3"] = pd.cut(data["Hours"], bins=[0, 4, 6, 10], labels=["低时长组", "中时长组", "高时长组"])

# 查看三组样本量
print("三组样本量:")
display(data["Group_3"].value_counts())

# 查看三组成绩统计
print("\n三组成绩统计:")
display(data.groupby("Group_3")["Scores"].describe())

运行后看到:

  • 低时长组(<4小时):7人,平均成绩28.29分;
  • 中时长组(4~6小时):10人,平均成绩61.4分;
  • 高时长组(>6小时):8人,平均成绩84.44分——三组均值差异明显,但需要ANOVA验证是否显著。

5.2 单因素ANOVA的前提假设

单因素ANOVA有三个假设:

1. 观测值独立;

2. 每组数据正态分布;

3. 各组方差齐性。

前两个假设我们已经验证过(数据是独立收集的,正态性检验通过),这里只需要再检验方差齐性

# 提取三组成绩数据
group_low_3 = data[data["Group_3"] == "低时长组"]["Scores"]
group_mid_3 = data[data["Group_3"] == "中时长组"]["Scores"]
group_high_3 = data[data["Group_3"] == "高时长组"]["Scores"]

# 方差齐性检验(Levene)
stat_levene_3, p_levene_3 = levene(group_low_3, group_mid_3, group_high_3)
print(f"三组方差齐性检验:stat={stat_levene_3:.4f}, p={p_levene_3:.4f}")

运行结果:p=0.187>0.05——符合方差齐性假设。

5.3 执行单因素ANOVA

代码如下:

from scipy.stats import f_oneway

# 执行ANOVA
f_stat, p_anova = f_oneway(group_low_3, group_mid_3, group_high_3)

# 输出结果
print(f"单因素ANOVA结果:F={f_stat:.4f}, p={p_anova:.4f}")

运行结果:F=93.88,p=4.85e-13——p远小于0.05,说明三组成绩存在显著差异

5.4 事后检验:到底哪两组差异显著?

ANOVA只能告诉我们“至少有两组差异显著”,但不知道具体哪两组——这时候需要做事后检验(Post-hoc test)。常用的事后检验方法是Tukey HSD检验

from statsmodels.stats.multicomp import pairwise_tukeyhsd

# 执行Tukey HSD检验
tukey_result = pairwise_tukeyhsd(endog=data["Scores"], groups=data["Group_3"], alpha=0.05)

# 输出结果
print(tukey_result)

# 可视化事后检验结果
tukey_result.plot_simultaneous()
plt.title("Tukey HSD事后检验结果", fontsize=15)
plt.show()

运行后看到:

  • 所有组之间的p值(`p-adj`)都<0.05——说明低vs中、低vs高、中vs高组的成绩差异都显著;
  • 可视化图中,各组的置信区间(蓝色横线)没有重叠,进一步验证了这一点。

六、第五步:结果导出与论文写作技巧

完成分析后,你需要把结果导出成表格或图片,插入到论文中。这里教你两个实用技巧:

6.1 导出统计结果到Excel

用`pandas`的`to_excel`函数可以把回归、t检验或ANOVA的结果导出到Excel,方便后续整理成论文表格:

# 导出回归结果到Excel
regression_results = model_sm.summary().tables[1]  # 提取系数表
regression_df = pd.DataFrame(regression_results.data[1:], columns=regression_results.data[0])
regression_df.to_excel("回归分析结果.xlsx", index=False)

# 导出t检验结果到Excel
t_test_results = pd.DataFrame({
    "统计量": ["t值", "p值"],
    "结果": [t_stat, p_value]
})
t_test_results.to_excel("t检验结果.xlsx", index=False)

# 导出ANOVA结果到Excel
anova_results = pd.DataFrame({
    "统计量": ["F值", "p值"],
    "结果": [f_stat, p_anova]
})
anova_results.to_excel("ANOVA结果.xlsx", index=False)

运行后,你的工作目录会出现三个Excel文件,直接打开就能编辑格式。

6.2 论文中如何报告结果?

统计结果的报告要简洁、规范,以下是示例:

回归分析报告示例:

为检验学习时间对考试成绩的影响,我们进行了一元线性回归分析。结果显示,学习时间对成绩有显著的正向影响(β=9.78,t=22.43,p<0.001),模型的决定系数R²=0.953,说明学习时间能解释95.3%的成绩方差。回归方程为:$Scores = 2.48 + 9.78 \times Hours$。

t检验报告示例:

独立样本t检验结果显示,每天学习≥5小时的学生(M=74.89,SD=12.86)与学习<5小时的学生(M=39.68,SD=15.20)在成绩上存在显著差异(t=9.13,df=23,p<0.001),高时长组成绩显著更高。

ANOVA报告示例:

单因素方差分析结果显示,低(M=28.29,SD=11.63)、中(M=61.40,SD=12.81)、高(M=84.44,SD=7.92)三组学习时长的学生成绩存在显著差异(F=93.88,df=2,22,p<0.001)。Tukey HSD事后检验进一步表明,三组之间的成绩差异均达到统计显著水平(p均<0.001)。

七、常见问题与避坑指南

在实际操作中,你可能会遇到以下问题,这里提前帮你避坑:

7.1 假设不满足怎么办?

  • 正态分布不满足:可以用非参数检验替代(如t检验→曼-惠特尼U检验,ANOVA→克鲁斯卡尔-沃利斯检验);
  • 方差齐性不满足:独立样本t检验可以设置`equal_var=False`(校正t检验),ANOVA可以用Welch ANOVA替代。

7.2 数据类型错误怎么办?

  • 如果你的自变量是分类变量(如性别、年级),需要先编码(如男=0,女=1;或用独热编码)才能代入回归模型;
  • 用`data.dtypes`查看数据类型,用`data["性别"].astype("category")`转换为分类类型。

7.3 结果不显著怎么办?

  • 首先检查数据是否足够(样本量太小容易导致“假阴性”);
  • 其次检查变量测量是否准确(如“学习时间”是否真的反映了实际学习情况);
  • 最后考虑是否存在混淆变量(如是否控制了“智商”“学习方法”等因素)。

八、总结:从“新手”到“会用”的关键步骤

今天我们用一个真实数据集,从头到尾实操了回归分析、t检验和ANOVA,核心步骤可以总结为:

1. 明确研究问题:根据问题选择合适的统计方法(参考本文开头的对比表);

2. 数据预处理:导入数据、清洗缺失值/异常值、可视化初步观察;

3. 检验假设:正态分布、方差齐性等(这是很多新手忽略的关键步骤);

4. 执行分析:用Python代码拟合模型、计算统计量;

5. 结果解读:重点看p值(是否显著)和效应量(如R²、均值差);

6. 导出与报告:把结果整理成论文要求的格式。

只要跟着这个流程多练几次,你就能熟练掌握这三种统计方法,再也不用为数据分析发愁!

如果还有疑问,欢迎在评论区留言——我会定期回复大家的问题~

附录:本文所有代码都可以在GitHub仓库下载,直接复制到Jupyter Notebook就能运行!