外观
AR 特征根检验
VAR 模型稳定性的特征根检验。
核心代码
py
def _fit_ar_roots(self, decimals: int, title: str) -> None:
"""
执行 VAR 模型的 AR 特征根稳定性检验(对应 Stata 的 varstable 命令)。
通过构建 VAR 伴随矩阵,计算其特征值,
检查所有特征值的模是否都在单位圆内(模 < 1)。
若全部在单位圆内则模型稳定,否则模型不稳定。
"""
from statsmodels.tsa.api import VAR
target_cols = self.x_vars
if not target_cols or len(target_cols) < 2:
raise ValueError(_('AR 特征根检验至少需要选择 2 个变量。'))
valid_cols = [c for c in target_cols if c in self.data.columns]
if len(valid_cols) < 2:
raise ValueError(_('有效的数值型变量不足 2 个。'))
df_clean = self.data[valid_cols].select_dtypes(include=[np.number]).dropna()
if df_clean.shape[0] < 20:
raise ValueError(_('有效数据行数不足(至少需要 20 行)。'))
# 获取参数
max_lags = int(self.ts_options.get('ar_roots_max_lags', 4))
ic = self.ts_options.get('ar_roots_ic', 'aic')
trend = self.ts_options.get('ar_roots_trend', 'c')
diff_order = int(self.ts_options.get('ar_roots_diff_order', 0))
# 差分处理
if diff_order > 0:
for _step in range(diff_order):
df_clean = df_clean.diff().dropna()
if df_clean.shape[0] < 20:
raise ValueError(_('经过 %(diff_order)s 阶差分后数据行数不足(至少需要 20 行)。') % {'diff_order': diff_order})
# 构建并拟合 VAR 模型
model = VAR(df_clean)
# 选择最优滞后阶数
try:
lag_order_result = model.select_order(maxlags=max_lags)
except Exception as e:
raise ValueError(_('滞后阶数选择失败: %(error)s') % {'error': str(e)})
ic_map = {'aic': 'aic', 'bic': 'bic', 'hqic': 'hqic', 'fpe': 'fpe'}
selected_ic = ic_map.get(ic, 'aic')
optimal_lag = getattr(lag_order_result, selected_ic, None)
if optimal_lag is None or optimal_lag == 0:
optimal_lag = 1
try:
var_result = model.fit(maxlags=optimal_lag, trend=trend)
except Exception as e:
raise ValueError(_('VAR 模型拟合失败: %(error)s') % {'error': str(e)})
# 构建伴随矩阵并计算特征值(与 Stata varstable 一致)
k = var_result.neqs # 内生变量数
p = var_result.k_ar # 滞后阶数
coefs = var_result.coefs # shape: (p, k, k)
companion = np.zeros((k * p, k * p))
companion[:k, :] = np.hstack(coefs) # 第一行放 [A1, A2, ..., Ap]
if p > 1:
companion[k:, :k * (p - 1)] = np.eye(k * (p - 1))
eigenvalues = np.linalg.eigvals(companion)
# 构建 HTML 输出
diff_note = _('(%(diff_order)s阶差分)') % {'diff_order': diff_order} if diff_order > 0 else ""
overall_title = title if title else _('VAR 模型稳定性检验(特征值)%(diff_note)s') % {'diff_note': diff_note}
html = f'<div class="table-editable-container">'
html += render_table_title_editor(overall_title)
# 模型信息摘要
n_vars = len(valid_cols)
display_names = [f'D{diff_order}.{c}' for c in valid_cols] if diff_order > 0 else valid_cols
html += '<table class="academic-table" style="margin-bottom: 16px;">'
html += '<tbody>'
html += '<tr><td style="text-align:center;font-weight:bold;">' + _('内生变量') + '</td><td style="text-align:center;">' + ", ".join(display_names) + '</td></tr>'
html += '<tr><td style="text-align:center;font-weight:bold;">' + _('VAR 滞后阶数') + '</td><td style="text-align:center;">' + f'{optimal_lag}({selected_ic.upper()} 准则选择,最大搜索 {max_lags})</td></tr>'
html += _('<tr><td style="text-align:center;font-weight:bold;">样本量</td><td style="text-align:center;">%(var_result_nobs)s</td></tr>') % {'var_result_nobs': var_result.nobs}
html += '<tr><td style="text-align:center;font-weight:bold;">' + _('特征值数量') + '</td><td style="text-align:center;">' + f'{len(eigenvalues)}(= 变量数 {n_vars} × 滞后阶数 {optimal_lag})</td></tr>'
html += '</tbody></table>'
# 特征值表
html += self._build_ar_roots_table(eigenvalues, decimals)
# 稳定性结论
all_inside = all(np.abs(ev) < 1.0 for ev in eigenvalues)
max_modulus = max(np.abs(ev) for ev in eigenvalues)
if all_inside:
html += '<div class="alert alert-success py-2 small mt-2">'
html += '<i class="fa-solid fa-circle-check"></i> '
html += _('<strong>结论:VAR 模型满足稳定性条件。</strong>')
html += _('所有特征值的模均小于 1(最大模 = %(modulus)s),') % {'modulus': format(max_modulus, f'.{decimals}f')}
html += _('全部位于单位圆内,模型是稳定的。')
html += '</div>'
else:
n_outside = sum(1 for ev in eigenvalues if np.abs(ev) >= 1.0)
html += '<div class="alert alert-danger py-2 small mt-2">'
html += '<i class="fa-solid fa-triangle-exclamation"></i> '
html += _('<strong>警告:VAR 模型不满足稳定性条件!</strong>')
html += _('有 %(n_outside)s 个特征值的模 ≥ 1(最大模 = %(modulus)s),') % {'n_outside': n_outside, 'modulus': format(max_modulus, f'.{decimals}f')}
html += _('位于单位圆外或边界上,模型不稳定。建议重新调整滞后阶数或对数据进行差分处理。')
html += '</div>'
# 图像放在最后,按需生成
if self.ts_options.get('ar_roots_generate_chart', False):
html += self._build_ar_roots_chart(eigenvalues, decimals)
html += '</div>'
self.custom_html = html