11.4 数据的相关性分析与数据回归(入门)¶

为什么学?
在人文社科研究里,你会经常问:学习时间和成绩有关系吗?收入与幸福感如何相关?
相关性分析回答“变量是否一起变化”,回归分析进一步建立“如何变化”的预测关系。

学习目标¶

  • 认识皮尔逊相关系数 r ∈ [-1,1] 的含义与解释方式。
  • 会用 pandas 计算相关矩阵,并能画散点图 + 回归直线做直观判断。
  • 掌握 LinearRegression(若不可用则用 numpy.linalg.lstsq 兜底)完成一元/多元回归。
  • 能写出自然语言解释,并避免“相关 ≠ 因果”的常见误解。

11.4.1 语法要点(抓住这些就够了)¶

A. 相关性(Correlation)¶

  • 皮尔逊相关 r:线性相关强度,r>0 正相关,r<0 负相关,|r| 越大相关越强。
  • 计算:df.corr()(默认皮尔逊);也可用 df.corr(method='spearman') 做等级相关(更稳健)。
  • 相关矩阵:多变量两两之间的相关系数表。

B. 简单线性回归(Linear Regression)¶

  • 模型:y = a x + b + ε(a 斜率,b 截距)。
  • 用法:
    from sklearn.linear_model import LinearRegression
    model = LinearRegression().fit(X, y)  # X 为二维数组
    y_pred = model.predict(X_new)
    
  • 若环境无 sklearn,可用 numpy.linalg.lstsq 最小二乘求解(本讲已内置兜底)。

C. 读数技巧¶

  • 斜率 a:x 每增加 1 单位,y 平均变化多少。
  • 截距 b:当 x=0 的基线水平(需注意是否有解释意义)。
  • R²:拟合优度(0~1),越接近 1 表示越能解释 y 的变异。

🔎 提示:散点图是理解“相关/回归”的第一视角;先看图,再下结论。

11.4.2 示范:相关性分析(学习时间 vs 期末成绩)¶

In [1]:
import pandas as pd

data = {
    '学习时间': [2, 3, 4, 5, 6],
    '期末成绩': [70, 75, 78, 85, 90]
}
df = pd.DataFrame(data)

# 计算相关矩阵(默认皮尔逊)
correlation_matrix = df.corr()
correlation_matrix
C:\Users\Zhouq\AppData\Roaming\Python\Python39\site-packages\pandas\core\computation\expressions.py:21: UserWarning: Pandas requires version '2.8.4' or newer of 'numexpr' (version '2.8.3' currently installed).
  from pandas.core.computation.check import NUMEXPR_INSTALLED
