Python中计算费率变更前后Price列平均值(排除变更当月)
计算费率变更前后Price平均值(排除变更当月)
需求说明
针对给定DataFrame完成以下操作:
- 按**客户(若含产品ID则按客户+产品ID)**分组,计算费率(Rate)变更前后Price列的平均值
- 排除费率变更当月的数值,不纳入前后平均值计算(例如XY Ltd8月变更费率,变更前取6、7月数据平均,变更后取9、10月数据平均)
- 新增两列:
New Column:对应行所属阶段的平均值(变更前填变更前平均,变更当月填0,变更后填变更后平均)New column 2:统一为「变更后平均值 - 变更前平均值」的差值,同一分组内所有行取值一致
已尝试代码(无法满足需求)
用户尝试了以下代码,但无法实现排除变更当月的逻辑,也无法计算前后差值:
df['New Column'] = df.groupby(['Client', 'Rate'])['Price'].transform('mean')
示例数据
| Date | Client | Rate | Price |
|---|---|---|---|
| 2022-06-01 | XY Ltd | 1.50 | 5 |
| 2022-07-01 | XY Ltd | 1.50 | 6 |
| 2022-08-01 | XY Ltd | 3.00 | 10 |
| 2022-09-01 | XY Ltd | 3.00 | -3 |
| 2022-10-01 | XY Ltd | 3.00 | -5 |
| 2022-06-01 | ZZ Inc | 1.60 | 3 |
| 2022-07-01 | ZZ Inc | 1.60 | 4 |
| 2022-08-01 | ZZ Inc | 4.00 | 12 |
| 2022-09-01 | ZZ Inc | 4.00 | -4 |
| 2022-10-01 | ZZ Inc | 4.00 | -6 |
期望输出
| Date | Client | Rate | Price | New Column | New column 2 |
|---|---|---|---|---|---|
| 2022-06-01 | XY Ltd | 1.50 | 5 | 5.5 | -9.5 |
| 2022-07-01 | XY Ltd | 1.50 | 6 | 5.5 | -9.5 |
| 2022-08-01 | XY Ltd | 3.00 | 10 | 0.0 | -9.5 |
| 2022-09-01 | XY Ltd | 3.00 | -3 | -4.0 | -9.5 |
| 2022-10-01 | XY Ltd | 3.00 | -5 | -4.0 | -9.5 |
| 2022-06-01 | ZZ Inc | 1.60 | 3 | 3.5 | -8.5 |
| 2022-07-01 | ZZ Inc | 1.60 | 4 | 3.5 | -8.5 |
| 2022-08-01 | ZZ Inc | 4.00 | 12 | 0.0 | -8.5 |
| 2022-09-01 | ZZ Inc | 4.00 | -4 | -5.0 | -8.5 |
| 2022-10-01 | ZZ Inc | 4.00 | -6 | -5.0 | -8.5 |
补充样本数据(含Prod ID)
| Date | Client | Prod ID | Rate | Price |
|---|---|---|---|---|
| 2021-11-30 | LL Inco | 71 | 0 | -29 |
| 2021-11-30 | LL Inco | 73 | 1.6 | 889 |
| 2021-11-30 | LL Inco | 74 | 1.6 | 754 |
| 2021-11-30 | LL Inco | 75 | 1.6 | 2608 |
| 2021-12-31 | LL Inco | 71 | 0 | -31 |
| 2021-12-31 | LL Inco | 73 | 1.6 | 916 |
| 2021-12-31 | LL Inco | 74 | 1.6 | 777 |
| 2021-12-31 | LL Inco | 75 | 1.6 | 2688 |
解决方案代码
核心逻辑
- 转换日期格式并分组识别费率变更的日期
- 标记每行数据所属的阶段(变更前、变更当月、变更后)
- 分别计算各分组的变更前、变更后Price平均值
- 映射填充目标列,计算差值
完整代码
import pandas as pd # 示例数据读取(实际使用时替换为你的数据加载方式) df = pd.DataFrame({ 'Date': ['2022-06-01', '2022-07-01', '2022-08-01', '2022-09-01', '2022-10-01', '2022-06-01', '2022-07-01', '2022-08-01', '2022-09-01', '2022-10-01'], 'Client': ['XY Ltd']*5 + ['ZZ Inc']*5, 'Rate': [1.5, 1.5, 3.0, 3.0, 3.0, 1.6, 1.6, 4.0, 4.0, 4.0], 'Price': [5,6,10,-3,-5,3,4,12,-4,-6] }) # 1. 处理日期列 df['Date'] = pd.to_datetime(df['Date']) # 2. 设置分组键:若使用含Prod ID的数据集,改为group_keys = ['Client', 'Prod ID'] group_keys = ['Client'] # 3. 找出每个分组内的费率变更日期 def get_change_date(group): group = group.sort_values('Date') # 标记Rate发生变化的行 group['Rate_Change'] = group['Rate'].ne(group['Rate'].shift()) # 取第一个变更的日期(排除初始行的默认变化标记) change_dates = group[(group['Rate_Change']) & (group.index != group.index[0])]['Date'] return change_dates.iloc[0] if not change_dates.empty else None change_dates = df.groupby(group_keys).apply(get_change_date).reset_index(name='Change_Date') df = df.merge(change_dates, on=group_keys, how='left') # 4. 标记数据所属阶段 df['Stage'] = pd.cut( df['Date'], bins=[pd.Timestamp.min, df['Change_Date'], pd.Timestamp.max], labels=['Before', 'Change_Month', 'After'], include_lowest=True ) # 无变更的分组统一标记为变更前 df.loc[df['Change_Date'].isna(), 'Stage'] = 'Before' # 5. 计算变更前后的平均值 before_mean = df[df['Stage'] == 'Before'].groupby(group_keys)['Price'].mean().reset_index(name='Before_Mean') after_mean = df[df['Stage'] == 'After'].groupby(group_keys)['Price'].mean().reset_index(name='After_Mean') df = df.merge(before_mean, on=group_keys, how='left') df = df.merge(after_mean, on=group_keys, how='left') # 6. 填充目标列 df['New Column'] = df.apply( lambda x: x['Before_Mean'] if x['Stage'] == 'Before' else (x['After_Mean'] if x['Stage'] == 'After' else 0.0), axis=1 ) # 无变更的分组差值为0 df['New column 2'] = df['After_Mean'].fillna(0) - df['Before_Mean'].fillna(0) # 整理输出列顺序 df = df[['Date', 'Client', 'Rate', 'Price', 'New Column', 'New column 2']] print(df)
适配含Prod ID的数据集
只需将代码中的group_keys改为:
group_keys = ['Client', 'Prod ID']
即可实现按客户+产品ID分组处理,适配补充样本数据的场景。
内容的提问来源于stack exchange,提问作者ross jim
相关产品推荐
相关产品推荐

