模型诊断 (ModelDiagnostics)

模型诊断类提供了8种方法来检验回归模型的基本假设和诊断模型质量。这些检验在线性回归、时间序列模型和其他统计模型的验证中至关重要。

class ModelDiagnostics

模型诊断类,包含方差齐性、正态性、多重共线性等模型假设检验方法。所有方法都返回 TestResult 对象。

主要方法:

检验方法详解

Breusch-Pagan检验

breusch_pagan_test(residuals: np.ndarray | List, exog: np.ndarray | List[List], alpha: float = 0.05) TestResult

Breusch-Pagan检验,检验回归模型的异方差性。

参数:

  • residuals: 回归残差

  • exog: 解释变量矩阵(设计矩阵)

  • alpha: 显著性水平

适用条件:

  • 线性回归模型

  • 残差应为模型拟合后的残差

  • 样本量 ≥ 变量数 + 10

原假设: 误差项具有同方差性

应用场景:

  • 线性回归模型诊断

  • 确定是否需要加权最小二乘法

  • 模型改进和变换指导

示例:

>>> from pywayne.statistics import ModelDiagnostics
>>> import numpy as np
>>> from sklearn.linear_model import LinearRegression
>>>
>>> md = ModelDiagnostics()
>>> # 生成异方差数据
>>> X = np.random.normal(0, 1, (100, 2))
>>> y = X[:, 0] + 2 * X[:, 1] + np.random.normal(0, 1 + X[:, 0]**2, 100)
>>> model = LinearRegression().fit(X, y)
>>> residuals = y - model.predict(X)
>>> result = md.breusch_pagan_test(residuals, X)
>>> print(f"存在异方差: {result.reject_null}")

White检验

white_test(residuals: np.ndarray | List, exog: np.ndarray | List[List], alpha: float = 0.05) TestResult

White检验,检验异方差性的稳健检验方法。

参数:

  • residuals: 回归残差

  • exog: 解释变量矩阵

  • alpha: 显著性水平

适用条件:

  • 线性回归模型

  • 对函数形式误设更稳健

  • 适用于各种异方差形式

原假设: 误差项具有同方差性

应用场景:

  • 异方差的稳健检验

  • 函数形式不确定时的诊断

  • Breusch-Pagan检验的补充

示例:

>>> # 使用相同的异方差数据
>>> result = md.white_test(residuals, X)
>>> print(f"White检验检测到异方差: {result.reject_null}")

Goldfeld-Quandt检验

goldfeld_quandt_test(y: np.ndarray | List, x: np.ndarray | List[List], split: float = 0.5, alpha: float = 0.05) TestResult

Goldfeld-Quandt检验,通过分段回归检验异方差性。

参数:

  • y: 因变量

  • x: 自变量矩阵

  • split: 分割点比例,默认0.5

  • alpha: 显著性水平

适用条件:

  • 线性回归模型

  • 异方差性与某个变量单调相关

  • 样本量足够进行分段

原假设: 两段数据具有相同方差

应用场景:

  • 特定变量相关的异方差检验

  • 结构性异方差分析

  • 异方差模式识别

Durbin-Watson检验

durbin_watson_test(residuals: np.ndarray | List, alpha: float = 0.05) TestResult

Durbin-Watson检验,检验回归残差的一阶自相关。

参数:

  • residuals: 回归残差(按时间顺序排列)

  • alpha: 显著性水平

适用条件:

  • 时间序列回归模型

  • 残差按时间顺序排列

  • 主要检验一阶自相关

原假设: 残差无一阶自相关

应用场景:

  • 时间序列回归诊断

  • 检验模型的动态设定

  • 确定是否需要自回归项

示例:

>>> # 生成有自相关的残差
>>> residuals = np.zeros(100)
>>> for i in range(1, 100):
...     residuals[i] = 0.7 * residuals[i-1] + np.random.normal(0, 1)
>>> result = md.durbin_watson_test(residuals)
>>> print(f"DW统计量: {result.statistic:.4f}, 存在自相关: {result.reject_null}")

方差膨胀因子 (VIF)

variance_inflation_factor(X: np.ndarray | List, feature_names: List[str] = None) List[float] | Dict[str, float]

计算方差膨胀因子,检验多重共线性问题。

