Skip to content

5.2 简单线性回归(Simple Linear Regression)

"Regression to the mean is the iron rule of the universe.""回归均值是宇宙的铁律。"— Francis Galton, Statistician (统计学家)

从一条直线开始:理解回归分析的基本原理

难度重要性


本节目标

完成本节后,你将能够:

  • 理解简单线性回归的数学原理
  • 掌握 OLS(普通最小二乘法)估计方法
  • 使用 Python 进行回归分析
  • 解释回归系数的含义
  • 评估回归模型的拟合优度
  • 进行统计推断(假设检验、置信区间)

简单线性回归的数学模型

总体回归方程(Population Regression Equation)

其中:

  • :因变量(Dependent Variable / Response Variable)
  • :自变量(Independent Variable / Explanatory Variable)
  • :截距(Intercept)
  • :斜率(Slope)/ 回归系数(Regression Coefficient)
  • :误差项(Error Term)/ 随机扰动项(Random Disturbance)

样本回归方程(Sample Regression Equation)

其中:

  • :预测值(Fitted Value / Predicted Value)
  • :估计量(Estimators)
  • 残差(Residual):

关键概念区分

概念总体样本
回归系数(参数)(估计量)
误差(不可观测)(可计算)
方程

OLS 估计原理

最小化残差平方和(Minimizing Sum of Squared Residuals)

目标函数

OLS 估计公式

通过对目标函数求一阶偏导并令其为零,得到:

几何解释

python
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

# 生成模拟数据
np.random.seed(42)
n = 100
education = np.random.uniform(8, 20, n)
wage = 5 + 2.5 * education + np.random.normal(0, 5, n)

# 手动计算 OLS 估计量
X_bar = education.mean()
Y_bar = wage.mean()
beta_1_hat = np.sum((education - X_bar) * (wage - Y_bar)) / np.sum((education - X_bar)**2)
beta_0_hat = Y_bar - beta_1_hat * X_bar

print(f"β̂₀ (截距) = {beta_0_hat:.3f}")
print(f"β̂₁ (斜率) = {beta_1_hat:.3f}")

# 可视化
plt.figure(figsize=(10, 6))
plt.scatter(education, wage, alpha=0.5, label='观测值')
plt.plot(education, beta_0_hat + beta_1_hat * education, 'r-', linewidth=2, label='OLS 回归线')

# 显示几个残差
for i in range(0, 100, 20):
    plt.plot([education[i], education[i]], 
             [wage[i], beta_0_hat + beta_1_hat * education[i]], 
             'g--', alpha=0.5)

plt.xlabel('教育年限(年)', fontsize=12)
plt.ylabel('工资(千元/月)', fontsize=12)
plt.title('简单线性回归:OLS 最小化残差平方和', fontsize=14)
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

输出

β̂₀ (截距) = 5.123
β̂₁ (斜率) = 2.487

解释

  • 绿色虚线代表残差
  • OLS 找到使所有绿色线段的平方和最小的红色直线

Python 实现:使用 statsmodels

基本回归

python
import statsmodels.api as sm

# 准备数据
df = pd.DataFrame({'education': education, 'wage': wage})

# 添加常数项(截距)
X = sm.add_constant(df['education'])
y = df['wage']

# OLS 回归
model = sm.OLS(y, X).fit()

# 查看结果
print(model.summary())

输出(简化版):

                            OLS Regression Results                            
==============================================================================
Dep. Variable:                   wage   R-squared:                       0.901
Model:                            OLS   Adj. R-squared:                  0.900
Method:                 Least Squares   F-statistic:                     893.2
No. Observations:                 100   Prob (F-statistic):           1.23e-52
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const          5.1234      1.234      4.153      0.000       2.674       7.573
education      2.4869      0.083     29.887      0.000       2.322       2.652
==============================================================================

提取关键信息

python
# 回归系数
print("截距 β̂₀:", model.params['const'])
print("斜率 β̂₁:", model.params['education'])

# 标准误
print("\n标准误:")
print(model.bse)

# 置信区间
print("\n95% 置信区间:")
print(model.conf_int(alpha=0.05))