C:\Users\Zhouq\AppData\Roaming\Python\Python39\site-packages\pandas\core\arrays\masked.py:60: UserWarning: Pandas requires version '1.3.6' or newer of 'bottleneck' (version '1.3.5' currently installed).
  from pandas.core import (
Out[1]:
学习时间 期末成绩
学习时间 1.000000 0.993661
期末成绩 0.993661 1.000000
In [3]:
# 散点图 + 简易回归直线(使用 matplotlib)
import matplotlib.pyplot as plt
import numpy as np

# 显示中文
# 设置支持中文的字体,例如黑体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 或者 'Microsoft YaHei', 'FangSong', 'KaiTi' 等
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

x = df['学习时间'].to_numpy(dtype=float)
y = df['期末成绩'].to_numpy(dtype=float)

# 用 numpy 最小二乘拟合直线 y = a*x + b
X_design = np.column_stack([x, np.ones_like(x)])
a, b = np.linalg.lstsq(X_design, y, rcond=None)[0]

plt.figure()
plt.scatter(x, y)            # 散点
x_line = np.linspace(x.min(), x.max(), 100)
y_line = a * x_line + b
plt.plot(x_line, y_line)     # 回归线(仅可视化)
plt.xlabel("学习时间")
plt.ylabel("期末成绩")
plt.title("散点图与回归线(可视化相关关系)")
plt.show()

11.4.3 示范:一元线性回归(用学习时间预测成绩)¶

In [4]:
# 优先使用 sklearn;若不可用则自动兜底到 numpy
import numpy as np

X = np.array([2, 3, 4, 5, 6], dtype=float).reshape(-1, 1)
y = np.array([70, 75, 78, 85, 90], dtype=float)

try:
    from sklearn.linear_model import LinearRegression
    model = LinearRegression()
    model.fit(X, y)
    slope = model.coef_[0]
    intercept = model.intercept_
    r2 = model.score(X, y)
    print("回归系数(斜率):", round(slope, 4))
    print("截距:", round(intercept, 4))
    print("R²:", round(r2, 4))
except Exception as e:
    # numpy 最小二乘兜底实现
    X_design = np.column_stack([np.ones(len(X)), X.flatten()])  # [1, x]
    beta, *_ = np.linalg.lstsq(X_design, y, rcond=None)
    intercept, slope = float(beta[0]), float(beta[1])
    y_hat = X_design @ beta
    rss = float(((y - y_hat)**2).sum())
    tss = float(((y - y.mean())**2).sum())
    r2 = 1 - rss/tss
    print("(使用 numpy 兜底)")
    print("回归系数(斜率):", round(slope, 4))
    print("截距:", round(intercept, 4))
    print("R²:", round(r2, 4))
回归系数(斜率): 5.0
截距: 59.6
R²: 0.9874
In [5]:
# 使用模型预测(比如学习 8 小时的成绩)
X_new = np.array([[8]], dtype=float)
try:
    predicted = model.predict(X_new)  # 若上方是 sklearn
except NameError:
    # 若没有 sklearn 的 model,则使用兜底系数计算
    predicted = slope * X_new.flatten() + intercept
print("预测学习 8 小时的成绩:", round(float(predicted[0]), 2))
预测学习 8 小时的成绩: 99.6

11.4.4 举例(贴近人文社科的数据情境)¶

例 1:多个自变量的多元线性回归¶

假设我们用 学习时间 与 练习题数量 共同预测 期末成绩。

In [6]:
import numpy as np
import pandas as pd

np.random.seed(0)
n = 12
learn_hours = np.linspace(1, 6, n)
practice = np.linspace(10, 60, n) + np.random.normal(0, 5, n)
score = 55 + 4.0*learn_hours + 0.05*practice + np.random.normal(0, 2, n)

X_multi = np.column_stack([learn_hours, practice])
y_multi = score

try:
    from sklearn.linear_model import LinearRegression
    m2 = LinearRegression().fit(X_multi, y_multi)
    print("斜率(学习时间, 练习题):", np.round(m2.coef_, 3))
    print("截距:", round(m2.intercept_, 3))
    print("R²:", round(m2.score(X_multi, y_multi), 4))
except Exception as e:
    X_design = np.column_stack([np.ones(len(X_multi)), X_multi])
    beta, *_ = np.linalg.lstsq(X_design, y_multi, rcond=None)
    intercept, b1, b2 = beta
    y_hat = X_design @ beta
    rss = float(((y_multi - y_hat)**2).sum())
    tss = float(((y_multi - y_multi.mean())**2).sum())
    r2 = 1 - rss/tss
    print("(numpy 兜底)")
    print("斜率(学习时间, 练习题):", [round(float(b1),3), round(float(b2),3)])
    print("截距:", round(float(intercept),3))
    print("R²:", round(r2,4))
斜率(学习时间, 练习题): [2.044 0.217]
截距: 55.487
R²: 0.9297

例 2:corr() 方法对比(皮尔逊 vs 斯皮尔曼)¶

  • 当数据偏离线性或包含极端值/等级数据时,Spearman(等级相关)更稳健。
In [7]:
df2 = pd.DataFrame({
    '学习时间': [1,2,3,4,5,6,7,8],
    '满意度_等级': [1,1,2,2,3,3,4,5]  # 等级型数据(有序分类)
})
pearson = df2.corr(method='pearson')
spearman = df2.corr(method='spearman')
pearson, spearman
Out[7]:
(            学习时间    满意度_等级
 学习时间    1.000000  0.973479
 满意度_等级  0.973479  1.000000,
             学习时间    满意度_等级
 学习时间    1.000000  0.981981
 满意度_等级  0.981981  1.000000)

11.4.5 回归模型对比与应用(速查表)¶

回归类型 Python方法 常见应用场景
线性回归 LinearRegression 简单线性关系预测
多元回归 LinearRegression 多因素联合分析
岭回归 Ridge 多重共线性、系数收缩
套索回归 Lasso 稀疏选择/特征选择
逻辑回归 LogisticRegression 二分类问题(通过/未通过)
多项式回归 PolynomialFeatures + LinearRegression 简单非线性趋势
决策树回归 DecisionTreeRegressor 非线性、可解释的分段模型
随机森林回归 RandomForestRegressor 高维、非线性、鲁棒预测
支持向量回归 SVR 小样本、非线性核方法

11.4.6 注意事项(常见坑)¶

  1. 相关 ≠ 因果:相关可能来自共同原因、混杂变量或巧合。
  2. 线性假设:若关系明显弯曲,简单线性模型会失真;可尝试多项式/树模型。
  3. 异常值影响大:单个极端点能显著改变斜率/相关;先画图检查。
  4. 外推风险:超出训练数据范围的预测不可靠。
  5. 多重共线性:多元回归中自变量高度相关会导致系数不稳定,需诊断(如方差膨胀因子 VIF,进阶)。

11.4.7 练习(建议先做再看答案)¶

练习 1:相关性
构造数据:学习时间=[1,2,3,4,5,6],成绩=[60,65,68,74,80,85]。
1) 计算皮尔逊相关系数与相关矩阵;2) 画散点图 + 回归直线。

练习 2:一元回归预测
用练习 1 的数据拟合回归,报告斜率、截距、R²;预测 学习时间=7 时的成绩。