参数:

  • X: 解释变量矩阵(不包含常数项)

  • feature_names: 特征名称列表

适用条件:

  • 多元线性回归

  • 变量数 ≥ 2

  • 样本量 > 变量数

原假设: 不存在严重多重共线性

应用场景:

  • 多重共线性诊断

  • 变量选择指导

  • 模型简化建议

VIF解释标准:

  • VIF < 5: 无明显共线性问题

  • 5 ≤ VIF < 10: 中等共线性

  • VIF ≥ 10: 严重共线性

示例:

>>> # 生成有共线性的数据
>>> X1 = np.random.normal(0, 1, 100)
>>> X2 = X1 + np.random.normal(0, 0.1, 100)  # 高度相关
>>> X3 = np.random.normal(0, 1, 100)         # 独立
>>> X = np.column_stack([X1, X2, X3])
>>> result = md.variance_inflation_factor(X)
>>> print(f"VIF检验结果: {result}")

Levene检验

levene_test(*groups, center: str = 'median', alpha: float = 0.05) TestResult

Levene检验,检验多组数据的方差齐性。

参数:

  • groups: 多个样本组

  • center: 中心化方法(’median’, ‘mean’, ‘trimmed’)

  • alpha: 显著性水平

适用条件:

  • 两组或多组独立样本

  • 对正态性假设稳健

  • 适用于各种分布

原假设: 各组方差相等

应用场景:

  • 方差分析前的假设检验

  • t检验的方差齐性检验

  • 分组数据的方差比较

示例:

>>> # 生成不等方差的组
>>> group1 = np.random.normal(0, 1, 50)     # 标准差=1
>>> group2 = np.random.normal(0, 2, 50)     # 标准差=2
>>> group3 = np.random.normal(0, 1.5, 50)   # 标准差=1.5
>>> result = md.levene_test(group1, group2, group3)
>>> print(f"方差齐性: {not result.reject_null}")

Bartlett检验

bartlett_test(*groups: np.ndarray | List, alpha: float = 0.05) TestResult

Bartlett检验,检验多组数据的方差齐性(假设正态分布)。

参数:

  • groups: 多个样本组

  • alpha: 显著性水平

适用条件:

  • 各组数据近似正态分布

  • 对非正态性敏感

  • 两组或多组独立样本

原假设: 各组方差相等

应用场景:

  • 正态数据的方差齐性检验

  • 传统方差分析前的检验

  • 与Levene检验对比使用

残差正态性检验

residual_normality_test(residuals: np.ndarray | List, alpha: float = 0.05) TestResult

综合检验回归残差的正态性。

参数:

  • residuals: 回归残差

  • alpha: 显著性水平

适用条件:

  • 回归模型残差

  • 样本量 ≥ 20

  • 用于模型假设验证

原假设: 残差服从正态分布

应用场景:

  • 回归模型诊断

  • 推断统计的有效性检验

  • 模型改进指导

示例:

>>> # 生成非正态残差
>>> residuals = np.random.exponential(1, 100) - 1  # 偏斜分布
>>> result = md.residual_normality_test(residuals)
>>> print(f"残差正态性: {not result.reject_null}")

使用建议

模型诊断流程

  1. 基本诊断顺序:

    • Step 1: 残差正态性检验

    • Step 2: 异方差性检验(Breusch-Pagan, White)

    • Step 3: 自相关检验(Durbin-Watson)

    • Step 4: 多重共线性检验(VIF)

  2. 针对性诊断:

    • 时间序列数据: 重点关注自相关

    • 截面数据: 重点关注异方差和共线性

    • 面板数据: 需要综合考虑各种问题

异方差处理策略

  1. 检验选择:

    • Breusch-Pagan: 经典检验,适用性广

    • White: 对函数形式稳健

    • Goldfeld-Quandt: 特定变量相关的异方差

  2. 后续处理:

    • 加权最小二乘法(WLS)

    • 稳健标准误

    • 数据变换(对数、平方根等)

多重共线性处理

  1. VIF判断标准:

    • VIF < 5: 可接受

    • 5 ≤ VIF < 10: 需要注意

    • VIF ≥ 10: 需要处理

  2. 处理方法:

    • 删除高度相关变量

    • 主成分分析

    • 岭回归等正则化方法

