You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按分组替换异常值?优化Pandas收益率修正代码

我明白你现在的问题——p3那两个极端收益率(-90%和900%)拉垮了后续的价格修正效果,而且原来用日期中位数替换异常值的逻辑不太合理,毕竟不同类型(p1/p2/p3)的正常收益率分布本来就不一样。咱们可以把整个流程封装成更清晰、可复用的函数,同时修正异常值替换的逻辑,让结果更贴合实际需求。

优化思路拆解

  • 先明确异常值判断规则:按每个type分组,筛选出收益率绝对值超过阈值(比如0.5)的记录
  • 对每个type,计算正常收益率的统计值(均值/中位数,可灵活配置)
  • 用对应type的统计值替换异常收益率
  • 基于修正后的收益率重新计算价格,严格保留初始价格的基准

优化后的代码实现

import pandas as pd

def clean_and_adjust_perf(df, threshold=0.5, stat='median'):
    # 1. 计算原始收益率
    df['perf'] = df.groupby('type')['price'].pct_change()
    
    # 2. 标记异常值:按type分组,收益率绝对值超过阈值则标记为True
    df['is_outlier'] = df.groupby('type')['perf'].transform(
        lambda x: x.abs() >= threshold
    )
    
    # 3. 校验统计类型,计算每个type的正常收益率统计值
    if stat not in ['mean', 'median']:
        raise ValueError("stat参数只能是'mean'或'median'")
    
    type_stats = df[~df['is_outlier']].groupby('type')['perf'].agg(stat)
    
    # 4. 替换异常值:异常值用对应type的统计值填充,正常值保留原始值
    df['perf_clean'] = df.apply(
        lambda row: type_stats[row['type']] if row['is_outlier'] else row['perf'],
        axis=1
    )
    
    # 5. 计算修正后的价格:初始价格 × 修正后收益率的累积乘积
    df['price2'] = df.groupby('type').apply(
        lambda g: g['price'].iloc[0] * (1 + g['perf_clean']).cumprod()
    ).reset_index(level=0, drop=True)
    
    return df.sort_values(['type', 'date'])

# 你的原始数据生成代码
data = {'p1': [100., 101, 102, 100, 100], 
        'p2': [100., 99., 98., 100., 100], 
        'p3': [1000., 1000., 100., 1000., 1000]}
df = (pd.DataFrame(data, index=pd.bdate_range(start='20100101', periods=5))
      .stack()
      .reset_index()
      .rename(columns={'level_0': 'date', 'level_1': 'type', 0: 'price'})
      .sort_values('date'))

# 使用函数处理,这里选择用中位数替换异常值
df_cleaned = clean_and_adjust_perf(df, threshold=0.5, stat='median')
print(df_cleaned[['date', 'type', 'price', 'perf', 'perf_clean', 'price2']])

关键改进点

  1. 逻辑更合理:异常值替换用的是同类型的正常收益率统计值,而非跨类型的日期中位数,符合不同产品收益率的分布特性
  2. 灵活复用:封装成函数后,你可以随时调整异常阈值(比如改成0.3)、切换均值/中位数,不用重复编写冗余代码
  3. 可读性提升:每一步都有明确注释,变量名更直观(比如is_outlier标记异常、perf_clean存储修正后的收益率)
  4. 避免索引错误:用apply结合分组统计值替换的方式,比原来的链式loc操作更稳定,不容易出现索引不匹配的问题

比如针对p3的异常值,它的正常收益率只有0,所以中位数为0,替换后p3的perf_clean会变成0,修正后的price2会保持初始的1000,不会出现大起大落的情况,完全符合你的预期。

内容的提问来源于stack exchange,提问作者Jacques Tebeka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 15:57:55