练习 3:多元回归
自拟 练习题数量 列,与 学习时间 一起预测 成绩;比较一元与多元模型的 R²。

练习 4:Spearman 等级相关
构造一列等级型满意度(1~5),计算与学习时间的 Spearman 相关并解释。

In [8]:
# === 在此动手做题(你可以多建几个单元格)===
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 练习 1:相关性与散点图


# 练习 2:一元回归(报告 斜率/截距/R² + 预测)


# 练习 3:多元回归(与一元回归比较 R²)


# 练习 4:Spearman 等级相关

11.4.8 参考答案(对照自检)¶

说明:若无 sklearn,答案会自动切换为 numpy 兜底实现。

In [9]:
# 练习 1 参考答案
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df_ex = pd.DataFrame({
    '学习时间': [1,2,3,4,5,6],
    '成绩': [60,65,68,74,80,85]
})

print("相关矩阵:")
print(df_ex.corr())

x = df_ex['学习时间'].to_numpy(dtype=float)
y = df_ex['成绩'].to_numpy(dtype=float)
X_design = np.column_stack([x, np.ones_like(x)])
a, b = np.linalg.lstsq(X_design, y, rcond=None)[0]

plt.figure()
plt.scatter(x, y)
x_line = np.linspace(x.min(), x.max(), 100)
y_line = a*x_line + b
plt.plot(x_line, y_line)
plt.xlabel("学习时间")
plt.ylabel("成绩")
plt.title("练习1:散点图与回归线")
plt.show()
相关矩阵:
          学习时间        成绩
学习时间  1.000000  0.996085
成绩    0.996085  1.000000
In [10]:
# 练习 2 参考答案:一元回归 + 预测
import numpy as np

X = np.array([1,2,3,4,5,6], dtype=float).reshape(-1,1)
y = np.array([60,65,68,74,80,85], dtype=float)

try:
    from sklearn.linear_model import LinearRegression
    m = LinearRegression().fit(X, y)
    slope, intercept = m.coef_[0], m.intercept_
    r2 = m.score(X, y)
    pred7 = m.predict(np.array([[7]], dtype=float))[0]
except Exception as e:
    Xd = np.column_stack([np.ones(len(X)), X.flatten()])
    beta, *_ = np.linalg.lstsq(Xd, y, rcond=None)
    intercept, slope = float(beta[0]), float(beta[1])
    y_hat = Xd @ beta
    rss = float(((y - y_hat)**2).sum()); tss = float(((y - y.mean())**2).sum())
    r2 = 1 - rss/tss
    pred7 = slope*7 + intercept

print("斜率:", round(slope,4), "截距:", round(intercept,4), "R²:", round(r2,4))
print("预测(学习时间=7):", round(float(pred7), 2))
斜率: 5.0286 截距: 54.4 R²: 0.9922
预测(学习时间=7): 89.6
In [11]:
# 练习 3 参考答案:多元回归 vs 一元回归
import numpy as np
from numpy.random import default_rng

rng = default_rng(0)
learn = np.array([1,2,3,4,5,6], dtype=float)
practice = np.array([10,20,30,40,45,55], dtype=float) + rng.normal(0, 2, 6)
score = 55 + 4.0*learn + 0.05*practice + rng.normal(0, 1.5, 6)

X1 = learn.reshape(-1,1)
X2 = np.column_stack([learn, practice])

def fit_ols(X, y):
    try:
        from sklearn.linear_model import LinearRegression
        m = LinearRegression().fit(X, y)
        r2 = m.score(X, y)
        return r2
    except Exception as e:
        Xd = np.column_stack([np.ones(len(X)), X])
        beta, *_ = np.linalg.lstsq(Xd, y, rcond=None)
        y_hat = Xd @ beta
        rss = float(((y - y_hat)**2).sum()); tss = float(((y - y.mean())**2).sum())
        return 1 - rss/tss

r2_simple = fit_ols(X1, score)
r2_multi = fit_ols(X2, score)

print("一元回归 R²:", round(r2_simple, 4))
print("多元回归 R²:", round(r2_multi, 4))
一元回归 R²: 0.9769
多元回归 R²: 0.9838
In [12]:
# 练习 4 参考答案:Spearman 等级相关
import pandas as pd

df_rank = pd.DataFrame({
    '学习时间': [1,2,3,4,5,6,7,8],
    '满意度': [1,1,2,2,3,3,4,5]  # 有序类别
})
spearman_corr = df_rank.corr(method='spearman')
spearman_corr
Out[12]:
学习时间 满意度
学习时间 1.000000 0.981981
满意度 0.981981 1.000000

11.4 小结¶

  • 相关性衡量“是否一起变动”,回归进一步量化“如何变动并预测”。
  • 先看散点图,再做结论;报告时写清方法、样本量、R²/系数与解释。
  • 别把相关当因果;注意异常值与外推风险。

下一步建议:学习残差诊断、方差膨胀因子(VIF)、正则化回归(Ridge/Lasso),和非线性模型(树/核方法)。

In [ ]: