Python数据模拟最优工作流:arrays、dictionaries、dataframes性能对比
核心性能瓶颈
你当前运行速度慢的核心原因不是中间存储的数据类型,是多层级频繁执行pandas DataFrame append操作。pandas DataFrame是不可变结构,每次append都会完整复制已有数据生成新对象,嵌套层级多、数据量大的场景下时间复杂度会达到平方级,是性能浪费的核心来源。
两种中间存储方案对比
字典存储:优先推荐
- 实现成本极低:match层直接返回单条记录对应的字典,round层用普通Python列表收集所有match的字典,整轮对局全部跑完后,一次性调用
pd.DataFrame(round_match_list)生成轮次DataFrame,完全避免单局append的复制开销 - 可维护性强:字段对应关系清晰,后续新增/修改返回字段不需要调整其他层级的逻辑,不会出现维度对齐错误,尤其适合存在字符串类字段(如棋手姓名)的场景
- 性能提升明显:Python列表的append操作是均摊O(1)复杂度,比pandas append的速度快两个数量级以上
numpy数组存储:适合纯数值、字段稳定的场景
- 性能上限更高:如果返回字段绝大多数为数值类型(如ELO分值、对局得分、胜负标记等),字符串字段极少甚至没有,按固定顺序用numpy数组存储中间数据,性能会比字典方案高15%~30%
- 缺点是维护成本高:需要提前约定好所有字段的排列顺序,新增/修改字段要同步调整所有层级的取值逻辑,很容易出现字段错位的错误,灵活性远低于字典方案
实操优化建议
- 优先修改所有层级的增量append逻辑:无论最终选哪种中间存储方案,都不要在中间步骤执行DataFrame的append/concat操作,所有中间结果用列表存储,等当前层级的所有子任务全部执行完成后,再一次性生成DataFrame或合并结果,这一步优化就能解决90%以上的性能问题
- 若单场tournament的轮次不多,tournament层可以直接收集各round生成的DataFrame,所有轮次跑完后用
pd.concat(tournament_round_df_list)一次性合并,不需要额外转换中间结构 - simulation层同理,收集所有tournament的结果DataFrame,100场赛事全部跑完后统一用
pd.concat(simulation_tournament_df_list)生成最终全量数据 - 若需要进一步压榨性能,可以提前计算总数据量:总对局数=100场赛事 × 单场赛事轮次数 × 每轮对局数(2^(n-1)),预分配对应长度的DataFrame或numpy数组,直接按索引填充数据,连列表append的开销都能省略,适合数据量极大的场景
内容的提问来源于stack exchange,提问作者ed WSA
相关产品推荐
相关产品推荐

