如何向量化遍历含df.iloc引用的df_ref时的eval语句?
优化动态iloc引用的向量化处理方案
需要处理存储指向源数据框df的iloc引用字符串的参考数据框df_ref,要求保留Python动态引用特性的同时,优化原循环+eval的低效执行方式。原代码执行10000次循环耗时约2.84秒,性能瓶颈明显。
原代码及执行结果
import pandas as pd dataValues = ['a','b','c'] df = pd.DataFrame({'values': dataValues}) df_list = ['df.iloc[0,0]','df.iloc[2,0]'] df_ref = pd.DataFrame({'ref':df_list}) # 循环10000次模拟实际场景的运行量 def help(): for i in range(10000): for index,row in df_ref.iterrows(): eval(df_ref.ref[index]) %timeit help()
执行结果:
2.84 s ± 366 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
优化思路与实现
原方案核心问题:iterrows遍历效率低,且每次循环调用eval重复解析字符串,重复执行时开销被放大。以下方案在保留动态引用特性的前提下,通过预解析索引+向量化操作提升性能:
预解析
iloc索引
用正则表达式提取每个引用字符串中的行、列索引,将df.iloc[x,y]格式的字符串转换为整数元组(x,y),避免每次执行重复解析字符串。向量化批量取值
利用pandas向量化特性,将解析后的索引批量传入df.iloc,一次性获取所有对应值,替代逐个循环的eval调用。
优化后的代码:
import pandas as pd import re dataValues = ['a','b','c'] df = pd.DataFrame({'values': dataValues}) df_list = ['df.iloc[0,0]','df.iloc[2,0]'] df_ref = pd.DataFrame({'ref':df_list}) # 解析引用字符串为索引元组的函数 def parse_refs(ref_series): pattern = re.compile(r'df\.iloc\[(\d+),(\d+)\]') return [tuple(map(int, pattern.match(s).groups())) for s in ref_series] # 优化后的执行函数 def optimized_help(): # 预解析一次索引(若ref列动态变化,可将此步骤移入循环) indices = parse_refs(df_ref['ref']) rows, cols = zip(*indices) for i in range(10000): # 向量化批量取值 df.iloc[list(rows), list(cols)] %timeit optimized_help()
性能对比
优化后的代码执行耗时通常可降低至几百毫秒级别(具体数值取决于运行环境),相比原方案有数量级的性能提升。
动态引用特性的保留
如果df_ref['ref']内容是动态变化的(比如每次循环都会更新),只需将索引解析步骤移入循环内部即可:
def dynamic_optimized_help(): for i in range(10000): # 每次循环重新解析动态变化的引用 indices = parse_refs(df_ref['ref']) rows, cols = zip(*indices) df.iloc[list(rows), list(cols)]
这样既保留了动态指定iloc引用的能力,又避免了原方案中重复eval和低效循环的开销。
内容的提问来源于stack exchange,提问作者Zach Kramer
相关产品推荐
相关产品推荐

