Pandas大数据集下字典别名模糊匹配生成新列的性能优化问询
针对Pandas大数据集模糊匹配的优化方案
首先,你的原代码在大数据集下变慢的核心原因是嵌套的Python循环+逐行处理——iterrows()本身效率就很低,再加上每个行要遍历所有客户及其别名,时间复杂度是O(nmk)(n是行数,m是客户数,k是别名数量),数据量上来后肯定会卡顿。下面给你几个实用的优化方案,能大幅提升处理速度:
优化方案一:用预编译正则+Pandas矢量化操作(最推荐)
利用Pandas的矢量化字符串方法(底层是C实现)替代Python循环,同时预编译正则表达式减少重复计算,这是提升效率最明显的方式。
步骤说明:
- 把每个客户的别名集合转换成预编译的正则表达式,用
|连接所有别名,忽略大小写。 - 对每个数据块,统一处理
Business Name列的字符串转换(避免重复操作)。 - 用
str.contains()批量匹配正则,一次性更新符合条件的行,替代逐行赋值。
优化后代码:
import pandas as pd from pathlib import Path import re import os def create_aggregate_names(workbook: str, names: dict, sheet: str) -> None: # 处理输入文件,CSV分块读取,Excel也建议分块(如果文件很大) if '.xlsx' in workbook: # 读取Excel时也可以分块,避免内存溢出 chunks = pd.read_excel(workbook, sheet_name=sheet, chunksize=100000) else: chunks = pd.read_csv(workbook, sep='|', encoding='latin-1', warn_bad_lines=True, error_bad_lines=False, chunksize=100000) path = Path(workbook).parents[0] output_file = f"{path}/data.csv" # 提前构建正则映射:客户名 -> 预编译正则(避免重复编译) regex_mapping = {} for customer, data in names.items(): aliases = data["aliases"] # 转义别名中的特殊字符,避免正则语法冲突,然后用|连接 pattern = '|'.join(re.escape(alias.lower()) for alias in aliases) regex_mapping[customer] = re.compile(pattern, re.IGNORECASE) # 处理每个数据块 for idx, chunk in enumerate(chunks): # 初始化新列,默认值为原Business Name if "Aggregate Business Name" not in chunk.columns: chunk["Aggregate Business Name"] = chunk["Business Name"].astype(str) # 统一转换为小写字符串,避免重复操作 business_names = chunk["Business Name"].astype(str).str.lower() # 批量匹配每个客户的正则,更新对应行 for customer, regex in regex_mapping.items(): # 找到匹配的行,批量赋值 mask = business_names.str.contains(regex) chunk.loc[mask, "Aggregate Business Name"] = customer # 写入CSV:第一次写入加表头,后续追加不加 chunk.to_csv(output_file, sep='|', index=False, mode='a', header=not os.path.exists(output_file) or idx == 0)
优化方案二:用Dask处理超大规模数据集(内存不足时用)
如果你的数据集大到Pandas无法一次性加载到内存(比如数亿行),可以用Dask DataFrame——它支持并行分块处理,语法和Pandas几乎一致,能充分利用多核CPU。
示例代码:
import dask.dataframe as dd from pathlib import Path import re def create_aggregate_names_dask(workbook: str, names: dict, sheet: str) -> None: path = Path(workbook).parents[0] output_file = f"{path}/data.csv" # 用Dask读取文件,自动分块 if '.xlsx' in workbook: df = dd.read_excel(workbook, sheet_name=sheet) else: df = dd.read_csv(workbook, sep='|', encoding='latin-1', warn_bad_lines=True, error_bad_lines=False) # 构建正则映射 regex_mapping = {} for customer, data in names.items(): aliases = data["aliases"] pattern = '|'.join(re.escape(alias.lower()) for alias in aliases) regex_mapping[customer] = re.compile(pattern, re.IGNORECASE) # 定义匹配函数,Dask会并行应用到每个分块 def map_aggregate_name(name): name_str = str(name).lower() for customer, regex in regex_mapping.items(): if regex.search(name_str): return customer return str(name) # 生成新列,指定数据类型 df["Aggregate Business Name"] = df["Business Name"].apply( map_aggregate_name, meta=('Aggregate Business Name', 'str') ) # 保存为单一CSV文件 df.to_csv(output_file, sep='|', index=False, single_file=True)
关键优化点总结
- 避免
iterrows():Pandas的矢量化方法(如str.contains)比Python循环快10~100倍,底层是C实现,没有Python循环的 overhead。 - 预编译正则:正则表达式只编译一次,避免每次匹配都重新解析语法。
- 减少重复字符串操作:提前把
Business Name转成小写,避免在循环中重复转换。 - 批量赋值:用
loc[mask]替代at[index],一次性更新所有匹配行,效率更高。 - 处理表头重复:写入CSV时判断是否需要添加表头,避免输出文件重复表头。
内容的提问来源于stack exchange,提问作者shickey
相关产品推荐
相关产品推荐

