分组值计算并应用至重复行:Pandas DataFrame两列计算的问题排查与实现
解决Pandas DataFrame的两个新列创建问题
先看一下你的原始DataFrame结构:
| 索引 | wallet | position | position_rewards | position_type | token_sales |
|---|---|---|---|---|---|
| 0 | 0x123 | SUSHI_LP | 250 | Sushi_LP | 500 |
| 0 | 0x123 | ALCX | 750 | LP Token | 500 |
| 1 | 0xabc | GAMMA | 333.33 | LP Token | 750 |
| 1 | 0xabc | FXS | 666.66 | LD | 750 |
接下来逐个解决你的两个新列需求:
1. 修复position_rewards_pct列的NaN问题
你的原代码问题在于没有按钱包(wallet)分组计算总奖励,而是直接用了整个DataFrame的position_rewards总和,这不仅逻辑不符合需求(你要的是仓位占对应钱包总奖励的比例),还可能因为Decimal类型的运算/广播问题导致NaN。
正确的实现步骤:
- 先按
wallet分组,计算每个钱包的总奖励(用Decimal保证精度) - 将分组后的总奖励合并回原DataFrame
- 计算每个仓位的奖励占比并转换为百分比
示例代码:
from decimal import Decimal # 1. 按wallet分组计算总奖励(转为Decimal避免精度丢失) wallet_total = df.groupby('wallet')['position_rewards'].apply( lambda x: sum(Decimal(str(val)) for val in x) ).reset_index(name='wallet_total_rewards') # 2. 合并回原DataFrame df = df.merge(wallet_total, on='wallet', how='left') # 3. 计算仓位奖励占比,转为Decimal并保留两位小数 df['position_rewards_pct'] = ( df.apply( lambda row: (Decimal(str(row['position_rewards'])) / row['wallet_total_rewards']) * 100, axis=1 ) ).round(2)
运行后,position_rewards_pct列的结果会是:
- 0x123的SUSHI_LP仓位:25.00%
- 0x123的ALCX仓位:75.00%
- 0xabc的GAMMA仓位:33.33%
- 0xabc的FXS仓位:66.67%
2. 创建token_sales_per_type列
根据需求,我们需要按wallet和position_type计算对应类型的已售token数量,核心逻辑是:该类型的奖励占钱包总奖励的比例 × 钱包的token_sales,并且同一position_type的行重复显示这个值,同时保证所有数值为Decimal类型。
实现步骤:
- 先按
wallet和position_type分组,计算该类型的总奖励 - 结合之前的钱包总奖励,计算类型占比
- 乘以
token_sales得到该类型的已售数量 - 合并回原DataFrame,确保同一类型的行重复显示结果
示例代码:
# 1. 按wallet和position_type分组,计算该类型的总奖励 type_total = df.groupby(['wallet', 'position_type'])['position_rewards'].apply( lambda x: sum(Decimal(str(val)) for val in x) ).reset_index(name='type_total_rewards') # 2. 合并钱包总奖励和唯一的token_sales值 type_total = type_total.merge(wallet_total, on='wallet', how='left') type_total = type_total.merge(df[['wallet', 'token_sales']].drop_duplicates(), on='wallet', how='left') # 3. 计算该类型的已售数量(Decimal类型) type_total['token_sales_per_type'] = ( type_total['type_total_rewards'] / type_total['wallet_total_rewards'] ) * type_total['token_sales'].apply(Decimal) # 4. 合并回原DataFrame df = df.merge(type_total[['wallet', 'position_type', 'token_sales_per_type']], on=['wallet', 'position_type'], how='left')
运行后,token_sales_per_type列的结果会是:
- 0x123的SUSHI_LP仓位:125
- 0x123的ALCX仓位:375
- 0xabc的GAMMA仓位:250
- 0xabc的FXS仓位:500
额外注意事项
- 所有数值转换都用
Decimal(str(val)),避免float类型的精度丢失问题 - 如果你的原始字段已经是Decimal类型,可以直接使用,无需二次转换
- 可以根据需求用
.quantize(Decimal('0.00'))来精确控制小数位数
内容的提问来源于stack exchange,提问作者Luiz Scheuer
相关产品推荐
相关产品推荐

