如何在Python的Pandas DataFrame中按条件生成Other计算行
Pandas DataFrame中生成「Other」行的最优实现方法
需求说明
需要为每个Name分组生成「Other」行,计算逻辑为:Other = Total - (Source_1 + Source_2 + Source_3),即Total行的数值减去同组内所有非Total Source的数值之和。
初始数据
| Name | Source | Lead | Sale |
|---|---|---|---|
| Prop_A | Source_1 | 100 | 3 |
| Prop_A | Source_2 | 50 | 5 |
| Prop_A | Source_3 | 20 | 0 |
| Prop_A | Total | 300 | 11 |
| Prop_B | Source_1 | 200 | 10 |
| Prop_B | Source_2 | 300 | 6 |
| Prop_B | Source_3 | 20 | 0 |
| Prop_B | Total | 700 | 23 |
目标结果
| Name | Source | Lead | Sale |
|---|---|---|---|
| Prop_A | Source_1 | 100 | 3 |
| Prop_A | Source_2 | 50 | 5 |
| Prop_A | Source_3 | 20 | 0 |
| Prop_A | Other | 130 | 3 |
| Prop_A | Total | 300 | 11 |
| Prop_B | Source_1 | 200 | 10 |
| Prop_B | Source_2 | 300 | 6 |
| Prop_B | Source_3 | 20 | 0 |
| Prop_B | Other | 180 | 7 |
| Prop_B | Total | 700 | 23 |
原实现代码
Total_df = df[df['Source'] == 'Total'] All_Sources_df = df[df['Source'] != 'Total'] All_Sources_df = All_Sources_df.groupby(['Name'], as_index=False).sum() result = pd.merge(Total_df, All_Sources_df, on=['Name']) result['Lead'] = result['Lead_x'] - result['Lead_y'] result['Sale'] = result['Sale_x'] - result['Sale_y'] result = result[['Name', 'Lead', 'Sale']] result['Source'] = 'Other' result = result[['Name','Source','Lead','Sale']]
优化实现方案
以下两种方案更简洁高效,减少中间变量,逻辑更连贯:
方案一:链式调用+合并
利用merge结合assign链式生成Other行,再通过concat合并所有数据,避免多次列重排操作:
import pandas as pd # 构造示例数据(实际使用时替换为你的df) data = [ ['Prop_A', 'Source_1', 100, 3], ['Prop_A', 'Source_2', 50, 5], ['Prop_A', 'Source_3', 20, 0], ['Prop_A', 'Total', 300, 11], ['Prop_B', 'Source_1', 200, 10], ['Prop_B', 'Source_2', 300, 6], ['Prop_B', 'Source_3', 20, 0], ['Prop_B', 'Total', 700, 23] ] df = pd.DataFrame(data, columns=['Name', 'Source', 'Lead', 'Sale']) # 1. 分离Total和非Total行 total_rows = df[df['Source'] == 'Total'] non_total_rows = df[df['Source'] != 'Total'] # 2. 计算非Total行的分组总和,生成Other行 other_rows = ( total_rows.merge(non_total_rows.groupby('Name')[['Lead', 'Sale']].sum().reset_index(), on='Name') .assign( Lead=lambda x: x['Lead_x'] - x['Lead_y'], Sale=lambda x: x['Sale_x'] - x['Sale_y'], Source='Other' )[['Name', 'Source', 'Lead', 'Sale']] ) # 3. 合并并排序 final_df = pd.concat([non_total_rows, other_rows, total_rows])\ .sort_values(['Name', 'Source'])\ .reset_index(drop=True) # 调整Source顺序,把Total移到最后(可选) final_df['Source'] = pd.Categorical( final_df['Source'], categories=['Source_1', 'Source_2', 'Source_3', 'Other', 'Total'], ordered=True ) final_df = final_df.sort_values(['Name', 'Source']).reset_index(drop=True) print(final_df)
方案二:透视表重塑(适合Source固定的场景)
如果Source的类型固定(比如只有Source_1/2/3和Total),可以用pivot_table快速计算,再重塑回原格式:
import pandas as pd # 构造示例数据 df = pd.DataFrame(data, columns=['Name', 'Source', 'Lead', 'Sale']) # 1. 透视表分组计算 pivot = df.pivot_table( index='Name', columns='Source', values=['Lead', 'Sale'], aggfunc='sum' ) pivot.columns = ['_'.join(col) for col in pivot.columns] # 2. 计算Other值 pivot['Lead_Other'] = pivot['Lead_Total'] - pivot[['Lead_Source_1', 'Lead_Source_2', 'Lead_Source_3']].sum(axis=1) pivot['Sale_Other'] = pivot['Sale_Total'] - pivot[['Sale_Source_1', 'Sale_Source_2', 'Sale_Source_3']].sum(axis=1) # 3. 重塑回原格式 final_df = ( pivot.unstack().reset_index(name='Value') .rename(columns={'level_0': 'Metric', 'Source': 'Source'}) .pivot(index=['Name', 'Source'], columns='Metric', values='Value') .reset_index() [['Name', 'Source', 'Lead', 'Sale']] ) # 调整Source顺序 final_df['Source'] = pd.Categorical( final_df['Source'], categories=['Source_1', 'Source_2', 'Source_3', 'Other', 'Total'], ordered=True ) final_df = final_df.sort_values(['Name', 'Source']).reset_index(drop=True) print(final_df)
优化优势
- 减少中间变量:用链式调用替代多个独立的DataFrame赋值,代码更简洁易读
- 逻辑连贯:从计算到合并的流程一气呵成,降低出错概率
- 效率提升:避免不必要的列选择和重排操作,大数据量下性能更优
内容的提问来源于stack exchange,提问作者Juana
相关产品推荐
相关产品推荐