自相关问题处理

  1. Durbin-Watson统计量解释:

    • DW ≈ 2: 无自相关

    • DW < 1.5 或 DW > 2.5: 可能存在自相关

    • 需要结合临界值表判断

  2. 处理方法:

    • 添加滞后项

    • 自回归误差模型

    • 差分变换

典型应用示例

线性回归模型完整诊断

from pywayne.statistics import ModelDiagnostics, NormalityTests
import numpy as np
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt

# 生成回归数据(包含各种问题)
np.random.seed(42)
n = 200

# 自变量
X1 = np.random.normal(0, 1, n)
X2 = 0.8 * X1 + np.random.normal(0, 0.5, n)  # 与X1相关(共线性)
X3 = np.random.normal(0, 1, n)
X = np.column_stack([X1, X2, X3])

# 因变量(引入异方差和非正态误差)
heterosced_error = np.random.normal(0, 1 + np.abs(X1), n)  # 异方差误差
y = 2 + 1.5*X1 + 0.8*X2 + 1.2*X3 + heterosced_error

# 拟合模型
model = LinearRegression()
model.fit(X, y)
y_pred = model.predict(X)
residuals = y - y_pred

md = ModelDiagnostics()
nt = NormalityTests()

print("回归模型诊断报告")
print("=" * 50)

# 1. 残差正态性检验
print("\n1. 残差正态性检验:")
normality_result = md.residual_normality_test(residuals)
print(f"   正态性检验: p值={normality_result.p_value:.4f}, "
      f"正态={not normality_result.reject_null}")

# 2. 异方差性检验
print("\n2. 异方差性检验:")
X_with_const = np.column_stack([np.ones(n), X])  # 添加常数项

bp_result = md.breusch_pagan_test(residuals, X_with_const)
print(f"   Breusch-Pagan: p值={bp_result.p_value:.4f}, "
      f"同方差={not bp_result.reject_null}")

white_result = md.white_test(residuals, X_with_const)
print(f"   White检验: p值={white_result.p_value:.4f}, "
      f"同方差={not white_result.reject_null}")

# 3. 多重共线性检验
print("\n3. 多重共线性检验:")
vif_result = md.variance_inflation_factor(X)
print(f"   VIF检验: {vif_result}")

# 4. 诊断结论
print("\n4. 诊断结论:")
issues = []
if normality_result.reject_null:
    issues.append("残差非正态")
if bp_result.reject_null or white_result.reject_null:
    issues.append("存在异方差")
if "高VIF" in vif_result:  # 简化判断
    issues.append("多重共线性")

if issues:
    print(f"   发现问题: {', '.join(issues)}")
    print("   建议: 考虑模型变换、稳健标准误或变量选择")
else:
    print("   模型诊断良好,基本假设满足")

时间序列回归诊断

# 模拟时间序列回归数据
np.random.seed(42)
n = 150

# 时间趋势和周期成分
t = np.arange(n)
trend = 0.05 * t
seasonal = 2 * np.sin(2 * np.pi * t / 12)  # 年度周期

# 自变量
X = np.random.normal(0, 1, n)

# 因变量(包含自相关误差)
y = 10 + trend + seasonal + 1.5 * X

# 添加自相关误差
ar_errors = np.zeros(n)
for i in range(1, n):
    ar_errors[i] = 0.6 * ar_errors[i-1] + np.random.normal(0, 1)

y += ar_errors

# 简单线性回归(忽略时间结构)
model = LinearRegression()
X_reshaped = X.reshape(-1, 1)
model.fit(X_reshaped, y)
y_pred = model.predict(X_reshaped)
residuals = y - y_pred

md = ModelDiagnostics()

print("时间序列回归诊断:")
print("=" * 40)

# Durbin-Watson检验
dw_result = md.durbin_watson_test(residuals)
print(f"Durbin-Watson统计量: {dw_result.statistic:.4f}")

# 解释DW统计量
dw_stat = dw_result.statistic
if dw_stat < 1.5:
    autocorr_conclusion = "存在正自相关"
elif dw_stat > 2.5:
    autocorr_conclusion = "存在负自相关"
else:
    autocorr_conclusion = "无显著自相关"

print(f"自相关诊断: {autocorr_conclusion}")

