Skip to content

t 检验

独立样本 t 检验(亦支持单样本与配对样本)。

核心代码

py
    def _fit_ttest_independent(self, df: pd.DataFrame, decimals: int, title: str) -> None:
        """
        执行独立样本T检验

        参数:
            df: 数据框
            decimals: 小数位数
            title: 表格标题
        """
        # 获取分组变量和检验参数
        group_var = self.ttest_options.get('group_var')
        alpha = self.ttest_options.get('alpha', 0.05)
        levene_test = self.ttest_options.get('levene_test', False)

        if not group_var or group_var not in df.columns:
            raise ValueError(_('未指定有效的分组变量。'))

        # 检查变量
        if not self.x_vars:
            raise ValueError(_('未选择任何检验变量。'))

        # 获取分组
        groups = df[group_var].dropna().unique()

        if len(groups) != 2:
            raise ValueError(_('分组变量必须恰好有2个类别,当前有%(count)s个类别。') % {'count': len(groups)})

        group1_name, group2_name = groups[0], groups[1]

        # 构建结果表格
        results = []
        levene_results = []

        for var in self.x_vars:
            if var not in df.columns:
                continue

            # 获取两组数据
            df_clean = df[[var, group_var]].dropna()
            group1_data = df_clean[df_clean[group_var] == group1_name][var]
            group2_data = df_clean[df_clean[group_var] == group2_name][var]

            if len(group1_data) < 2 or len(group2_data) < 2:
                continue

            # 执行Levene方差齐性检验(如果勾选)
            equal_var = True  # 默认假设方差相等
            levene_p = None
            if levene_test:
                from scipy.stats import levene
                levene_stat, levene_p = levene(group1_data, group2_data)
                # 如果Levene检验显著(p<0.05),则认为方差不相等
                equal_var = levene_p >= 0.05

                levene_results.append({
                    'Variable': var,
                    'Levene Statistic': f"{levene_stat:.{decimals}f}",
                    'P-value': f"{levene_p:.{decimals}f}",
                    'Equal Variance': _('是') if equal_var else _('否')
                })

            # 执行独立样本T检验
            t_stat, p_value = ttest_ind(group1_data, group2_data, equal_var=equal_var)

            # 计算各组统计量
            mean1 = group1_data.mean()
            mean2 = group2_data.mean()
            std1 = group1_data.std()
            std2 = group2_data.std()
            n1 = len(group1_data)
            n2 = len(group2_data)

            # 计算均值差异
            mean_diff = mean1 - mean2

            # 计算标准误
            if equal_var:
                # 合并方差的标准误
                pooled_var = ((n1 - 1) * std1**2 + (n2 - 1) * std2**2) / (n1 + n2 - 2)
                se_diff = np.sqrt(pooled_var * (1/n1 + 1/n2))
                df_t = n1 + n2 - 2
            else:
                # Welch's方法的标准误
                se_diff = np.sqrt((std1**2 / n1) + (std2**2 / n2))
                # Welch-Satterthwaite自由度
                df_t = ((std1**2 / n1) + (std2**2 / n2))**2 / \
                       ((std1**2 / n1)**2 / (n1 - 1) + (std2**2 / n2)**2 / (n2 - 1))

            # 计算置信区间
            from scipy.stats import t as t_dist
            t_critical = t_dist.ppf(1 - alpha/2, df_t)
            ci_lower = mean_diff - t_critical * se_diff
            ci_upper = mean_diff + t_critical * se_diff

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

            results.append({
                'Variable': var,
                f'N ({group1_name})': int(n1),
                f'Mean ({group1_name})': f"{mean1:.{decimals}f}",
                f'Std ({group1_name})': f"{std1:.{decimals}f}",
                f'N ({group2_name})': int(n2),
                f'Mean ({group2_name})': f"{mean2:.{decimals}f}",
                f'Std ({group2_name})': f"{std2:.{decimals}f}",
                'Mean Diff': f"{mean_diff:.{decimals}f}",
                't-statistic': f"{t_stat:.{decimals}f}{stars}",
                'P-value': f"{p_value:.{decimals}f}",
                f'{int((1-alpha)*100)}% CI Lower': f"{ci_lower:.{decimals}f}",
                f'{int((1-alpha)*100)}% CI Upper': f"{ci_upper:.{decimals}f}"
            })

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

        # 生成HTML表格
        results_df = pd.DataFrame(results)
        title = title if title else _('独立样本T检验 (分组变量: %(group_var)s)') % {'group_var': group_var}

        # 构建注释
        test_method = _('Pooled t-test (等方差)') if not levene_test else _('根据Levene检验结果自动选择')
        note = _('H0: mean(%(group1_name)s) = mean(%(group2_name)s); 显著性水平: α=%(alpha)s; %(test_method)s; *** p<0.01, ** p<0.05, * p<0.1') % {'group1_name': group1_name, 'group2_name': group2_name, 'alpha': alpha, 'test_method': test_method}

        # 如果执行了Levene检验,生成两个表格
        if levene_test and levene_results:
            levene_df = pd.DataFrame(levene_results)
            levene_html = self._generate_ttest_html(
                levene_df,
                _('Levene方差齐性检验'),
                _('H0: 两组方差相等; 若p<0.05则拒绝原假设,认为方差不相等'),
                decimals
            )
            ttest_html = self._generate_ttest_html(results_df, title, note, decimals)
            self.custom_html = levene_html + ttest_html
        else:
            self.custom_html = self._generate_ttest_html(results_df, title, note, decimals)

Released under the AGPL-3.0 License.