Skip to content

分组描述统计

按分组变量分别计算各组的描述统计量。

核心代码

py
    def _fit_grouped_descriptive(self, df: pd.DataFrame, decimals: int, title: str) -> None:
        """
        执行分组描述性统计分析
        
        参数:
            df: 数据框
            decimals: 小数位数
            title: 表格标题
        """
        if not self.group_var:
            raise ValueError(_('未指定分组变量。'))
        
        if self.group_var not in df.columns:
            raise ValueError(_('分组变量 %(var)s 不存在于数据中。') % {'var': self.group_var})
        
        # 检查分析变量
        target_cols = self.x_vars
        if not target_cols:
            raise ValueError(_('未选择任何分析变量。'))
        
        # 筛选数值列
        valid_cols = [c for c in target_cols if c in df.columns]
        numeric_cols = [c for c in valid_cols if df[c].dtype in [np.float64, np.float32, np.int64, np.int32]]
        
        if not numeric_cols:
            raise ValueError(_('没有可用的数值型变量进行描述性统计。'))
        
        # 只删除分组变量的缺失值,保留分析变量的缺失值
        df_clean = df[[self.group_var] + numeric_cols].dropna(subset=[self.group_var])
        
        if df_clean.empty:
            raise ValueError(_('清洗后的数据为空。'))
        
        # 按分组变量分组计算描述性统计
        grouped = df_clean.groupby(self.group_var)
        
        # 为每个分组构建独立的结果表格
        group_tables = []
        
        for group_name, group_data in grouped:
            group_results = []
            
            for var in numeric_cols:
                # 对每个变量单独处理缺失值
                var_data = group_data[var].dropna()
                
                row_dict = {'Variable': var}
                
                # 如果该变量在该分组中全是缺失值,只显示N=0,其他统计量不显示
                if len(var_data) == 0:
                    if 'nobs' in self.desc_options or 'count' in self.desc_options:
                        row_dict['N'] = 0
                    # 其他统计量设置为空字符串(不显示)
                    if 'mean' in self.desc_options:
                        row_dict['Mean'] = ''
                    if 'std' in self.desc_options:
                        row_dict['Std.Dev'] = ''
                    if 'min' in self.desc_options:
                        row_dict['Min'] = ''
                    if 'max' in self.desc_options:
                        row_dict['Max'] = ''
                    if 'p50' in self.desc_options:
                        row_dict['Median'] = ''
                else:
                    # 有有效数据,正常计算统计量
                    if 'nobs' in self.desc_options or 'count' in self.desc_options:
                        row_dict['N'] = len(var_data)
                    if 'mean' in self.desc_options:
                        row_dict['Mean'] = var_data.mean()
                    if 'std' in self.desc_options:
                        row_dict['Std.Dev'] = var_data.std()
                    if 'min' in self.desc_options:
                        row_dict['Min'] = var_data.min()
                    if 'max' in self.desc_options:
                        row_dict['Max'] = var_data.max()
                    if 'p50' in self.desc_options:
                        row_dict['Median'] = var_data.median()
                
                group_results.append(row_dict)
            
            # 如果该分组有有效数据,添加到结果中
            if group_results:
                group_df = pd.DataFrame(group_results)
                group_tables.append({
                    'group_name': str(group_name),
                    'data': group_df
                })
        
        # 生成HTML(竖式布局,每个分组一个表格)
        title = title if title else f"Grouped Descriptive Statistics by {self.group_var}"
        self.custom_html = self._generate_grouped_descriptive_html_vertical(
            group_tables, title, decimals, self.group_var
        )

Released under the AGPL-3.0 License.