Python循环执行过慢问题求助(附完整代码)
优化Python循环性能:处理23000行CSV的高效方案
嘿,我看了你这段代码,慢的根源其实很明确——你在循环里反复调用DataFrame.append(),这玩意儿每次都会创建一个全新的DataFrame对象,23000次下来内存开销和时间成本简直爆炸!咱们来一步步优化,让速度直接起飞:
核心优化点1:彻底抛弃循环中的DataFrame.append()
append()是性能杀手,尤其是在大规模循环里。我们改用列表收集数据,最后一次性转成DataFrame,这能把速度提升几十倍甚至上百倍。
优化后的目标循环代码
# 先准备空列表收集所有数据字典 data_list = [] for i in range(A_size): # 这里建议用.iloc[i]来定位行,比直接用[i]更稳定 lat = A['Latitude'].iloc[i] lon = A['Longitude'].iloc[i] x1 = float(fn_CalcParcelID(lat, ParcelSize)) / 100000 x2 = float(fn_CalcParcelID(lon, ParcelSize)) / 100000 data_list.append({ 'Lat': x1, 'Lon': x2, 'PSC': A['PSC'].iloc[i], 'UARFCN': UARFCN, 'SC_Avg_EcNo': A['EcNo'].iloc[i], 'SC_Avg_RSCP': A['RSCP'].iloc[i] }) # 最后一次性生成DataFrame,只创建一次对象 B1 = pd.DataFrame(data_list) B1.to_csv('B1.csv', index=False) # index=False可以减少文件体积和写入时间
核心优化点2:矢量化处理ParcelID计算(彻底干掉循环)
既然用了pandas,就要发挥它的矢量化优势!把fn_CalcParcelID改成支持整个Series的函数,完全不用逐行循环:
矢量化版本的ParcelID函数
import numpy as np def fn_CalcParcelID_vec(Pos_series, ParcelUnitSize): # 用numpy.where实现矢量化条件判断,比逐行快N倍 result = np.where( Pos_series == 500, 50000000, np.where( Pos_series < 0, (Pos_series * 100000).astype(int) - ParcelUnitSize + ((Pos_series * 100000).astype(int) % ParcelUnitSize), (Pos_series * 100000).astype(int) - ((Pos_series * 100000).astype(int) % ParcelUnitSize) ) ) return result.astype(int) # 直接对整个列计算,完全不需要循环! A['Parcel_Lat'] = fn_CalcParcelID_vec(A['Latitude'], ParcelSize) / 100000 A['Parcel_Lon'] = fn_CalcParcelID_vec(A['Longitude'], ParcelSize) / 100000 # 直接构造B1,一行代码搞定 B1 = pd.DataFrame({ 'Lat': A['Parcel_Lat'], 'Lon': A['Parcel_Lon'], 'PSC': A['PSC'], 'UARFCN': UARFCN, 'SC_Avg_EcNo': A['EcNo'], 'SC_Avg_RSCP': A['RSCP'] }) B1.to_csv('B1.csv', index=False)
这个版本的速度会比循环快几个数量级,因为numpy的矢量化操作是用C语言实现的,远快于Python层面的逐行循环。
额外优化:拆分Top1-Top9的代码也可以提速
你前面拆分A1到A9的循环同样存在append的性能问题,用pandas的melt和pivot可以彻底替代手动循环:
# 把宽表转成窄表,自动拆分所有Top1-Top9的数据 id_vars = ['Latitude', 'Longitude'] # 定义需要转换的列名模板 value_vars = [] for n in range(1, 10): value_vars.extend([ f'PSC: Top #{n} (UARFCN #01)', f'Sc Aggr Ec/Io (dB): Top #{n} (UARFCN #01)', f'Sc Aggr Ec (dBm): Top #{n} (UARFCN #01)' ]) # 用melt展开数据 melted = scanner.melt(id_vars=id_vars, value_vars=value_vars, var_name='Metric', value_name='Value') # 提取Top编号和指标类型 melted['Top'] = melted['Metric'].str.extract(r'Top #(\d+)') melted['Metric_Type'] = melted['Metric'].str.replace(r': Top #\d+ \(UARFCN #01\)', '', regex=True) melted['Metric_Type'] = melted['Metric_Type'].map({ 'PSC': 'PSC', 'Sc Aggr Ec/Io (dB)': 'EcNo', 'Sc Aggr Ec (dBm)': 'RSCP' }) # 透视回宽表,得到每个样本的PSC/EcNo/RSCP pivoted = melted.pivot_table( index=['Latitude', 'Longitude', 'Top'], columns='Metric_Type', values='Value', aggfunc='first' ).reset_index() # 过滤掉PSC为-1的行 pivoted = pivoted[pivoted['PSC'] != -1] # 合并所有Top数据,去重 A = pivoted.drop('Top', axis=1) A = A[~A.apply(frozenset, axis=1).duplicated()] A.to_csv('table_data_pilot.csv', index=False)
总结优化思路
- 避免循环中修改DataFrame:
append/concat在循环里会不断复制数据,改用列表或矢量化操作 - 优先用矢量化函数:pandas/numpy的内置函数都是C级别的速度,远快于Python循环
- 宽表转窄表用melt/pivot:替代手动拆分列的循环,代码更简洁且高效
内容的提问来源于stack exchange,提问作者Heba R
相关产品推荐
相关产品推荐

