如何用更Pythonic的方法按多列条件拆分订单运费金额
订单运费行维度分摊优化方案
需求背景
我们的线上订单数据仅在订单维度统计总运费,但财务人员需要将总运费拆分到行维度的多个供应商名下。
拆分运费时需遵循规则:
- 未发货商品不参与运费分摊
- 参与促销免运费的商品不参与运费分摊
该需求此前是通过ODBC连接SQL数据库拉取数据后,使用Excel公式处理的,现有实现可正常输出结果,但希望找到更高效、更符合Pythonic规范的写法。
原有实现代码
import pandas as pd import numpy as np df = pd.DataFrame({'id': [10, 11, 11, 11, 12, 12, 13, 14, 15, 15], 'shipping': [5, 5, 5, 5, 5, 5, 0, 0, 5, 5], 'shipstatus': [True, True, True, False, True, True, False, True, True, True], 'freeship': [False, True, False, False, False, False, False, True, False, False]}) df['a'] = df.groupby(['id','shipstatus','freeship'])['shipping'].transform('count') # 原逻辑按id、发货状态、是否免运费分组统计数量,避免免运费商品被计入分摊基数 df['b'] = df['a'] * (df['freeship']==False) # 免运费商品的分摊基数直接置0 df['c'] = df['shipping']/df['b'] # 计算单条商品分摊运费,这里会出现除以0导致的inf、运费为0导致的NaN df['LineShipping'] = df['shipstatus'] * (df['freeship']==False) * df['c'] # 不符合分摊条件的行直接置0 df = df.fillna(0) # 处理异常值 df = df.drop(columns=['a','b','c']) # 删除临时列 print(df)
优化后实现
原有逻辑是正确的,我们可以简化中间临时变量的生成,通过分组统计+条件赋值直接实现,代码更简洁,运行效率也更高:
import pandas as pd import numpy as np df = pd.DataFrame({'id': [10, 11, 11, 11, 12, 12, 13, 14, 15, 15], 'shipping': [5, 5, 5, 5, 5, 5, 0, 0, 5, 5], 'shipstatus': [True, True, True, False, True, True, False, True, True, True], 'freeship': [False, True, False, False, False, False, False, True, False, False]}) # 标记符合分摊条件的行 eligible = df['shipstatus'] & (~df['freeship']) # 按订单id分组,统计每个订单下符合分摊条件的商品数 df['eligible_cnt'] = eligible.groupby(df['id']).transform('sum') # 直接按条件生成分摊运费,自动规避异常值 df['LineShipping'] = np.where(eligible & (df['eligible_cnt'] > 0), df['shipping'] / df['eligible_cnt'], 0) # 删除临时计数列 df = df.drop('eligible_cnt', axis=1) print(df)
优化点说明
- 仅生成1个临时计数列,内存占用更低
- 用
np.where直接处理条件分支,避免了除以0产生的inf、运费为0产生的NaN问题,不需要额外调用fillna - 逻辑更直观,可读性更高,完全符合pandas的常用写法规范
输出结果
运行优化后代码得到的结果和预期完全一致,如下:
| id | shipping | shipstatus | freeship | LineShipping |
|---|---|---|---|---|
| 10 | 5 | True | False | 5.0 |
| 11 | 5 | True | True | 0.0 |
| 11 | 5 | True | False | 5.0 |
| 11 | 5 | False | False | 0.0 |
| 12 | 5 | True | False | 2.5 |
| 12 | 5 | True | False | 2.5 |
| 13 | 0 | False | False | 0.0 |
| 14 | 0 | True | True | 0.0 |
| 15 | 5 | True | False | 2.5 |
| 15 | 5 | True | False | 2.5 |
内容的提问来源于stack exchange,提问作者John Henry Erikson
相关产品推荐
相关产品推荐

