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

如何加速/替换Python for循环:百万级DataFrame性能优化求助

优化百万行DataFrame的循环效率:用Pandas向量化操作替代嵌套循环

你的代码慢的核心原因是多层嵌套的Python级循环——尤其是内层遍历merged_data每行的操作,在百万行数据下会带来巨大的性能开销。Pandas的优势在于向量化操作和C级别的窗口计算,我们可以完全替换掉这些循环,把运行时间从小时级压缩到分钟甚至秒级。

先梳理原代码的核心逻辑

你要实现的是:

  • 对每个资产,针对多个时间窗口offset,计算:
    1. 窗口内正收益天数占比(Pct_positive_{offset})
    2. 窗口内跑赢标普500的天数占比(Pct_beating_{offset})
    3. 过去offset期的涨跌幅(Pct_change_{offset})
    4. 未来offset期的涨跌幅(Pct_change_plus_{offset})

优化方案:用Rolling窗口+向量化计算替代循环

下面是重构后的代码,我会逐部分解释优化点:

import pandas as pd

def get_previous_next_returns(portfolio, total_returns):
    # 提前计算标普500的日收益(避免每次合并后重复计算)
    sp_500['return_sp'] = sp_500['Close'].pct_change(periods=1)
    
    assets = []
    for idx, asset in enumerate(portfolio, 1):
        print(f"Processing asset {idx}: {asset.name}")
        try:
            # 1. 提前计算资产的日收益
            asset['return_stock'] = asset['Close'].pct_change(periods=1)
            
            # 2. 合并数据(只做一次,而非每个offset都重复合并)
            merged_data = pd.merge(asset, sp_500, on='Date', how='inner')
            
            # 3. 定义需要处理的offset列表
            offsets = [1,5,15,30,45,60,75,90,120,150,200,250,500,750,1000,1250,1500]
            
            for offset in offsets:
                print(f"  Handling offset {offset}")
                
                # --------------------------
                # 优化:用Rolling窗口计算正收益天数占比
                # --------------------------
                # 窗口内正收益的天数:return_stock > 0,rolling求和后除以窗口大小
                rolling_positive = (merged_data['return_stock'] > 0).rolling(window=offset, min_periods=offset).sum()
                merged_data[f'Pct_positive_{offset}'] = rolling_positive / offset
                
                # --------------------------
                # 优化:用Rolling窗口计算跑赢标普的天数占比
                # --------------------------
                # 窗口内跑赢的天数:return_stock > return_sp,rolling求和后除以窗口大小
                rolling_beating = (merged_data['return_stock'] > merged_data['return_sp']).rolling(window=offset, min_periods=offset).sum()
                merged_data[f'Pct_beating_{offset}'] = rolling_beating / offset
                
                # --------------------------
                # 优化:向量化计算涨跌幅(原逻辑不变,但避免循环)
                # --------------------------
                # 过去offset期的涨跌幅
                merged_data[f'Pct_change_{offset}'] = merged_data['Close_x'].pct_change(periods=offset)
                # 未来offset期的涨跌幅(用shift替代pct_change的负periods,效果一致)
                merged_data[f'Pct_change_plus_{offset}'] = merged_data['Close_x'].shift(-offset) / merged_data['Close_x'] - 1
            
            # 将处理后的资产数据加入列表和总收益表
            assets.append(merged_data)
            total_returns = pd.concat([total_returns, merged_data], ignore_index=True)
        
        except Exception as e:
            print(f"Error processing asset {idx}: {asset.name} - {str(e)}")
    
    return assets, total_returns

关键优化点说明

  • 避免重复计算:提前计算标普500和资产的日收益,资产与标普的合并仅执行一次,消除每个offset循环内的冗余操作
  • 用Rolling窗口替代行级循环:原代码逐行切片计算窗口统计量的逻辑,替换为Pandas内置的rolling方法——这是C级别的高效实现,速度比Python循环快100-1000倍,同时min_periods=offset自动过滤数据不足的窗口,替代原代码中index-offset>0的判断
  • 简化未来涨跌幅计算:用shift(-offset)/Close - 1替代pct_change(periods=-offset),逻辑等价且更直观
  • 更清晰的异常处理:捕获所有异常并打印具体信息,方便排查问题

额外性能建议

  • 如果资产数量较多,可以用concurrent.futures实现多进程/多线程并行处理单个资产,进一步压缩总运行时间
  • 确保Date列为datetime类型且已排序,Rolling窗口依赖有序的时间序列
  • 若内存允许,可将数据转换为float32类型,减少内存占用并提升运算速度

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:55:19