如何对Dataframe分组统计并补全缺失结果为0、添加间隔与总计
实现DataFrame分组统计的三个需求:补全类别、添加总计、分组空行间隔
先明确咱们的原始数据和当前的问题:
原始DataFrame定义如下:
import pandas as pd df = {'Client': ['A', 'A', 'A', 'B', 'B', 'B', 'B','B','B','B','C','D','D','D','D','D','D','D','D','D','D','D' ], 'Result': ['Covered', 'Customer Reject', 'Customer Timeout', 'Dealer Reject','Dealer Timeout','Done','Tied Covered','Tied Done','Tied Traded Away','Traded Away','No RFQ','Covered','Customer Reject','Customer Timeout','Dealer Reject','Dealer Timeout','Done','Tied Covered','Tied Done','Tied Traded Away','Traded Away','No RFQ']} df = pd.DataFrame.from_dict(df)
现在直接用df.groupby(['Client','Result']).agg({'Result': 'size'})只能统计每个Client下实际出现过的Result,没法满足补全缺失类别、加总计、分组空行这三个需求,下面咱们一步步来实现:
步骤1:补全每个Client未出现的Result类别
首先把所有11种Result列出来,生成每个Client和所有Result的笛卡尔积索引,再和原统计结果合并填充0:
# 定义所有需要包含的Result类别 all_results = [ 'Covered', 'Customer Reject', 'Customer Timeout', 'Dealer Reject', 'Dealer Timeout', 'Done', 'Tied Covered', 'Tied Done', 'Tied Traded Away', 'Traded Away', 'No RFQ' ] # 先做基础分组统计 grouped = df.groupby(['Client', 'Result']).agg(size=('Result', 'size')) # 生成每个Client对应的全Result组合的MultiIndex clients = df['Client'].unique() full_index = pd.MultiIndex.from_product( [clients, all_results], names=['Client', 'Result'] ) # 合并并填充缺失值为0 full_grouped = grouped.reindex(full_index, fill_value=0)
步骤2:为每个Client添加总计行
接下来给每个Client分组添加一行总计,统计该Client下所有Result的总和:
# 计算每个Client的总计 client_totals = full_grouped.groupby('Client').sum().rename({'size': 'Total'}, axis=1) # 把总计行转换为和原数据一致的MultiIndex格式 total_rows = client_totals.reset_index().assign(Result='Total').set_index(['Client', 'Result']) # 合并原数据和总计行,然后按Client排序 combined = pd.concat([full_grouped, total_rows]).sort_index(level='Client')
步骤3:添加分组间的空行间隔
最后在不同Client的分组之间插入空行,让结果更易读:
# 获取每个Client的行索引位置,准备插入空行 client_indices = combined.index.get_level_values('Client').unique() insert_positions = [] for i in range(1, len(client_indices)): # 找到当前Client的第一行位置,在前一个Client的最后一行后插入空行 pos = combined.index.get_loc((client_indices[i], all_results[0])) insert_positions.append(pos) # 创建空行DataFrame,索引用(' ', '')来占位 empty_row = pd.DataFrame({'size': [None]}, index=pd.MultiIndex.from_tuples([(' ', '')], names=['Client', 'Result'])) # 逐个插入空行(倒序插入避免位置偏移) for pos in reversed(insert_positions): combined = pd.concat([combined.iloc[:pos], empty_row, combined.iloc[pos:]]).reset_index(drop=False) # 重置索引后再设置回MultiIndex combined = combined.set_index(['Client', 'Result'])
现在打印combined就能看到符合要求的结果了:每个Client下的11种Result都被补全(未出现的统计值为0)、每个Client最后有总计行、不同Client分组之间有空白行间隔。
内容的提问来源于stack exchange,提问作者Peter Lucas
相关产品推荐
相关产品推荐

