如何优化800万次DataFrame循环迭代的执行性能?
优化方案:从24小时到8小时以内的核心改进
核心瓶颈分析
你的代码有两个致命性能问题:
df_original_rel.query()线性扫描:每次查询都要遍历整个不断增长的DataFrame,随着数据量变大,单次查询耗时从毫秒级飙升到秒级,800万次循环下来总时间指数级增长。df.append()频繁创建新对象:每次追加行都会生成新的DataFrame,底层是内存拷贝,同样是O(n)开销,累计起来占了大量时间。
针对性优化代码
直接替换原循环逻辑,用集合存已有对+列表收新行+快速采样的组合:
import pandas as pd import random import numpy as np # 提前准备:把已有的(FROM, TO)对转成集合,实现O(1)查询 existing_pairs = set(zip(df_original_rel['FROM'], df_original_rel['TO'])) # 用列表收集新行,避免频繁DataFrame操作 new_rows = [] # 把UID转成numpy数组,加快随机选取速度 uid_array = df_original_nodes['UID'].values # 预定义迭代次数 n_iter = 8000000 for _ in range(n_iter): # 随机选关系类型 ran_rel_type = rel_type[random.randint(0, 1)] # 直接从数组里随机选2个不重复的UID,比df.sample()更快 FROM, TO = np.random.choice(uid_array, size=2, replace=False) # 集合查询,常数时间完成判断 if (FROM, TO) not in existing_pairs: existing_pairs.add((FROM, TO)) new_rows.append({ "FROM": FROM, "TO": TO, "TYPE": ran_rel_type[0], "PART_OF": ran_rel_type[1] }) # 最后一次性合并新行到原DataFrame df_new_rel = pd.DataFrame(new_rows) df_original_rel = pd.concat([df_original_rel, df_new_rel], ignore_index=True) # 写入CSV df_original_rel.to_csv("output/extra_rel.csv", encoding="utf-8", index=False)
优化点说明
- 集合存储已有对:集合的成员查询是常数时间,不管
df_original_rel涨多大,每次判断是否存在都几乎不花时间,彻底解决查询瓶颈。 - 列表收集新行:列表追加是O(1)操作,800万次追加的开销可以忽略,最后一次性转DataFrame比每次
append快几个数量级。 - numpy数组采样:
np.random.choice比df.sample()更底层,避免了DataFrame采样的额外封装开销,进一步压缩单次循环时间。
进阶批量优化(可选)
如果内存足够,完全跳过循环,用批量生成的方式处理,速度会更快:
import pandas as pd import numpy as np existing_pairs = set(zip(df_original_rel['FROM'], df_original_rel['TO'])) uid_array = df_original_nodes['UID'].values n_iter = 8000000 # 批量生成所有随机对 indices = np.random.randint(0, len(uid_array), size=(n_iter, 2)) # 过滤掉同组内重复的UID indices = indices[indices[:, 0] != indices[:, 1]] # 转成元组集合去重 all_pairs = set(tuple(p) for p in uid_array[indices]) # 筛选出不存在的新对 new_pairs = all_pairs - existing_pairs # 批量生成关系类型 rel_indices = np.random.randint(0, len(rel_type), size=len(new_pairs)) # 构造新数据 new_data = { "FROM": [p[0] for p in new_pairs], "TO": [p[1] for p in new_pairs], "TYPE": [rel_type[i][0] for i in rel_indices], "PART_OF": [rel_type[i][1] for i in rel_indices] } # 合并并写入 df_original_rel = pd.concat([df_original_rel, pd.DataFrame(new_data)], ignore_index=True) df_original_rel.to_csv("output/extra_rel.csv", encoding="utf-8", index=False)
这种方式把循环开销降到0,适合内存充足的场景,速度能再提升30%-50%。
内容的提问来源于stack exchange,提问作者Viki
相关产品推荐
相关产品推荐

