6.2 残差、异方差与影响点:诊断针对哪一种失效
拟合成功只说明软件返回了结果。残差弧线可能提示遗漏非线性,漏斗形可能提示方差变化,高杠杆点也可能左右拟合线。这些都是结合数据生成过程调查的线索,不能翻译成“把碍眼的行删掉”。
本课目标
- 连接线性、独立、方差和分布假设与具体结论;
- 解释 residual-vs-fitted、Q-Q、leverage 与 Cook's distance;
- 使用 robust/clustered standard errors 的适当边界;
- 理解共线性影响系数稳定而非必然损害预测。
1. 先区分预测条件与推断条件
常见假设:
- 条件均值形式正确,
; - 观测或误差依赖结构被正确处理;
- 条件方差是否恒定;
- 误差分布与有限样本推断方法相容;
- 设计矩阵无完全共线;
- 模型训练与应用分布相符。
异方差不必然让 OLS 系数有偏,但普通标准误可能错误,OLS 也不再保证是最有效的线性无偏估计。遗漏混杂变量导致的
2. Residual vs fitted 检查均值和方差结构
以下代码使用 statsmodels 0.15、pandas、NumPy、seaborn 和 Matplotlib,按顺序运行。合成数据刻意含有队伍共享效应、变化的噪声尺度和两个缺失字段。先明确筛选拟合样本,后续诊断才能指向同一批行。
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt
import seaborn as sns
rng = np.random.default_rng(12)
training = pd.DataFrame({
"team_id": np.repeat(np.arange(20), 4),
"team_size": rng.integers(2, 9, 80),
"resources_used": rng.uniform(10, 50, 80),
"mission_type": pd.Categorical(rng.choice(["survey", "repair"], 80)),
})
team_effect = rng.normal(0, 3, 20)
training["duration_minutes"] = (
30 - 2 * training["team_size"] + .4 * training["resources_used"]
+ team_effect[training["team_id"]]
+ rng.normal(0, 1 + .04 * training["resources_used"], 80)
)
training.loc[2, "resources_used"] = np.nan
training.loc[5, "mission_type"] = np.nan
required = ["duration_minutes", "team_size", "resources_used", "mission_type", "team_id"]
included = training[required].notna().all(axis=1)
fit_rows = training.loc[included].copy()
model = smf.ols(
"duration_minutes ~ team_size + resources_used + C(mission_type)",
data=fit_rows, missing="raise",
).fit()
fitted, residuals = model.fittedvalues, model.resid
fig, ax = plt.subplots()
sns.scatterplot(x=fitted, y=residuals, alpha=0.5, ax=ax)
ax.axhline(0, color="black", linestyle="--")
ax.set_xlabel("Fitted duration (minutes)")
ax.set_ylabel("Residual (minutes)")
plt.show()观察:
- 弧形:漏掉非线性或交互;
- 漏斗:方差随均值变化;
- 分组条带:类别、测量离散化或遗漏群体;
- 大片结构:时间/空间依赖或模型错设。
平滑线有助看系统模式,但 residual 和 fitted 共用估计参数,图形解释仍需结合数据生成过程。
3. Q-Q 图不是“模型是否有效”的总开关
Q-Q 图比较标准化残差分位数与参考分布。尾部偏离可能来自重尾、异常点、混合群体或方差错设。
import statsmodels.api as sm
influence = model.get_influence()
sm.qqplot(influence.resid_studentized_internal, line="45")
plt.show()误差正态性主要用于某些小样本精确 t/F 推断;OLS 点估计并不要求
若目标是预测,验证集误差和区间覆盖比“残差看起来正态”更直接。
4. 异方差的处理取决于目标
- 只修推断:使用 heteroskedasticity-consistent covariance(HC 系列);
- 已知方差结构:加权最小二乘;
- 方差随均值变化:转换目标或使用合适 GLM;
- 预测区间:显式建模条件方差或分位数。
robust_model = model.get_robustcov_results(cov_type="HC3")
print(robust_model.summary())robust SE 不会修复非线性、遗漏变量、错误依赖结构或坏数据。它主要改变协方差估计,不改变原 OLS 拟合值。
HC3 按
5. 聚类与时间相关
同一队伍任务可能共享未观测因素,队内正相关会让独立性假设下的区间过窄。若处理分配或抽样发生在队伍层,应选择尊重该单位的推断方法,例如聚类协方差或适当的层级模型:
clustered = model.get_robustcov_results(
cov_type="cluster", groups=fit_rows["team_id"].to_numpy(),
use_correction=True, df_correction=True, use_t=True,
)
print(pd.DataFrame({
"ordinary_se": model.bse,
"hc3_se": robust_model.bse,
"cluster_se": clustered.bse,
}))聚类数很少时,常规渐近近似可能不可靠,需要小样本校正、randomization inference 或其他专门方法。
分组数组取自 fit_rows,不能直接拿未筛选的 training:长度和顺序都必须与实际设计矩阵一致。样本有 20 个组,不能仅凭这个数量认定覆盖可靠。代码明确选择小样本修正与 t 参考分布,statsmodels 协方差选项说明了具体行为。
时间序列误差还可能自相关;Newey–West/HAC 等方法有带宽与平稳性假设,不能把 cov_type 当一键修复。
6. Leverage、残差和 influence 不同
- leverage:
空间中是否远离其他点; - residual:该点结果与拟合相差多少;
- influence:删除该点会怎样改变拟合。
Cook's distance 综合残差与 leverage,是调查指标,不是自动删除规则。4/n 是经验筛查线,不是“超过即错误”的定理。
diagnostics = pd.DataFrame({
"leverage": influence.hat_matrix_diag,
"studentized_residual": influence.resid_studentized_internal,
"cooks_d": influence.cooks_distance[0],
}, index=fit_rows.index)
print(diagnostics.sort_values("cooks_d", ascending=False).head())满秩设计下,杠杆值是
高影响点的处理:
- 核对采集与解析;
- 判断是否属于目标总体;
- 比较保留/排除敏感性;
- 考虑稳健回归或更合适模型;
- 报告规则和结论变化。
删除真实极端案例会让模型只适用于普通区域,应明确新的适用范围。
7. 共线性扩大系数不确定性
当列接近线性组合时,模型难以分离各自贡献。表现为系数对小数据变化敏感、标准误大、符号可能翻转。
VIF:
其中 VIF > 5 或 >10 只是经验阈值,不能替代目的判断。
共线特征仍可能共同提供好预测;问题主要影响单个系数解释和外推稳定性。删除特征、合成指标、重新设计采样或正则化各有不同语义后果。
8. 缺失模式与选择也要诊断
回归库常按 complete cases 静默删行。先比较进入模型与被删除样本:
纳入规则应覆盖公式中用到的所有变量,包括类别预测变量和变换的定义域限制;额外要求 cluster ID 非空,也会改变分析集合。显式筛选后使用 missing="raise",可防止拟合阶段再次静默删行。
print({"input_rows": len(training), "fit_rows": len(fit_rows), "nobs": model.nobs})
print(training.groupby(included)["mission_type"].value_counts(dropna=False, normalize=True))
assert fit_rows.index.equals(model.model.data.row_labels)若删除与结果或群体相关,complete-case 模型目标总体已经改变。需回到缺失机制和 estimand,而不是只看最终 nobs。
9. 诊断后要做敏感性分析
比较:
- 线性与样条/非线性规格;
- 普通、robust 与 clustered SE;
- 含/不含高影响点;
- 不同缺失处理;
- 预设协变量组合;
- 时间或群体切片。
若结论方向随合理选择剧烈变化,报告这种脆弱性,不应只挑最漂亮的规格。
常见误区
- 残差不正态就不能用 OLS:要看目标和推断方法。
- robust SE 修复模型错设:它不修均值结构和混杂。
- Cook's D 超阈值必须删除:它只提示影响值得调查。
- 共线性意味着预测一定差:它更直接损害系数可分辨性。
练习
- 分别构造非线性、异方差和高杠杆数据,比较残差图。
- 对同一模型比较普通、HC3 与 cluster SE。
- 删除高 Cook's D 点前后比较系数,并写调查结论。
- 检查 complete-case 删除是否改变群体构成。
小结
诊断不是给模型打一个“通过/失败”标签,而是定位哪项结论受威胁。均值错设、异方差、相关误差、影响点和共线性影响不同,应采取对应修复并报告敏感性。
下一课把预测流程与正则化放进交叉验证,确保缩放、缺失处理和超参数选择都没有偷看最终测试数据。