Skip to content

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

Released under the AGPL-3.0 License.