如何将pandas iterrows循环改为向量化操作提升DataFrame性能
问题背景
已实例化全局作用域的报表DataFrame report_df,该对象派生自df_a,索引与df_a完全对齐,用于存储df_a和df_b两个DataFrame的对比结果。
当前对比逻辑通过iterrows逐行循环调度:
for idx, row in df_a.iterrows(): compare(df_b, row, df_a_col, df_b_col, idx)
其中compare函数实现如下:
def compare(df_b, row, df_a_col, df_b_col, idx): matching_rows = df_b[df_b[df_b_col].astype(str) == str(row[df_a_col])] if len(matching_rows) > 0: # 仅取第一条匹配的行 matching_row = matching_rows.head(1) # 标记当前行匹配成功 report_df.loc[idx, "match"] = True # 仅当match_how为unmatched时更新匹配索引 if report_df.at[report_df.index[idx], 'match_how'] == 'unmatched': report_df.loc[idx, "match_index"] = matching_row.index[0]
随着数据规模增长,逐行循环性能下降明显,需要改造为向量化流程提升运行效率。
解决方案
完全可以通过pandas内置的向量化操作替换逐行循环,核心是用C层级实现的哈希连接替代Python层逐行遍历,性能通常可以提升10~100倍,数据量越大优势越显著。
逻辑对齐说明
改造后的逻辑和原逐行实现100%对齐,核心规则保持一致:
- 匹配时统一将两表的对比列转为字符串后做相等判断
- 每一行
df_a仅取df_b中第一条匹配的记录 - 仅对匹配成功的行标记
match=True,未匹配行保留原有字段值 - 仅当
report_df对应行的match_how值为unmatched时,才写入df_b匹配行的索引
实现代码
# 1. 预处理两表的匹配键,统一转为字符串避免类型误差 df_a_match_key = df_a[df_a_col].astype(str) df_b_match_key = df_b[df_b_col].astype(str) # 2. 构造临时表:df_b提前去重,每个匹配键仅保留第一条出现的记录,减少计算量 df_b_tmp = df_b.assign( __b_idx=df_b.index, __match_key=df_b_match_key ).drop_duplicates(subset="__match_key", keep="first") # 3. 构造df_a临时表,绑定自身索引用于后续和report_df对齐 df_a_tmp = df_a.assign( __a_idx=df_a.index, __match_key=df_a_match_key ) # 4. 左连接完成批量匹配,保留df_a所有行 match_res = df_a_tmp.merge( df_b_tmp[["__match_key", "__b_idx"]], on="__match_key", how="left" ).set_index("__a_idx") # 5. 批量更新report_df字段 # 筛选出匹配成功的行 has_match_mask = match_res["__b_idx"].notna() # 标记匹配成功状态 report_df.loc[has_match_mask, "match"] = True # 筛选出需要更新match_index的行:匹配成功 且 match_how为unmatched need_update_idx_mask = has_match_mask & (report_df["match_how"] == "unmatched") # 批量写入匹配到的df_b索引 report_df.loc[need_update_idx_mask, "match_index"] = match_res.loc[need_update_idx_mask, "__b_idx"]
性能说明
原iterrows逐行循环属于Python层解释执行,每次循环都要对整个df_b做布尔筛选,时间复杂度为O(n*m)(n为df_a行数,m为df_b行数)。
上述向量化实现基于pandas内部优化的哈希连接逻辑,时间复杂度为O(n+m),百万行级数据集通常可以从几十分钟的运行时间压缩到数秒。如果内存足够,还可以通过将两表匹配键转为category类型进一步压缩内存、提升速度。
内容的提问来源于stack exchange,提问作者Jordan
相关产品推荐
相关产品推荐