# 残差正态性
norm_result = md.residual_normality_test(residuals)
print(f"残差正态性: p值={norm_result.p_value:.4f}, "
      f"正态={not norm_result.reject_null}")

# 建议
if dw_stat < 1.5 or dw_stat > 2.5:
    print("\n建议:")
    print("- 考虑添加滞后因变量")
    print("- 使用自回归误差模型")
    print("- 采用稳健标准误")

方差齐性检验应用

# 生成不同方差的组数据
np.random.seed(42)

# 三组数据,方差递增
group1 = np.random.normal(50, 5, 40)    # 均值=50, 标准差=5
group2 = np.random.normal(52, 8, 40)    # 均值=52, 标准差=8
group3 = np.random.normal(48, 12, 40)   # 均值=48, 标准差=12

md = ModelDiagnostics()

print("方差齐性检验比较:")
print("=" * 40)

# Levene检验(稳健)
levene_result = md.levene_test(group1, group2, group3)
print(f"Levene检验: 统计量={levene_result.statistic:.4f}, "
      f"p值={levene_result.p_value:.4f}")
print(f"方差齐性: {not levene_result.reject_null}")

# Bartlett检验(假设正态)
bartlett_result = md.bartlett_test(group1, group2, group3)
print(f"Bartlett检验: 统计量={bartlett_result.statistic:.4f}, "
      f"p值={bartlett_result.p_value:.4f}")
print(f"方差齐性: {not bartlett_result.reject_null}")

# 描述性统计
print("\n各组描述性统计:")
groups = [group1, group2, group3]
for i, group in enumerate(groups, 1):
    print(f"组{i}: 均值={np.mean(group):.2f}, 标准差={np.std(group):.2f}")

# 检验比较
print("\n检验方法比较:")
if levene_result.p_value != bartlett_result.p_value:
    print("- Levene检验和Bartlett检验结果可能不同")
    if levene_result.p_value > bartlett_result.p_value:
        print("- Levene检验更稳健,建议采用其结果")
    else:
        print("- 两种检验结果一致性较好")

多重共线性诊断

# 生成具有不同共线性程度的数据
np.random.seed(42)
n = 100

# 独立变量
X1 = np.random.normal(0, 1, n)

# 不同程度的相关变量
X2 = X1 + np.random.normal(0, 0.1, n)        # 高度相关
X3 = 0.5 * X1 + np.random.normal(0, 1, n)    # 中度相关
X4 = np.random.normal(0, 1, n)               # 独立

# 构建设计矩阵
X_all = np.column_stack([X1, X2, X3, X4])
X_subset = np.column_stack([X1, X3, X4])  # 移除高共线性变量

md = ModelDiagnostics()

print("多重共线性诊断:")
print("=" * 40)

# 全变量VIF
print("1. 包含所有变量:")
vif_all = md.variance_inflation_factor(X_all)
print(f"   VIF结果: {vif_all}")

# 移除高共线性变量后
print("\n2. 移除高共线性变量后:")
vif_subset = md.variance_inflation_factor(X_subset)
print(f"   VIF结果: {vif_subset}")

# 相关性矩阵分析
print("\n3. 变量相关性分析:")
correlation_matrix = np.corrcoef(X_all.T)
variable_names = ['X1', 'X2', 'X3', 'X4']

print("   相关系数矩阵:")
for i, name_i in enumerate(variable_names):
    for j, name_j in enumerate(variable_names):
        if i < j:
            corr = correlation_matrix[i, j]
            print(f"   {name_i}-{name_j}: {corr:.3f}")

print("\n4. 建议:")
if "高VIF" in vif_all:
    print("   - 检测到严重多重共线性")
    print("   - 建议移除高度相关的变量")
    print("   - 或考虑主成分分析、岭回归等方法")
else:
    print("   - 多重共线性问题不严重")
    print("   - 可以保留当前变量设定")

注意事项

  1. 检验序列: - 按照逻辑顺序进行诊断 - 某些问题可能相互影响 - 综合考虑多个检验结果

  2. 样本量要求: - 确保足够的样本量 - 小样本时结果可能不稳定 - 考虑检验的功效

  3. 实际意义: - 统计显著性不等于实际重要性 - 结合专业知识解释结果 - 考虑模型的预测性能

  4. 修正方法: - 数据变换(对数、平方根等) - 稳健估计方法 - 模型重新设定