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

使用df.iterrows()创建类实例运行极慢的优化方案咨询

性能瓶颈核心原因
  • df.iterrows()是pandas所有遍历方式中效率最低的实现,逐行生成Series对象的开销占总耗时的30%以上
  • 重复计算问题严重:同一标的同一交易日的分钟线筛选、时间格式转换、盘前/盘后时段切分、指标计算,会在每遍历到当日日K行时重复执行,甚至同一个盘前时段数据会被多次调用GET_TIME_PERIOD_DATA重复计算
  • 逐行生成单条记录的小DataFrame再循环append、最后concat的写法,会产生大量无意义的内存拷贝操作,额外拖慢速度
  • 所有指标计算均为单值串行计算,完全没有用到pandas/numpy的向量化批量计算能力
具体优化方案

1. 提前完成分钟线全量预处理,彻底消除重复计算

把原来放在SESSION_DATA类里的分钟线筛选、时段切分、指标计算逻辑全部挪到外层,读入单只标的分钟线后一次性处理完所有交易日的指标,存为键为date的查询字典,后续直接取值即可,不需要重复计算。示例代码:

from datetime import time
import numpy as np
import pandas as pd

def preprocess_minute(df_minute):
    # 一次性完成全量时间格式转换
    df_minute['time'] = pd.to_datetime(df_minute['time']).dt.time
    # 提前固定时段判断条件,避免循环内重复判断
    pre_market_cond = (df_minute['time'] >= time(4, 0)) & (df_minute['time'] < time(9, 30))
    after_hours_cond = (df_minute['time'] >= time(16, 0)) & (df_minute['time'] < time(20, 0))
    
    day_metrics = {}
    # 按日期分组批量计算所有当日分钟指标
    for trade_date, day_df in df_minute.groupby('date'):
        # 切分盘前、盘后数据
        pm_df = day_df[pre_market_cond]
        ah_df = day_df[after_hours_cond]
        
        # 批量计算盘前指标
        if len(pm_df) == 0:
            pm_high = pd.NA
            pm_high_time = pd.NA
            pm_200ema_hit = False
        else:
            pm_high = pm_df['high'].max()
            pm_high_time = pm_df.loc[pm_df['high'].idxmax(), 'time']
            # 向量化计算200EMA,判断是否触及
            pm_df['ema200'] = pm_df['close'].ewm(span=200, adjust=False).mean()
            pm_200ema_hit = (pm_df['low'] <= pm_df['ema200']).any()
        
        # 盘后指标
        ah_high = ah_df['high'].max() if len(ah_df) else pd.NA
        
        day_metrics[trade_date] = {
            'PM hi': pm_high,
            'PM hi time': pm_high_time,
            'PM 200 ema hit': pm_200ema_hit,
            'ah_high': ah_high
        }
    return day_metrics

仅这一步优化,通常就能把整体运行速度提升10倍以上。

2. 放弃逐行类实例化+iterrows逻辑,改用表关联+向量化计算

最终输出的字段一半来自日K原始数据,一半来自提前计算好的分钟指标,完全不需要逐行循环生成结果,直接做表关联后批量计算衍生指标即可:

for fname in glob.glob(folder)[:13]:
    ticker = Path(fname).stem
    df_daily = pd.read_csv(fname)
    df_daily['ticker'] = ticker
    
    # 读入分钟线并一次性预处理
    df_min = pd.read_csv(f'intraday_bars_gapped_new/{ticker}.csv')
    min_metrics = preprocess_minute(df_min)
    # 把分钟指标转成DataFrame和日K表关联
    df_min_metrics = pd.DataFrame.from_dict(min_metrics, orient='index').reset_index().rename(columns={'index':'date'})
    df_res = df_daily.merge(df_min_metrics, on='date', how='left')
    
    # 向量化计算所有剩余衍生指标,无循环
    df_res['Spike'] = (df_res['high'] - df_res['open']).abs()
    df_res['after_hours_runner'] = np.where(df_res['ah_high'] > df_res['previous close'], True, False)
    # 对应原有new_gapper、pop_over_10等逻辑,全部用np.where/pandas内置向量化方法实现
    df_res['Pop over 10%'] = np.where(df_res['Spike']/df_res['open'] >= 0.1, True, False)
    
    df_intraday_analysis.append(df_res)

# 最后一次性合并所有结果
final_df = pd.concat(df_intraday_analysis, ignore_index=True)
final_df.to_csv('mikeys-spreadsheet2222.csv', index=False)

这一步替换后,基本可以消除90%以上的循环开销,原来数小时的计算量通常可以压缩到几十秒级别。

3. 附加提速技巧

  • 如果暂时不想完全重构逻辑,至少把iterrows()替换成itertuples(),后者遍历速度是前者的50~100倍,且取值开销更低
  • 读CSV时手动指定字段dtype,比如把价格字段设为float32、成交量设为int32,可以减少一半以上内存占用,同时提升IO和计算速度
  • 如果数据量特别大,可以替换为Polars库做数据处理,其IO和向量化计算速度是pandas的5~20倍,API逻辑和pandas高度相似,迁移成本很低
  • 避免在循环中反复执行pd.concat,如果必须用循环收集结果,优先存为字典列表,最后一次性转为DataFrame,减少内存拷贝次数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 15:48:22