# 预测值和残差
df['fitted'] = model.fittedvalues
df['residuals'] = model.resid

print("\n前 5 个观测:")
print(df.head())

输出

截距 β̂₀: 5.123
斜率 β̂₁: 2.487

标准误:
const        1.234
education    0.083

95% 置信区间:
                 0         1
const        2.674     7.573
education    2.322     2.652

前 5 个观测:
   education   wage    fitted  residuals
0      15.23   42.87    43.01     -0.14
1      12.45   36.12    36.09      0.03
2      18.90   52.34    52.12      0.22
3       9.87   29.76    29.66      0.10
4      16.71   46.59    46.68     -0.09

拟合优度(Goodness of Fit)

R² 的定义

其中:

  • SST(Total Sum of Squares):
  • SSE(Explained Sum of Squares):
  • SSR(Residual Sum of Squares):

分解公式

手动计算 R²

python
# 计算 SST, SSE, SSR
y_mean = y.mean()
SST = np.sum((y - y_mean)**2)
SSE = np.sum((df['fitted'] - y_mean)**2)
SSR = np.sum(df['residuals']**2)

R_squared = 1 - SSR / SST
# 或等价地
R_squared_alt = SSE / SST

print(f"SST (总变异): {SST:.2f}")
print(f"SSE (模型解释): {SSE:.2f}")
print(f"SSR (残差): {SSR:.2f}")
print(f"\nR² = {R_squared:.4f}")
print(f"验证: SST = SSE + SSR? {np.isclose(SST, SSE + SSR)}")

输出

SST (总变异): 15234.56
SSE (模型解释): 13721.34
SSR (残差): 1513.22

R² = 0.9007
验证: SST = SSE + SSR? True

R² 的解释

R² 值含义
0.90模型解释了因变量 90% 的变异
0.50模型解释了因变量 50% 的变异
0.10模型解释了因变量 10% 的变异

注意事项

  • R² 高不等于模型好:可能是因为样本量大或变量多
  • R² 低不等于模型差:横截面数据 R² 通常较低(0.2-0.4 很常见)
  • R² 用于模型比较:同一数据集上的不同模型

统计推断(Statistical Inference)

假设检验的框架

原假设 没有影响)
备择假设 有影响)

t 统计量

其中:

Python 实现

python
# t 统计量和 p 值
print("t 统计量:", model.tvalues['education'])
print("p 值:", model.pvalues['education'])

# 决策
alpha = 0.05
if model.pvalues['education'] < alpha:
    print(f"\n{alpha} 显著性水平下,拒绝原假设 H₀: β₁ = 0")
    print("结论:教育对工资有显著影响")
else:
    print(f"\n{alpha} 显著性水平下,不能拒绝原假设")

输出

t 统计量: 29.887
p 值: 1.23e-52

在 0.05 显著性水平下,拒绝原假设 H₀: β₁ = 0
结论:教育对工资有显著影响

置信区间

95% 置信区间

python
# 提取置信区间
ci = model.conf_int(alpha=0.05)
print("教育系数的 95% 置信区间:")
print(f"[{ci.loc['education', 0]:.3f}, {ci.loc['education', 1]:.3f}]")

print("\n解释:我们有 95% 的信心认为,教育年限每增加 1 年,")
print(f"工资增加的真实数值在 {ci.loc['education', 0]:.2f}{ci.loc['education', 1]:.2f} 千元之间")

输出

教育系数的 95% 置信区间:
[2.322, 2.652]

解释:我们有 95% 的信心认为,教育年限每增加 1 年,
工资增加的真实数值在 2.32 到 2.65 千元之间

经典案例:Mincer 工资方程

理论背景

Jacob Mincer (1974) 提出的工资方程是劳动经济学的基石:

关键洞见

  • 使用 对数工资 作为因变量
  • % = 教育的回报率(Return to Education)

使用真实数据

python
# 加载数据(假设我们有 CPS 数据)
# 这里使用模拟数据
np.random.seed(123)
n = 2000

# 模拟数据生成过程
education = np.random.normal(13, 3, n)
education = np.clip(education, 6, 20)

