如何在Pandas DataFrame中按ID计算特定rate差值并新增列?
Pandas按客户计算费率差值的解决方案
问题背景
原始DataFrame结构如下:
| ID | rate | Sequential number |
|---|---|---|
| a | 150 | 1 |
| a | 150 | 1 |
| a | 50 | 2 |
| b | 250 | 1 |
| c | 25 | 1 |
| d | 25 | 1 |
| d | 40 | 2 |
| d | 25 | 3 |
字段说明:
- ID:客户唯一标识
- rate:月度费率
- Sequential number:客户每次更改费率时递增的序号
需求明确:
对每个ID,找到Sequential number最大值对应的rate,减去该ID下Sequential number最小值对应的rate,将差值作为新列rate_diff添加到DataFrame;若ID仅有一条记录,rate_diff为0。仅在该ID的最大序号行显示差值,其余行填0。
期望结果:
| ID | rate | Sequential number | rate_diff |
|---|---|---|---|
| a | 150 | 1 | 0 |
| a | 150 | 1 | 0 |
| a | 50 | 2 | -100 |
| b | 250 | 1 | 0 |
| c | 25 | 1 | 0 |
| d | 25 | 1 | 0 |
| d | 40 | 2 | 0 |
| d | 30 | 3 | 5 |
你之前尝试的代码:
df['diff_rate'] = df.groupby('ID')['rate'].transform(lambda x : x-x.min())
不符合预期的原因是:这段代码是用每条记录的rate减去该ID下rate的最小值,而非按Sequential number的最大、最小值对应的rate计算差值,也没实现仅在最大序号行显示差值的逻辑。
解决方案
可以通过分组提取每个ID的初始费率(最小序号对应rate)和最新费率(最大序号对应rate),再结合行序号判断计算差值:
方法一:分步实现
import pandas as pd # 原始数据 data = [ ['a', 150, 1], ['a', 150, 1], ['a', 50, 2], ['b', 250, 1], ['c', 25, 1], ['d', 25, 1], ['d', 40, 2], ['d', 30, 3] ] df = pd.DataFrame(data, columns=['ID', 'rate', 'Sequential number']) # 1. 提取每个ID的初始费率和最新费率 id_rate_info = df.groupby('ID').apply( lambda group: pd.Series({ 'initial_rate': group[group['Sequential number'] == group['Sequential number'].min()]['rate'].iloc[0], 'latest_rate': group[group['Sequential number'] == group['Sequential number'].max()]['rate'].iloc[0] }) ).reset_index() # 2. 合并回原DataFrame df = df.merge(id_rate_info, on='ID', how='left') # 3. 计算rate_diff:仅最大序号行显示差值,其余为0 df['rate_diff'] = df.apply( lambda row: row['latest_rate'] - row['initial_rate'] if row['Sequential number'] == df[df['ID'] == row['ID']]['Sequential number'].max() else 0, axis=1 ) # 4. 清理中间列(可选) df.drop(['initial_rate', 'latest_rate'], axis=1, inplace=True) print(df)
方法二:更简洁的transform写法
import pandas as pd # 原始数据同上 df = pd.DataFrame(data, columns=['ID', 'rate', 'Sequential number']) # 获取每个ID的最大序号 df['max_seq'] = df.groupby('ID')['Sequential number'].transform('max') # 获取每个ID的初始费率(最小序号对应的rate) df['initial_rate'] = df.groupby('ID').apply( lambda g: g[g['Sequential number'] == g['Sequential number'].min()]['rate'].iloc[0] ).reset_index(level=0, drop=True) # 获取每个ID的最新费率(最大序号对应的rate) df['latest_rate'] = df.groupby('ID').apply( lambda g: g[g['Sequential number'] == g['Sequential number'].max()]['rate'].iloc[0] ).reset_index(level=0, drop=True) # 计算差值 df['rate_diff'] = df.apply( lambda x: x['latest_rate'] - x['initial_rate'] if x['Sequential number'] == x['max_seq'] else 0, axis=1 ) # 清理中间列 df.drop(['max_seq', 'initial_rate', 'latest_rate'], axis=1, inplace=True) print(df)
两种方法都能得到符合预期的结果,核心是先定位每个ID的最小/最大序号对应的费率,再判断当前行是否是最大序号行,进而计算差值。
内容的提问来源于stack exchange,提问作者gloria_1990
相关产品推荐
相关产品推荐

