如何为DataFrame中每个客户添加小时数汇总的总计行?
给DataFrame每个客户添加总计行的正确姿势
嘿,我来帮你搞定这个需求!你之前用循环+df.append()的方式踩坑太正常了——不仅append()已经被pandas官方弃用,而且循环处理的逻辑和效率都有问题,咱们换个更优雅高效的方法。
先说说你原来方法的问题
- 循环逻辑错误:你写的
for hours in df其实是在遍历DataFrame的列名,不是遍历每个客户,这根本没按客户分组计算总和; - append的坑:
df.append()不会修改原DataFrame,它返回一个新对象,你如果没把结果重新赋值给df,等于白忙活;而且就算赋值了,循环多次生成中间对象,大数据量下效率极低; - 已被弃用:pandas 2.0之后
append()就被标记为弃用,官方推荐用pd.concat()替代。
正确实现方法(两种常用思路)
假设你的DataFrame结构类似这样(有customer客户列、hours小时列,还有其他业务列比如date):
import pandas as pd data = { 'customer': ['A', 'A', 'B', 'B'], 'date': ['2024-01-01', '2024-01-02', '2024-01-01', '2024-01-02'], 'hours': [2, 3, 1, 4] } df = pd.DataFrame(data)
方法一:分组生成总计行 + 合并排序
这种方法适合列数不多的情况,手动指定聚合规则:
# 1. 按客户分组,生成每个客户的总计行 total_rows = df.groupby('customer').agg( customer=('customer', 'first'), # 保留客户名称 date=('date', lambda x: 'Total'), # 日期列标记为Total hours=('hours', 'sum') # 汇总小时数 ).reset_index(drop=True) # 2. 合并原数据和总计行,排序让总计行在每个客户的最后 result = pd.concat([df, total_rows]).sort_values( by=['customer', 'date'], key=lambda x: x.ne('Total').astype(int) # 让'Total'排在该客户数据的最后 ).reset_index(drop=True) print(result)
输出结果:
customer date hours 0 A 2024-01-01 2 1 A 2024-01-02 3 2 A Total 5 3 B 2024-01-01 1 4 B 2024-01-02 4 5 B Total 5
方法二:自动适配多列的分组生成法
如果你的DataFrame有很多列,不想手动写每个列的聚合规则,可以用这种方式:
# 1. 给原数据标记非总计行 df['is_total'] = False # 2. 分组生成总计行:复制客户的基础信息,小时数取总和,其他列标记为Total total_df = df.groupby('customer').apply( lambda x: pd.Series( {**x.iloc[0].to_dict(), 'hours': x['hours'].sum(), 'date': 'Total', 'is_total': True} ) ).reset_index(drop=True) # 3. 合并、排序并清理临时列 result = pd.concat([df, total_df]).sort_values( by=['customer', 'is_total'] # 非总计行在前,总计行在后 ).drop('is_total', axis=1).reset_index(drop=True)
注意事项
- 如果你的业务列不需要标记为
Total,可以根据需求调整聚合规则(比如留空、保留客户名称等); - 大数据量下,这两种方法都比循环高效得多,因为pandas的groupby是向量化操作,避免了循环的性能损耗。
内容的提问来源于stack exchange,提问作者Timothy Mcwilliams
相关产品推荐
相关产品推荐