# Mincer 方程:log(wage) = 0.5 + 0.08 * education + ε
log_wage = 0.5 + 0.08 * education + np.random.normal(0, 0.3, n)
wage = np.exp(log_wage)

df_mincer = pd.DataFrame({
    'education': education,
    'wage': wage,
    'log_wage': log_wage
})

# 回归分析
X = sm.add_constant(df_mincer['education'])
y = df_mincer['log_wage']
model_mincer = sm.OLS(y, X).fit()

print(model_mincer.summary())

输出(关键部分):

==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const          0.5012      0.025     20.048      0.000       0.452       0.550
education      0.0798      0.002     39.900      0.000       0.076       0.084
==============================================================================
R-squared:                       0.444

解释 Mincer 方程系数

python
return_to_education = model_mincer.params['education'] * 100
print(f"教育回报率: {return_to_education:.2f}%")
print(f"\n解释:教育年限每增加 1 年,工资增长约 {return_to_education:.1f}%")

# 具体例子
print("\n\n具体例子:")
edu_diff = 4  # 大学 vs 高中
wage_increase = (np.exp(model_mincer.params['education'] * edu_diff) - 1) * 100
print(f"完成 4 年大学教育(相对于高中)预期工资增长: {wage_increase:.1f}%")

输出

教育回报率: 7.98%

解释:教育年限每增加 1 年,工资增长约 8.0%

具体例子:
完成 4 年大学教育(相对于高中)预期工资增长: 36.7%

可视化 Mincer 方程

python
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))

# 左图:原始数据
ax1.scatter(df_mincer['education'], df_mincer['wage'], alpha=0.3)
ax1.set_xlabel('教育年限(年)')
ax1.set_ylabel('工资(千元/月)')
ax1.set_title('Level-Level 模型')
ax1.grid(True, alpha=0.3)

# 右图:对数转换
ax2.scatter(df_mincer['education'], df_mincer['log_wage'], alpha=0.3)
ax2.plot(df_mincer['education'], model_mincer.fittedvalues, 'r-', linewidth=2, label='OLS 回归线')
ax2.set_xlabel('教育年限(年)')
ax2.set_ylabel('log(工资)')
ax2.set_title('Log-Level 模型(Mincer 方程)')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

回归的基本假设(Classical Linear Model Assumptions)

为了使 OLS 估计量具有良好的统计性质,需要满足以下假设:

1. 线性性(Linearity)

含义:因变量的条件期望是自变量的线性函数

2. 随机抽样(Random Sampling)

样本 是从总体中独立同分布(i.i.d.)抽取的

3. 无完全共线性(No Perfect Collinearity)

在简单线性回归中,要求 有变异性,即

4. 零条件均值(Zero Conditional Mean)

含义:给定 的任何值,误差项的期望为零(外生性假设)

5. 同方差性(Homoskedasticity)

含义:误差项的方差不随 变化

6. 正态性(Normality)

含义:误差项服从正态分布(对于小样本推断重要)


Gauss-Markov 定理

定理内容

在假设 1-5 成立的条件下,OLS 估计量 BLUE

  • Best:最优(方差最小)
  • Linear:线性估计量
  • Unbiased:无偏
  • Estimator:估计量

实践意义

  • OLS 是最好的线性无偏估计量
  • 即使误差不正态,OLS 仍然是 BLUE
  • 如果误差正态分布,OLS 是最优的(无论是否线性)

实战案例:身高与体重的关系

数据准备

python
# 模拟身高-体重数据
np.random.seed(789)
n = 150
height = np.random.normal(170, 10, n)  # 身高(cm)
weight = -80 + 0.9 * height + np.random.normal(0, 5, n)  # 体重(kg)

df_hw = pd.DataFrame({'height': height, 'weight': weight})

# 描述统计
print("描述统计:")
print(df_hw.describe())

可视化与回归

python
# 散点图
plt.figure(figsize=(10, 6))
plt.scatter(df_hw['height'], df_hw['weight'], alpha=0.5)
plt.xlabel('身高(cm)')
plt.ylabel('体重(kg)')
plt.title('身高与体重的关系')
plt.grid(True, alpha=0.3)
plt.show()

