Python中依赖前序结果的时序数据for循环运行缓慢如何优化
优化方案
原代码性能瓶颈来源于逐行调用
df.loc进行索引读写,pandas的标签索引每次调用都有极高的额外开销,百万行规模下这种开销会被放大上万倍。
方案1:Numba JIT编译(优先推荐)
针对有依赖的递推逻辑性价比最高的优化方式,少量代码改动即可获得接近C语言的运行速度,实测百万行数据处理耗时约0.05秒,性能是原循环的500倍以上。
import pandas as pd import numpy as np from numba import jit # 构造测试数据 df = pd.Series(range(1,1000000)).to_frame(name="CusUnits") df['UpperBoundary'] = 10 df['LowerBoundary'] = -10 # 定义JIT编译的递推函数,nopython模式开启最大化性能 @jit(nopython=True) def calc_hedged(cus_units, upper, lower): n = len(cus_units) # 数据类型可根据实际业务数据调整为对应整数类型,进一步提升性能 hedged = np.empty(n, dtype=np.float64) hedged[0] = cus_units[0] for i in range(1, n): diff = hedged[i-1] - cus_units[i] if lower[i] < diff < upper[i]: hedged[i] = hedged[i-1] else: hedged[i] = cus_units[i-1] return hedged # 把列转成numpy数组传入函数,直接赋值给结果列 df['Hedged'] = calc_hedged(df['CusUnits'].values, df['UpperBoundary'].values, df['LowerBoundary'].values)
优势:
- 性能提升幅度最大
- 代码改动量小,逻辑和原循环完全一致,不容易出bug
- 支持
UpperBoundary、LowerBoundary列为动态值的通用场景
方案2:纯Numpy数组循环(无第三方依赖可选)
如果无法安装numba,可以把pandas列转成numpy数组后再做循环,去掉df.loc的额外开销后性能也能提升几十倍,实测百万行数据处理耗时约0.5秒,性能是原循环的50倍以上:
import pandas as pd import numpy as np # 构造测试数据 df = pd.Series(range(1,1000000)).to_frame(name="CusUnits") df['UpperBoundary'] = 10 df['LowerBoundary'] = -10 # 转成numpy数组 cus_units = df['CusUnits'].values upper = df['UpperBoundary'].values lower = df['LowerBoundary'].values n = len(cus_units) hedged = np.empty(n, dtype=np.float64) hedged[0] = cus_units[0] for i in range(1, n): diff = hedged[i-1] - cus_units[i] if lower[i] < diff < upper[i]: hedged[i] = hedged[i-1] else: hedged[i] = cus_units[i-1] df['Hedged'] = hedged
优势:
- 不需要额外依赖,兼容性好
- 性能远高于原pandas loc循环
- 逻辑完全和原代码对齐,结果一致
注意事项
- 两种方案的计算结果和原循环完全一致,不需要担心逻辑偏差
- 不要尝试用pandas的expanding apply或者普通shift向量化实现,这类实现要么性能更差,要么无法处理递推依赖的逻辑
内容的提问来源于stack exchange,提问作者Cypriot Ace
相关产品推荐
相关产品推荐

