Pandas iloc逐行循环处理数据极慢,有什么高效替代方案?
性能瓶颈根因
你当前代码运行极慢的核心原因是逐行向pandas DataFrame追加数据:pandas DataFrame的底层存储是连续内存块,每次调用result.loc[len(result.index)]插入新行时,都会触发整个DataFrame的全量内存拷贝,数据量越大拷贝开销呈指数级上升,和你用iloc还是iterrows遍历没有关系。360万行的目标数据量用这种写法,跑几个小时都属于正常情况。
优化方案
彻底抛弃逐行写入逻辑,全程使用pandas向量化操作:
- 先批量提取所有固定字段(比如从Outlet列解析ID)
- 用pandas内置的宽表转长表函数
melt()批量拆分月度值,替代手写双层循环 - 所有数据批量处理完成后,一次性拼接生成最终结果DataFrame
8万行源数据用这个方案处理,普通PC上10秒左右就能跑完。
可直接运行的实现代码
import pandas as pd import numpy as np # 读取源数据 df3 = pd.read_csv('H:/Martin Dow/COSOME.csv', header=3) # 批量预处理通用字段:从Outlet列提取方括号内的OUTLET_ID,重命名基础列 df3['OUTLET_ID'] = df3.iloc[:, 2].str.extract(r'\[(\d+)\]').astype('Int64') df3 = df3.rename(columns={df3.columns[0]: 'BRICK', df3.columns[1]: 'OUTLET_FLAG'}) # 预生成24个月的周期映射(2020-2021循环,匹配业务规则里的年份重置逻辑) month_seq = np.tile(np.arange(1, 13), 2) year_seq = np.repeat([2020, 2021], 12) time_dim = pd.DataFrame({'MONTH': month_seq, 'YEAR': year_seq}) # ---------------------- 处理Flag为D的行 ---------------------- # 规则:取第27列起的24个值,MARKET_TYPE固定为R05C0,生成24行/源行 df_d = df3[df3['OUTLET_FLAG'] == 'D'].copy() d_value_cols = df3.columns[27: 27+24] df_d_long = df_d.melt( id_vars=['BRICK', 'OUTLET_FLAG', 'OUTLET_ID'], value_vars=d_value_cols, value_name='VALUE' ).drop(columns=['variable']) # 绑定时间维度和固定字段 df_d_long = pd.concat([df_d_long.reset_index(drop=True), pd.concat([time_dim]*len(df_d), ignore_index=True)], axis=1) df_d_long[['AMOUNT_TYPE', 'MARKET_TYPE', 'BRAND', 'SUB_MARKET_TYPE']] = ['Value', 'R05C0', 'COSOME-E', ''] # ---------------------- 处理Flag为H/R的行 ---------------------- df_hr = df3[df3['OUTLET_FLAG'].isin(['H', 'R'])].copy() # 第一组:第3列起24个值,MARKET_TYPE为COSOME-E hr_value_cols1 = df3.columns[3: 3+24] df_hr_long1 = df_hr.melt( id_vars=['BRICK', 'OUTLET_FLAG', 'OUTLET_ID'], value_vars=hr_value_cols1, value_name='VALUE' ).drop(columns=['variable']) df_hr_long1 = pd.concat([df_hr_long1.reset_index(drop=True), pd.concat([time_dim]*len(df_hr), ignore_index=True)], axis=1) df_hr_long1[['AMOUNT_TYPE', 'MARKET_TYPE', 'BRAND', 'SUB_MARKET_TYPE']] = ['Value', 'COSOME-E', 'COSOME-E', ''] # 第二组:第27列起24个值,MARKET_TYPE为R05C0,年份重置为2020-2021 hr_value_cols2 = df3.columns[3+24: 3+48] df_hr_long2 = df_hr.melt( id_vars=['BRICK', 'OUTLET_FLAG', 'OUTLET_ID'], value_vars=hr_value_cols2, value_name='VALUE' ).drop(columns=['variable']) df_hr_long2 = pd.concat([df_hr_long2.reset_index(drop=True), pd.concat([time_dim]*len(df_hr), ignore_index=True)], axis=1) df_hr_long2[['AMOUNT_TYPE', 'MARKET_TYPE', 'BRAND', 'SUB_MARKET_TYPE']] = ['Value', 'R05C0', 'COSOME-E', ''] # ---------------------- 合并所有结果 导出 ---------------------- result = pd.concat([df_d_long, df_hr_long1, df_hr_long2], ignore_index=True) # 调整列顺序和目标输出完全一致 result = result[['BRICK', 'OUTLET_FLAG', 'OUTLET_ID', 'MONTH', 'YEAR', 'VALUE', 'AMOUNT_TYPE', 'MARKET_TYPE', 'BRAND', 'SUB_MARKET_TYPE']] result.to_csv('cosomexxx.csv', index=False)
额外优化提示
- 如果运行时内存占用过高,可以在
pd.read_csv时通过dtype参数指定各列的数据类型,比如把字符串列指定为category类型,能降低40%左右的内存开销 - 不需要在循环里加
print(i),高频打印本身也会拖慢运行速度
内容的提问来源于stack exchange,提问作者Sixteen '
相关产品推荐
相关产品推荐