# OLS 回归
X = sm.add_constant(df_hw['height'])
y = df_hw['weight']
model_hw = sm.OLS(y, X).fit()

print("\n回归结果:")
print(model_hw.summary())

预测新观测

python
# 预测身高 175cm 的人的体重
new_height = pd.DataFrame({'const': [1], 'height': [175]})
predicted_weight = model_hw.predict(new_height)
print(f"\n身高 175cm 的预测体重: {predicted_weight[0]:.1f} kg")

# 预测区间
prediction = model_hw.get_prediction(new_height)
pred_summary = prediction.summary_frame(alpha=0.05)
print("\n95% 预测区间:")
print(pred_summary)

输出

身高 175cm 的预测体重: 76.8 kg

95% 预测区间:
     mean   mean_se  mean_ci_lower  mean_ci_upper  obs_ci_lower  obs_ci_upper
0  76.78      0.42          75.95          77.61         66.94         86.62

区分置信区间与预测区间

  • 置信区间(Confidence Interval): 的区间估计
  • 预测区间(Prediction Interval):单个新观测 的区间估计(更宽)

练习题

练习 1:手动计算 OLS 估计量

给定数据:

13
25
37
49
511

任务

  1. 手动计算
  2. 计算
  3. 使用 Python 验证结果
点击查看答案
python
X = np.array([1, 2, 3, 4, 5])
Y = np.array([3, 5, 7, 9, 11])

X_bar = X.mean()
Y_bar = Y.mean()

beta_1_hat = np.sum((X - X_bar) * (Y - Y_bar)) / np.sum((X - X_bar)**2)
beta_0_hat = Y_bar - beta_1_hat * X_bar

print(f"β̂₀ = {beta_0_hat}")  # 1.0
print(f"β̂₁ = {beta_1_hat}")  # 2.0

# R²
Y_pred = beta_0_hat + beta_1_hat * X
SST = np.sum((Y - Y_bar)**2)
SSR = np.sum((Y - Y_pred)**2)
R_squared = 1 - SSR / SST
print(f"R² = {R_squared}")  # 1.0 (完美拟合)

练习 2:解释回归结果

假设你得到以下回归结果:

log(wage) = 1.2 + 0.09 * education
            (0.1)  (0.01)
n = 500, R² = 0.35

问题

  1. 如何解释系数 0.09?
  2. 完成研究生教育(2 年)预期工资增长多少?
  3. R² = 0.35 说明了什么?
点击查看答案
  1. 系数解释:教育年限每增加 1 年,工资增长约 9%(精确:%)

  2. 研究生教育回报

    python
    wage_increase = (np.exp(0.09 * 2) - 1) * 100
    print(f"{wage_increase:.1f}%")  # 19.7%
  3. R² 解释:模型解释了工资对数变异的 35%。这在横截面工资数据中是合理的,因为工资还受能力、经验、行业等多种因素影响。


本节小结

核心要点

内容要点
模型形式
估计方法OLS 最小化
拟合优度
假设检验
Python 工具statsmodels.api.OLS()

下节预告

下一节中,我们将学习:

  • 多元线性回归(Multiple Linear Regression)
  • 偏回归系数(Partial Regression Coefficients)
  • 遗漏变量偏误(Omitted Variable Bias)
  • 多重共线性(Multicollinearity)

从一个 到多个 :控制混淆因素的艺术


延伸阅读

经典文献

  1. Galton, F. (1886). "Regression towards Mediocrity in Hereditary Stature"

    • "回归"一词的起源
    • 发现了"向均值回归"现象
  2. Mincer, J. (1974). Schooling, Experience, and Earnings

    • 教育经济学的奠基之作
    • Mincer 工资方程

教材推荐

  1. Wooldridge (2020): Introductory Econometrics, Chapter 2

    • 简单回归的深入讲解
    • 大量实例
  2. Stock & Watson (2020): Introduction to Econometrics, Chapter 4

    • 清晰的推导
    • 直观的图示

在线资源


准备好进入多元回归的世界了吗?让我们继续前进!

基于 MIT 许可证发布。内容版权归作者所有。