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

pandas groupby标记30天内待拆股 赋值失效问题排查

问题说明
  • 数据集为多只股票近5年收盘价数据,通过SecuritiesCode字段区分不同股票,AdjustmentFactor != 1代表对应日期发生股票拆分/反向拆分,数据集样例结构如下:
    数据集样例
  • 目标:新增pending_stocksplit列,标记单只股票当前日期之后30天内是否存在即将发生的拆分事件。
  • 初始实现代码运行后DataFrame未发生任何修改,初始代码如下:
def pending_stocksplit(df):
  stock_split_dates = df[df['AdjustmentFactor'] != 1]['Date'].tolist()
  for i in stock_split_dates:
    df[(df['Date'] >= i - timedelta(days=30)) & (df['Date'] <= i)]['pending_stocksplit'] = 1
  return df

df = df.groupby('SecuritiesCode').apply(pending_stocksplit)
核心问题点

代码完全不生效的核心原因是链式索引赋值失效,这是Pandas最常见的新手坑:

  • 写法df[筛选条件]['pending_stocksplit'] = 1属于连续两次索引操作(先选行、再选列),第一次筛选行返回的是Pandas生成的临时副本,赋值操作实际作用在这个临时副本上,根本没有落地到原始传入的DataFrame,赋值结束后临时副本被回收,原数据自然没有任何变化。Pandas通常会抛出SettingWithCopyWarning提示这个问题,但多数场景下警告容易被忽略。

除此之外代码还有两个潜在问题:

  • 没有提前初始化pending_stocksplit列,哪怕修正了赋值方式,给不存在的列做切片赋值也容易触发异常。
  • 用自然日timedelta(days=30)计算时间窗口,会把周末、节假日等非交易日算入间隔,实际覆盖的交易日数量不足30个,和股票场景的业务逻辑有偏差;另外如果需求是「之后30天内」,拆分事件当天不应该算「即将发生」,条件里的Date <= i需要调整为Date < i。
修正代码

首先确保日期列是标准时间格式,再用.loc做原生赋值避免链式索引,自然日版本的实现如下:

from datetime import timedelta
import pandas as pd

# 先统一日期格式
df['Date'] = pd.to_datetime(df['Date'])

def pending_stocksplit(group):
    # 复制分组避免修改原数据切片,初始化标记列默认值为0
    group = group.copy()
    group['pending_stocksplit'] = 0
    split_dates = group.loc[group['AdjustmentFactor'] != 1, 'Date'].tolist()
    for split_day in split_dates:
        # 构造筛选掩码,用.loc直接在原数据上赋值
        mask = (group['Date'] >= split_day - timedelta(days=30)) & (group['Date'] < split_day)
        group.loc[mask, 'pending_stocksplit'] = 1
    return group

# group_keys=False避免分组键变成额外索引层级
df = df.groupby('SecuritiesCode', group_keys=False).apply(pending_stocksplit)

如果要按30个交易日(而非30个自然日)做判断,推荐用逆序滚动窗口实现,不需要写循环,运行效率高很多,适合Kaggle竞赛的大体量数据集:

def pending_stocksplit_tradeday(group):
    group = group.sort_values('Date').copy()
    # 标记拆分日
    group['_is_split'] = (group['AdjustmentFactor'] != 1).astype(int)
    # 逆序滚动30个交易日窗口,排除当天的拆分事件,只要窗口内有拆分就标记为1
    group['pending_stocksplit'] = (
        group['_is_split']
        .shift(-1) # 错开当天,只看之后的日期
        .rolling(30, min_periods=1)
        .max()
        .fillna(0)
        .astype(int)
    )
    return group.drop(columns=['_is_split'])

df = df.groupby('SecuritiesCode', group_keys=False).apply(pending_stocksplit_tradeday)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:15:38