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

如何优化Pandas按日期累计计算持股数量的代码实现?

问题描述

我有2个DataFrame:

  • df_hist:存储股票价值的每日数据
  • df_buy_data:存储股票买入日期的相关数据

需求是基于df_buy_data按日期计算累计持股数量,将对应持股数据匹配到df_hist的每一行中。目前通过遍历DataFrame的方式实现了需求,但认为实现方式不够优雅,原实现代码如下:

import pandas as pd

hist_data={'Date':['2022-01-01','2022-01-02','2022-01-03','2022-01-04'],'Value':[23,22,21,24]}
df_hist=pd.DataFrame(hist_data)

buy_data={'Date':['2022-01-01','2022-01-04'],'Ticker': ['Index1', 'Index1'], 'NumberOfShares':[15,29]}
df_buy_data = pd.DataFrame(buy_data)

for i, historical_row in df_hist.iterrows():
        
        ticker_count = df_buy_data.loc[(df_buy_data['Date'] <= historical_row['Date'])]\
                                    .groupby('Ticker').sum()['NumberOfShares']

        if(len(ticker_count)>0):
            df_hist.at[i,'Index1_NumberOfShares'] = ticker_count.item()
        else:
            df_hist.at[i, 'Index1_NumberOfShares'] = 0

df_hist
优化方案

原写法用iterrows()做Python层逐行循环,没有用到pandas底层的C级优化,数据量稍大时性能会非常差。这类时间序列累计匹配的场景,用pandas内置的向量化API就能高效实现,代码更简洁,运行速度比循环高2~3个数量级。

单标的实现(适配当前示例)

如果只需要处理单个股票标的,直接用pd.merge_asof即可,这个API就是专门为“匹配小于等于当前时间点的最近记录”这类场景设计的:

# 先将日期列转为datetime类型,避免字符串比较出现顺序错误
df_hist['Date'] = pd.to_datetime(df_hist['Date'])
df_buy_data['Date'] = pd.to_datetime(df_buy_data['Date'])

# 按日期聚合买入量,计算累计持股
buy_cum = (df_buy_data.groupby('Date', as_index=False)['NumberOfShares'].sum()
           .sort_values('Date')
           .assign(cum_shares = lambda x: x['NumberOfShares'].cumsum())
          )

# 按日期做向后匹配合并,自动对齐每个历史日期之前的累计持股
df_hist = pd.merge_asof(
    df_hist.sort_values('Date'),
    buy_cum[['Date', 'cum_shares']],
    on='Date',
    direction='backward'
).rename(columns={'cum_shares':'Index1_NumberOfShares'})

# 无买入记录的日期持股数填充为0
df_hist['Index1_NumberOfShares'] = df_hist['Index1_NumberOfShares'].fillna(0).astype(int)

运行结果和原循环逻辑完全一致:

DateValueIndex1_NumberOfShares
2022-01-012315
2022-01-022215
2022-01-032115
2022-01-042444

多标的通用实现

如果后续df_buy_data会包含多个不同Ticker的买入记录,可以用透视+重采样的方式一次性计算所有标的的累计持股,不需要修改循环逻辑:

# 日期格式转换
df_hist['Date'] = pd.to_datetime(df_hist['Date'])
df_buy_data['Date'] = pd.to_datetime(df_buy_data['Date'])

# 按日期、标的聚合买入量,转为日期为索引、标的为列的宽表
buy_pivot = (df_buy_data.pivot_table(
    index='Date', 
    columns='Ticker', 
    values='NumberOfShares', 
    aggfunc='sum'
).sort_index())

# 对齐到历史表的所有日期,计算累计值后前向填充空缺
buy_cum_all = buy_pivot.reindex(df_hist['Date']).cumsum().ffill().fillna(0).astype(int)

# 统一列名后缀,合并回历史数据表
buy_cum_all.columns = [f'{col}_NumberOfShares' for col in buy_cum_all.columns]
df_hist = pd.concat([df_hist.set_index('Date'), buy_cum_all], axis=1).reset_index()

提示:所有涉及日期比较、计算的逻辑,都要先把日期列转为datetime64类型,不要直接用字符串比较,否则会出现类似'2022-1-10' < '2022-01-02'的判断错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 22:24:12