Pandas大型DataFrame字符串匹配提速:转Cython遇阻求方案
高效解决大数据集字符串部分匹配问题
一、优先选择:线性时间复杂度的多模式匹配方案
你的原方案性能瓶颈在于逐行遍历+重复扫描df_b,650万次扫描120万行的数据集,时间复杂度是O(M*N),完全不可行。推荐用Aho-Corasick多模式匹配算法,时间复杂度降到O(M+N),处理百万级数据毫无压力。
实现步骤(用pyahocorasick库)
import ahocorasick import pandas as pd # 1. 预处理:去重减少计算量 unique_partials = df_a['partial_string'].unique() unique_combined = df_b['combined_string'].unique() # 2. 构建Aho-Corasick自动机,加载所有待匹配的子串 automaton = ahocorasick.Automaton() for partial in unique_partials: automaton.add_word(partial, partial) automaton.make_automaton() # 3. 建立子串到匹配结果的映射 partial_to_matches = {p: set() for p in unique_partials} for combined in unique_combined: # 找出当前字符串中包含的所有子串 for _, partial in automaton.iter(combined): partial_to_matches[partial].add(combined) # 4. 生成最终结果 def get_ref(partial): matches = partial_to_matches.get(partial, set()) if not matches: return 'None' return matches.pop() if len(matches) == 1 else 'Array' df_a['reference'] = df_a['partial_string'].map(get_ref)
二、备选方案:矢量化正则匹配(适合子串数量较少的场景)
如果partial_string的唯一值不多(比如≤10万),可以用正则批量匹配:
import pandas as pd # 生成匹配所有子串的正则表达式 pattern = '|'.join(df_a['partial_string'].unique()) # 提取所有匹配关系 matches = df_b['combined_string'].str.extractall(f'({pattern})').reset_index() # 分组构建子串到匹配结果的映射 match_map = matches.groupby(0)[0].agg( lambda x: set(x.index.get_level_values(0).map(df_b['combined_string'])) ).to_dict() # 生成结果 df_a['reference'] = df_a['partial_string'].map( lambda p: match_map.get(p, set()) and (next(iter(match_map[p])) if len(match_map[p])==1 else 'Array') or 'None' )
三、Cython方案修正(不推荐,仅满足你的需求)
你的Cython代码存在语法错误和性能设计问题,修正后的版本如下:
%%cython -c=-fopenmp -l=-fopenmp cimport numpy as np import numpy as np from cython.parallel import prange def process_matches(np.ndarray[np.str_, ndim=1] partials, np.ndarray[np.str_, ndim=1] combineds): # 先对combined字符串去重 cdef np.ndarray[np.str_, ndim=1] unique_comb = np.unique(combineds) cdef int n_partials = partials.shape[0] cdef np.ndarray[np.str_, ndim=1] results = np.full(n_partials, 'None', dtype=np.str_) cdef int i cdef str partial cdef set matched # 多线程遍历子串(需要OpenMP支持) for i in prange(n_partials, nogil=True): partial = partials[i] matched = set() for comb in unique_comb: if partial in comb: matched.add(comb) with gil: if len(matched) == 1: results[i] = matched.pop() elif len(matched) > 1: results[i] = 'Array' return results # 使用方式 df_a['reference'] = process_matches(df_a['partial_string'].to_numpy(), df_b['combined_string'].to_numpy())
⚠️ 注意:Cython版本本质还是双重循环,时间复杂度依然是O(P*C),对于650万×100万的量级,依然远不如Aho-Corasick方案高效。
内容的提问来源于stack exchange,提问作者IT Jeroen
相关产品推荐
相关产品推荐

