如何优化Pandas DataFrame的正则匹配迭代逻辑?
优化百万级DataFrame的正则匹配性能
原始问题与代码
当前正则匹配逻辑针对百万行数据效率极低,核心问题是逐行迭代+循环编译正则,未利用pandas的向量化优势。原始实现如下:
构造DataFrame
import pandas as pd import re d = { 'I am a sentence of words.': 'words', 'I am not a sentence of words.': 'words', 'I have no sentence with words or punctuation': 'letter', 'I am not a sentence with a letter or punctuation': 'letter' } df = pd.Series(d).rename_axis('sentence').reset_index(name='mention')
原始匹配逻辑
def get_negated(row): negated = False terms = ['neg', 'negative', 'no', 'free of', 'not', 'without', 'denies', 'ruled out'] for term in terms: regex_str=r"(?:\s+\S+)*\b{0}(?:\s+\S+)*\s+{1}\b".format(term, row.mention) if re.search(regex_str, row['sentence']): negated = True break return int(negated) negated_terms=[] for row in df.itertuples(): negated_terms.append(get_negated(row)) df['negated'] = negated_terms
优化方案
以下方案核心思路是预编译正则+减少循环次数+利用pandas向量化操作,可将性能提升10~100倍:
方案1:按Mention分组+预编译正则
通过分组减少正则编译次数,结合pandas内置的str.contains向量化匹配:
import pandas as pd import re # 处理否定词,转义特殊字符后合并为正则备选组 neg_terms = ['neg', 'negative', 'no', 'free of', 'not', 'without', 'denies', 'ruled out'] neg_pattern = '|'.join(re.escape(term) for term in neg_terms) # 构造基础正则模板 base_regex = r"\b(?:{neg})\b(?:\s+\S+)*\b{mention}\b" # 为每个唯一的mention预编译正则 unique_mentions = df['mention'].unique() compiled_regex = { mention: re.compile(base_regex.format(neg=neg_pattern, mention=re.escape(mention))) for mention in unique_mentions } # 分组应用预编译正则,生成结果列 def check_negation(group): regex = compiled_regex[group.name] return group['sentence'].str.contains(regex).astype(int) df['negated'] = df.groupby('mention')['sentence'].apply(check_negation)
方案2:numpy向量化函数
利用np.vectorize包装预编译的正则匹配逻辑,避免Python级逐行迭代开销:
import numpy as np # 预编译正则(同方案1) neg_terms = ['neg', 'negative', 'no', 'free of', 'not', 'without', 'denies', 'ruled out'] neg_pattern = '|'.join(re.escape(term) for term in neg_terms) unique_mentions = df['mention'].unique() compiled_regex = { mention: re.compile(r"\b(?:{neg})\b(?:\s+\S+)*\b{mention}\b".format(neg=neg_pattern, mention=re.escape(mention))) for mention in unique_mentions } # 定义向量化匹配函数 vectorized_check = np.vectorize(lambda sent, mention: int(compiled_regex[mention].search(sent) is not None)) # 生成结果列 df['negated'] = vectorized_check(df['sentence'].values, df['mention'].values)
方案3:多阶段匹配减少计算量
先通过单一正则过滤候选句子,再针对每个mention做精准匹配,适合mention数量较多的场景:
# 构造包含所有否定词和mention的过滤正则 mention_pattern = '|'.join(re.escape(m) for m in df['mention'].unique()) full_regex = re.compile( r"\b(?:{neg})\b(?:\s+\S+)*\b(?:{mentions})\b".format(neg=neg_pattern, mentions=mention_pattern) ) # 先过滤再精准匹配 def match_mention(row): if full_regex.search(row['sentence']): specific_regex = compiled_regex[row['mention']] return int(specific_regex.search(row['sentence']) is not None) return 0 df['negated'] = df.apply(match_mention, axis=1)
优化效果说明
- 预编译正则:避免每行重复编译正则的开销,这是原始实现最大的性能瓶颈
- 向量化操作:pandas/numpy的底层基于C实现,比Python循环效率提升一个数量级
- 分组/多阶段处理:减少无效计算,进一步降低整体耗时
内容的提问来源于stack exchange,提问作者horcle_buzz
相关产品推荐
相关产品推荐

