Skip to content

卡方检验

列联表卡方独立性检验。

核心代码

py
    def _fit_chisq_independence(self, df: pd.DataFrame, decimals: int, title: str) -> None:
        """
        执行卡方独立性检验

        参数:
            df: 数据框
            decimals: 小数位数
            title: 表格标题
        """
        # 获取检验参数
        alpha = self.chisq_options.get('alpha', 0.05)
        show_expected = self.chisq_options.get('show_expected', False)

        # 检查变量(独立性检验需要恰好两个变量)
        if not self.x_vars or len(self.x_vars) < 2:
            raise ValueError(_('卡方独立性检验需要至少选择两个分类变量。'))

        # 如果选择了多个变量,进行两两独立性检验
        results = []
        contingency_tables = []

        # 生成所有可能的配对组合
        from itertools import combinations
        pairs = list(combinations(self.x_vars, 2))

        for var1, var2 in pairs:
            if var1 not in df.columns or var2 not in df.columns:
                continue

            # 获取数据并删除缺失值
            df_clean = df[[var1, var2]].dropna()

            if len(df_clean) < 2:
                continue

            # 构建列联表
            contingency_table = pd.crosstab(df_clean[var1], df_clean[var2])

            # 执行卡方检验
            chi2_stat, p_value, dof, expected_freq = chi2_contingency(contingency_table)

            # 计算Cramér's V(效应量)
            n = contingency_table.sum().sum()
            min_dim = min(contingency_table.shape[0] - 1, contingency_table.shape[1] - 1)
            cramers_v = np.sqrt(chi2_stat / (n * min_dim)) if min_dim > 0 else 0

            # 判断显著性
            if p_value < 0.01:
                stars = "***"
            elif p_value < 0.05:
                stars = "**"
            elif p_value < 0.1:
                stars = "*"
            else:
                stars = ""

            results.append({
                'Variable 1': var1,
                'Variable 2': var2,
                'N': int(n),
                'Chi-square': f"{chi2_stat:.{decimals}f}{stars}",
                'df': int(dof),
                'P-value': f"{p_value:.{decimals}f}",
                "Cramér's V": f"{cramers_v:.{decimals}f}"
            })

            # 如果需要显示期望频数,保存列联表和期望频数
            if show_expected:
                contingency_tables.append({
                    'var1': var1,
                    'var2': var2,
                    'observed': contingency_table,
                    'expected': pd.DataFrame(
                        expected_freq,
                        index=contingency_table.index,
                        columns=contingency_table.columns
                    )
                })

        if not results:
            raise ValueError(_('没有可用的数据进行卡方检验。'))

        # 生成HTML表格
        results_df = pd.DataFrame(results)
        title = title if title else _('卡方独立性检验')
        note = _("H0: 两变量相互独立; 显著性水平: α=%(alpha)s; Cramér's V: 效应量 (0-1); *** p<0.01, ** p<0.05, * p<0.1") % {'alpha': alpha}

        # 生成主结果表格
        main_html = self._generate_chisq_html(results_df, title, note, decimals)

        # 如果需要显示期望频数,生成列联表和期望频数表格
        if show_expected and contingency_tables:
            contingency_html = ""
            for ct_info in contingency_tables:
                var1 = ct_info['var1']
                var2 = ct_info['var2']
                observed = ct_info['observed']
                expected = ct_info['expected']

                # 生成观测频数表格
                obs_title = _('列联表: %(var1)s × %(var2)s (观测频数)') % {'var1': var1, 'var2': var2}
                obs_html = self._generate_contingency_table_html(observed, obs_title, decimals)

                # 生成期望频数表格
                exp_title = _('列联表: %(var1)s × %(var2)s (期望频数)') % {'var1': var1, 'var2': var2}
                exp_html = self._generate_contingency_table_html(expected, exp_title, decimals)

                contingency_html += obs_html + exp_html

            self.custom_html = main_html + contingency_html
        else:
            self.custom_html = main_html

Released under the AGPL-3.0 License.