循环序列优化:Python下二进制串匹配Pandas DataFrame的性能问题求解
优化方案
原代码性能问题核心
两层嵌套循环+iterrows遍历的时间复杂度为O(M*N)(M、N分别为短串列表和DataFrame的长度),15万条数据的乘积运算量极高,且iterrows本身遍历DataFrame的效率极低,是耗时过长的根本原因。
优化思路
核心是把前缀匹配逻辑从嵌套遍历改为预构建映射表+O(1)查询,将复杂度降到线性级别,具体实现步骤如下:
步骤1:预计算DataFrame的权重字段
提前把每条数据对应的权重、yes/no分类权重计算好,避免循环中重复计算:
import pandas as pd import numpy as np from collections import defaultdict # 预计算单条数据的权重,以及对应yes/no的权重值 df['weight'] = 2 ** (32 - df['prefix'].astype(int)) df['yes_weight'] = np.where(df['value'] == 'yes', df['weight'], 0) df['no_weight'] = np.where(df['value'] == 'no', df['weight'], 0)
步骤2:按短串长度分组,预构建前缀权重映射表
相同长度的短串匹配逻辑一致,我们按短串长度分组后,为每个长度提前统计好所有前缀对应的总yes权重、总no权重,存储为字典:
# 按长度对短串分组,key为短串长度,value为对应长度的短串列表 len_to_binaries = defaultdict(list) for bin_str in list_of_binary_numbers: str_len = len(bin_str) len_to_binaries[str_len].append(bin_str) # 为每个长度构建前缀-权重映射表 len_to_prefix_map = {} for str_len in len_to_binaries: # 截取df中binary字段前str_len位作为匹配前缀 df['temp_prefix'] = df['binary'].str[:str_len] # 分组统计每个前缀对应的总yes、no权重,转成字典方便查询 stat = df.groupby('temp_prefix').agg( sum_yes=('yes_weight', 'sum'), sum_no=('no_weight', 'sum') ).reset_index() len_to_prefix_map[str_len] = dict(zip(stat['temp_prefix'], zip(stat['sum_yes'], stat['sum_no'])))
步骤3:批量查询得到最终结果
遍历短串列表,直接查预构建的映射表得到结果,完全不需要嵌套遍历DataFrame:
list1 = [] list2 = [] for bin_str in list_of_binary_numbers: str_len = len(bin_str) # 查映射表,没有匹配结果默认返回(0,0) sum_yes, sum_no = len_to_prefix_map[str_len].get(bin_str, (0, 0)) list1.append(sum_yes) list2.append(sum_no)
可选进阶优化
如果需要进一步提速,可以做如下改造:
- 由于示例中df的binary字段都是固定32位字符串,可以将二进制串转成整数存储,前缀匹配用位运算代替字符串截取,性能还能提升3~5倍
- 如果短串长度种类极少,可直接用pandas矢量化操作一次性处理所有短串,完全省略短串遍历的循环
性能表现
改造后代码在常规配置的机器上,15万条数据的处理时间可以从原来的几十小时降到10秒以内。
内容的提问来源于stack exchange,提问作者Denver Dang
相关产品推荐
相关产品推荐

