为何不应遍历Pandas DataFrame行?特定场景优化方案咨询
关于pandas中iterrows()的问题解答
为什么不推荐用iterrows()?
除了大家熟知的效率问题,还有两个关键问题:
- 效率瓶颈:pandas的向量化操作基于numpy的C级循环,比Python级别的iterrows()快几十到上百倍,数据量越大差距越明显。
- 类型自动转换:iterrows()返回的是Series对象,会自动转换数据类型。比如原DataFrame里的
datetime64类型会变成Python原生datetime对象,带缺失值的整数列会被转成float类型,后续处理容易出现类型不兼容的问题。 - 无法直接修改原数据:iterrows()返回的是行的副本而非视图,修改循环里拿到的Series不会影响原DataFrame,必须通过索引定位修改(比如
df.loc[idx, col]),这又会额外增加开销。
你的场景是否只能用遍历?
不是必须,但要看数据规模:
- 如果你的DataFrame只有几千行,用iterrows()或者itertuples()完全没问题,不会有明显性能问题;
- 如果数据量上万甚至更多,就需要做优化,不能硬用iterrows()。
修改原始数据会有问题吗?
直接修改循环里的行对象(比如row['col'] = value)是无效的,因为那是副本。如果要修改原DataFrame,要么用df.loc[idx, col] = value,但这种方式每次都会触发索引查找,效率极低;要么先把所有处理结果收集到列表,最后一次性赋值给新列,这是更推荐的方式。
针对你的场景的优化方案
虽然涉及文件IO和条件判断,但还是有不少优化空间:
用itertuples()替代iterrows()
itertuples()返回命名元组,比Series更轻量,速度更快,而且不会自动转换数据类型,能保留原DataFrame的类型。示例代码:results = [] for row in df.itertuples(index=False): # 从行中获取日期时间 target_dt = row.datetime_column # 匹配对应的grib文件(建议预先构建映射) grib_path = grib_file_map[target_dt] # 处理grib文件 with pygrib.open(grib_path) as grb: msg = grb.select(shortName='2t')[0] data = msg.values # 执行条件判断 if data.mean() > threshold: processed_val = data * 1.2 else: processed_val = data * 0.8 results.append(processed_val.mean()) # 一次性赋值给DataFrame df['processed_result'] = results预先构建文件映射
提前把所有grib文件的时间信息和路径存成字典,避免每次循环都搜索文件:import glob import pygrib grib_file_map = {} # 遍历所有grib文件 for file_path in glob.glob('./data/*.grib'): with pygrib.open(file_path) as grb: # 获取文件对应的时间(根据实际情况调整) valid_dt = grb.message(1).validDate grib_file_map[valid_dt] = file_path并行处理
如果文件IO或数据处理是瓶颈,用多进程/多线程并行处理,大幅缩短总耗时:from concurrent.futures import ProcessPoolExecutor def process_single_row(row): target_dt = row.datetime_column grib_path = grib_file_map[target_dt] with pygrib.open(grib_path) as grb: msg = grb.select(shortName='2t')[0] data = msg.values if data.mean() > threshold: return data.mean() * 1.2 else: return data.mean() * 0.8 # 把行转成可迭代对象 row_list = list(df.itertuples(index=False)) # 并行处理 with ProcessPoolExecutor(max_workers=4) as executor: results = list(executor.map(process_single_row, row_list)) df['processed_result'] = results避免循环内修改DataFrame
不管用哪种遍历方式,都先把结果收集到列表,最后一次性赋值给新列,这比循环内每次用loc修改快得多,也能避免索引错误。
内容的提问来源于stack exchange,提问作者Feva
相关产品推荐
相关产品推荐

