如何在DataFrame分组中跨不同索引实现PL与Position的除法运算
问题:跨日期分组计算比值列
现有DataFrame结构
构造的DataFrame以Position Date、Client、Product、Buy Date为多层索引,包含Position和PL两列,初始数据如下:
| Position Date | Client | Product | Buy Date | Position | PL |
|---|---|---|---|---|---|
| 2022-01-02 | Client 1 | Product 1 | 2022-05-02 | 130 | 0 |
| 2022-01-02 | Client 1 | Product 4 | 2022-06-02 | 5000 | 0 |
| 2022-01-02 | Client 2 | Product 2 | 2022-03-12 | 120 | 0 |
| 2022-01-02 | Client 2 | Product 3 | 2022-01-25 | 77 | 0 |
| 2022-01-03 | Client 1 | Product 4 | 2022-06-02 | 7000 | 2000 |
| 2022-01-03 | Client 2 | Product 2 | 2022-03-12 | 200 | 80 |
| 2022-01-03 | Client 2 | Product 3 | 2022-01-25 | 100 | 23 |
构造代码:
import pandas as pd df = pd.DataFrame() df['Client'] = ['Client 1', 'Client 1','Client 2','Client 2', 'Client 1', 'Client 2','Client 2'] df['Product'] = ['Product 1', 'Product 4', 'Product 2', 'Product 3', 'Product 4', 'Product 2', 'Product 3'] df['Position Date'] = ['2022-01-02', '2022-01-02', '2022-01-02', '2022-01-02', '2022-01-03', '2022-01-03', '2022-01-03'] df['Buy Date'] = ['2022-05-02', '2022-06-02', '2022-03-12', '2022-01-25', '2022-06-02', '2022-03-12', '2022-01-25'] df['Position'] = [130, 5000, 120, 77, 7000, 200, 100] df['PL'] = [0,0,0,0, 50, 2000, 80, 100] df = df.set_index(['Position Date','Client', 'Product', 'Buy Date'], drop=True) df['PL'] = df.groupby(level=['Client', 'Product', 'Buy Date']).diff().fillna(0)
需求:创建新列X
新列X的计算规则:
- 首日(最早的
Position Date)的X值为0 - 非首日的
X值 = 当日的PL值 ÷ 该Client-Product-Buy Date分组上一日的Position值
举个例子:2022-01-03当天,Client 2、Product 2的X值 = 80 ÷ 120(120是2022-01-02该分组的Position值)
预期最终结果中,非首日的X值分别为:2000/5000、80/120、23/77
之前方案的问题
之前使用基于reindex的方案:
df['output'] = df['PL'].div(df.loc[df.index[0][0], 'Position'] .reindex(df.droplevel('Position Date').index).values )
但当不同Position Date的分组结构不一致时(比如2022-01-03的Client 1分组比2022-01-02少了Product 1的条目),该方案无法正确匹配分组,导致计算错误。
解决方案
通过按Client-Product-Buy Date分组后,对每个分组内的Position进行移位实现,不受分组结构变化的影响:
# 按Client、Product、Buy Date分组,提取上一日的Position值 prev_position = df.groupby(level=['Client', 'Product', 'Buy Date'])['Position'].shift(1) # 计算X列:PL除以移位后的Position,首日无数据填充为0 df['X'] = df['PL'].div(prev_position).fillna(0)
逻辑说明
groupby(level=['Client', 'Product', 'Buy Date'])确保仅在同一客户-产品-购买日期的分组内处理数据shift(1)将每个分组内的Position值向上移动一行,正好对应上一日的Position数据- 首日的移位结果为
NaN,用fillna(0)填充为0,符合需求
验证结果
执行后df['X']的结果如下:
| Position Date | Client | Product | Buy Date | X |
|---|---|---|---|---|
| 2022-01-02 | Client 1 | Product 1 | 2022-05-02 | 0.0 |
| 2022-01-02 | Client 1 | Product 4 | 2022-06-02 | 0.0 |
| 2022-01-02 | Client 2 | Product 2 | 2022-03-12 | 0.0 |
| 2022-01-02 | Client 2 | Product 3 | 2022-01-25 | 0.0 |
| 2022-01-03 | Client 1 | Product 4 | 2022-06-02 | 0.4 |
| 2022-01-03 | Client 2 | Product 2 | 2022-03-12 | 0.666... |
| 2022-01-03 | Client 2 | Product 3 | 2022-01-25 | 0.2987... |
完全符合预期,且不受不同日期分组结构差异的影响。
内容的提问来源于stack exchange,提问作者João Weckerle
相关产品推荐
相关产品推荐

