如何调整groupby+unstack的列名?兼顾性能与列名可读性
解决Groupby+Unstack后的多层列名问题
首先我注意到你原来的groupby代码里有个小笔误——你在agg里写的是tuning_hours,但样本数据里的目标字段是watch_hours,先修正这个细节,然后我们来搞定列名的问题。
最优方案:简化聚合写法,直接得到规范列名
你之所以会得到多层列名,是因为用了agg({'watch_hours':['sum']})这种字典+列表的聚合写法,它会生成包含字段名、聚合函数名的多层索引。其实如果只需要对watch_hours求和,完全可以简化写法,这样unstack之后直接就能得到和pivot_table一样的规范列名,同时保留groupby的高性能:
# 优化后的Groupby方案,性能不变,列名和pivot_table一致 df_grouped = df_tier.groupby(['device_id', 'net_grp'])['watch_hours'].sum() result = df_grouped.unstack(level='net_grp').fillna(0).reset_index()
执行这段代码后,结果的列名就是各个net_grp的取值(比如TCH、NJV、SHX等),和pivot_table输出的列格式完全一致,而且性能和你之前测试的15ms差不多,甚至可能更快——因为减少了多层索引的生成开销。
备选方案:处理已生成的多层列名
如果因为业务需求,你必须保留原来的字典式聚合写法(比如需要同时执行多个聚合操作),可以通过修改MultiIndex列名来提取net_grp的取值:
# 修正笔误后的原Groupby代码 result = df_tier.groupby(['device_id','net_grp']).agg({'watch_hours':['sum']}).unstack(level='net_grp').fillna(0).reset_index() # 提取多层列索引的最后一层(也就是net_grp的取值)作为列名 result.columns = result.columns.get_level_values(-1) # 把device_id的列名修正回来(reset_index后它的列名会变成空字符串) result = result.rename(columns={'': 'device_id'}).set_index('device_id')
这样处理后,列名就只剩下net_grp的各个取值,和pivot_table的输出一致。
为什么这个方案可行?
- 第一种简化写法直接跳过了多层索引的生成,聚合结果是单层Series,unstack后自然用
net_grp的值作为列名,完全符合你的需求。 - 第二种方法针对已生成的MultiIndex,通过
get_level_values(-1)直接取最底层的net_grp值作为列名,再修正device_id的列名即可。
这样你就能在享受groupby高性能的同时,得到和pivot_table一样易用的输出格式了。
内容的提问来源于stack exchange,提问作者Shuvayan Das
相关产品推荐
相关产品推荐

