Python pandas数据框行快速赋值方法 仿真运行优化问询
核心性能瓶颈定位
当前的性能损耗核心来自逐行调用df.loc[len(df)]追加数据,这类操作在pandas中时间复杂度为O(n²),每次追加都会触发全量内存重分配与数据拷贝,是远高于仿真逻辑本身的最大开销来源。
最优行赋值实现方式
仿真过程中完全不要实时操作DataFrame,遵循以下流程即可获得最高写入速度:
- 用Python原生列表存储单条订单的字段值(tuple或dict格式均可),利用列表O(1)摊销的append开销攒全量数据
- 所有仿真跑完后,一次性将列表转换为DataFrame
该方式比逐行loc追加快10~100倍,是pandas批量生成数据的通用最优实践。
全流程速度优化点
1. 随机采样逻辑优化
当前每次采样都执行随机数生成+df.iloc读取,存在大量pandas层的冗余校验开销,优化方向:
- 仿真开始前,将
shipments、daily_arrivals中需要的字段提取为numpy数组,彻底绕开pandas的列名匹配、索引检查逻辑 - 若仿真规模较大,可一次性预抽取全量仿真需要的样本值,仿真循环内直接按顺序读取,省去循环内的随机数生成开销
示例初始化代码:
# 仿真启动前执行一次即可 sample_pool = shipments[['SHIP TO COUNTRY','SEGMENT','TRANSIT TIME','OTM_WEIGHT','TOTAL_TRANSPORT_COST','CARRIER']].to_numpy() sample_pool[:,4] = np.round(sample_pool[:,4], 2) # 统一完成成本字段的四舍五入 arrival_pool = daily_arrivals['DELIVERY DATE'].to_numpy()
2. 冗余逻辑消除
- 废弃全局变量传参的写法:Python中int/float为不可变对象,原代码在仿真进程内修改
balance/dispatch/orders实际仅修改局部副本,无法传回外层(属于隐藏bug)。等所有订单数据攒为列表后,直接通过DataFrame聚合计算总费用、总重量、总订单数,既规避bug又省掉循环内的累加开销 - 方案2中逐日新建SimPy环境的逻辑完全冗余,SimPy环境支持连续运行21天的业务逻辑,无需拆分21次初始化环境,可减少70%以上的仿真框架调度开销
- 去掉循环内重复的字段处理逻辑,将四舍五入、类型转换等操作提前到初始化阶段完成
对齐原逻辑的优化后代码
import random import simpy import numpy as np import pandas as pd # 仿真初始化:静态数据池预加载 sample_pool = shipments[['SHIP TO COUNTRY','SEGMENT','TRANSIT TIME','OTM_WEIGHT','TOTAL_TRANSPORT_COST','CARRIER']].to_numpy() sample_pool[:,4] = np.round(sample_pool[:,4], 2) arrival_pool = daily_arrivals['DELIVERY DATE'].to_numpy() # 全局存储容器 all_order_records = [] def LC_run(env, order_records, sim_run_idx, sample_idx): while True: arrival = arrival_pool[random.randint(0, len(arrival_pool)-1)] pickup = env.now for m in range(arrival): # 直接读取numpy数组,无pandas开销 country, segment, trans_time, weight, cost, carrier = sample_pool[sample_idx[0]] sample_idx[0] += 1 # 直接append到原生列表,无DataFrame内存拷贝开销 order_records.append([ f'order #{m}', country, segment, pickup, pickup+trans_time, trans_time, weight, cost, carrier, sim_run_idx ]) yield env.timeout(1) for j in range(100): # 用可变列表存储样本索引,规避不可变对象传参问题 sample_idx = [0] # 按预估单轮订单量预生成随机样本索引,省循环内随机数开销 est_orders = 21 * int(np.mean(arrival_pool)) run_sample_idx = np.random.randint(0, len(sample_pool), size=int(est_orders*1.2)) run_sample_pool = sample_pool[run_sample_idx] env = simpy.Environment() env.process(LC_run(env, all_order_records, j, sample_idx)) env.run(until=21) # 全量仿真结束后,一次性生成订单明细DataFrame df = pd.DataFrame(all_order_records, columns=[ 'Order #','SHIP TO COUNTRY','SEGMENT','PICK UP DATE','DELIVERY DATE', 'TRANSIT TIME','OTM_WEIGHT', 'TRANSPORTATION COST','CARRIER','SIM_RUN' ]) # 一次性聚合计算统计量 pdf = df.groupby('SIM_RUN').agg( TOTAL_TRANSPORTATION_COST=('TRANSPORTATION COST','sum'), TOTAL_WEIGHT=('OTM_WEIGHT','sum'), TOTAL_NUMBER_OF_ORDERS=('Order #','count') ).reset_index()
性能参考
按照上述优化,100次21天仿真的运行速度通常可从原逻辑的几十分钟缩短至数秒,核心提升来自:
- 消除逐行DataFrame追加的O(n²)内存拷贝开销
- 将pandas层的样本读取替换为numpy数组直读
- 去掉SimPy环境的重复初始化开销
- 消除循环内的重复计算与冗余逻辑
内容的提问来源于stack exchange,提问作者nesquess
相关产品推荐
相关产品推荐

