Pandas按指定列分组后统计分组总数及其他列值子计数
问题:如何在Pandas分组统计中获取多列不同取值的子计数?
我有如下DataFrame:
import pandas as pd df = pd.DataFrame({ 'org':['a','a','a','a','b','b'], 'product_version':['bpm','bpm','bpm','bpm','ppp','ppp'], 'release_date':['2022-07','2022-07','2022-07','2022-07','2022-08','2022-08'], 'date_avail':['no','no','no','yes','no','no'], 'status':['green','green','yellow','yellow','green','green'] })
数据展示如下:
org product_version release_date date_avail status 0 a bpm 2022-07 no green 1 a bpm 2022-07 no green 2 a bpm 2022-07 no yellow 3 a bpm 2022-07 yes yellow 4 b ppp 2022-08 no green 5 b ppp 2022-08 no green
我已经实现按['org','product_version','release_date']列分组并统计分组总数:
print(df.groupby(['org','product_version','release_date']).size())
输出结果:
org product_version release_date a bpm 2022-07 4 b ppp 2022-08 2
现在需要进一步获取分组中date_avail、status列不同取值的子计数,最终期望得到如下格式的结果:
org product release_date total number_of_no number_of_yes number_of_green number_of_yellow a bpm 2022-07 4 3 1 2 2 b ppp 2022-08 2 2 0 2 0
解决方案
可以通过分组聚合结合自定义统计逻辑实现,以下是几种简洁的方法:
方法1:分组聚合+多表合并
分别统计总计数、各列取值数,再合并结果:
# 计算分组总计数 total_counts = df.groupby(['org','product_version','release_date']).size().rename('total').reset_index() # 统计date_avail列各取值数量 date_counts = df.groupby(['org','product_version','release_date'])['date_avail'].value_counts()\ .unstack(fill_value=0).add_prefix('number_of_').reset_index() # 统计status列各取值数量 status_counts = df.groupby(['org','product_version','release_date'])['status'].value_counts()\ .unstack(fill_value=0).add_prefix('number_of_').reset_index() # 合并所有统计结果 result = total_counts.merge(date_counts, on=['org','product_version','release_date'])\ .merge(status_counts, on=['org','product_version','release_date']) # 重命名列匹配期望格式 result = result.rename(columns={'product_version': 'product'}) print(result)
输出结果:
org product release_date total number_of_no number_of_yes number_of_green number_of_yellow 0 a bpm 2022-07 4 3 1 2 2 1 b ppp 2022-08 2 2 0 2 0
方法2:交叉表一次性生成统计结果
利用pd.crosstab直接关联分组维度和目标列取值:
# 生成交叉表,行是分组维度,列是目标列的取值 cross_tab = pd.crosstab( index=[df['org'], df['product_version'], df['release_date']], columns=[df['date_avail'], df['status']], margins=False, dropna=False ).fillna(0).astype(int) # 重命名列名 cross_tab.columns = cross_tab.columns.map(lambda x: f'number_of_{x[0]}' if pd.isna(x[1]) else f'number_of_{x[1]}') # 补充总计数列 cross_tab['total'] = cross_tab.sum(axis=1) # 重置索引并调整列顺序 result = cross_tab.reset_index().rename(columns={'product_version': 'product'}) result = result[['org','product','release_date','total','number_of_no','number_of_yes','number_of_green','number_of_yellow']] print(result)
方法3:哑变量转换+分组求和
将目标列转为哑变量后直接分组求和,适合取值固定的场景:
# 将date_avail和status转为哑变量 dummy_df = pd.get_dummies(df, columns=['date_avail', 'status'], prefix='number_of', prefix_sep='_') # 分组求和并计算总计数 result = dummy_df.groupby(['org','product_version','release_date']).sum().reset_index() result['total'] = result.filter(like='number_of_').sum(axis=1) # 调整列名和顺序 result = result.rename(columns={'product_version': 'product'}) result = result[['org','product','release_date','total','number_of_no','number_of_yes','number_of_green','number_of_yellow']] print(result)
内容的提问来源于stack exchange,提问作者moth
相关产品推荐
相关产品推荐

