You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何加速十万级姓名列表的模糊匹配?

大规模姓名模糊匹配优化方案

针对10万级姓名记录的模糊匹配效率问题,先拆解现有代码的核心瓶颈:

  • 时间复杂度为O(N*M):外层遍历待筛查姓名,内层遍历所有参考姓名计算相似度,10万×10万的计算量直接导致效率极低
  • 重复计算排序姓名:每次调用相似度函数都要重新拆分、排序字符串,无缓存机制
  • Pandas apply的额外开销:本质是循环,远慢于向量化操作

以下是可落地的优化思路:

1. 预处理排序姓名,消除重复计算

提前为两个数据集生成排序后的姓名列,避免每次计算都重复执行拆分、排序逻辑:

import pandas as pd

# 读取数据
df_name_reference = pd.read_csv('path_to_file')
df_name_to_screen = pd.read_csv('path_to_file')

# 预生成排序姓名列
def sort_name(name):
    if pd.isna(name) or not isinstance(name, str):
        return ""
    return ' '.join(sorted(name.split())).strip()

df_name_reference['sorted_fullname'] = df_name_reference['fullname'].apply(sort_name)
df_name_to_screen['sorted_fullname'] = df_name_to_screen['fullname'].apply(sort_name)

2. 替换为RapidFuzz库,提升单计算速度

RapidFuzz是C++实现的模糊匹配库,速度比原生Levenshtein快数倍,支持批量计算:

from rapidfuzz import fuzz

# 批量计算单个待筛查姓名与所有参考姓名的最大相似度
def get_batch_scores(ref_name, ref_sorted, ref_names, ref_sorted_names):
    # 批量计算原姓名相似度
    scores_raw = fuzz.ratio(ref_name, ref_names, score_cutoff=0.0)
    # 批量计算排序后姓名相似度
    scores_sorted = fuzz.ratio(ref_sorted, ref_sorted_names, score_cutoff=0.0)
    # 取每个姓名的最高分数
    return [max(a, b) for a, b in zip(scores_raw, scores_sorted)]

# 转换为列表,减少Pandas索引开销
ref_names_list = df_name_reference['fullname'].tolist()
ref_sorted_list = df_name_reference['sorted_fullname'].tolist()

screening_results = []
for _, row in df_name_to_screen.iterrows():
    scores = get_batch_scores(row['fullname'], row['sorted_fullname'], ref_names_list, ref_sorted_list)
    screening_results.append(pd.DataFrame({
        'screened_name': row['fullname'],
        'scores': scores,
        'reference_name': ref_names_list
    }))

3. 预过滤减少计算量

通过姓名长度、首字母等特征快速过滤不可能匹配的样本,大幅减少计算次数:

def pre_filter(ref_name, ref_names, ref_sorted_names, length_tol=3):
    ref_len = len(ref_name)
    # 仅保留长度差在阈值内的姓名
    filtered_indices = [
        i for i, name in enumerate(ref_names)
        if abs(len(name) - ref_len) <= length_tol
    ]
    return (
        filtered_indices,
        [ref_names[i] for i in filtered_indices],
        [ref_sorted_names[i] for i in filtered_indices]
    )

# 结合预过滤的批量计算
screening_results = []
for _, row in df_name_to_screen.iterrows():
    ref_name = row['fullname']
    ref_sorted = row['sorted_fullname']
    # 预过滤
    filtered_indices, filtered_names, filtered_sorted = pre_filter(ref_name, ref_names_list, ref_sorted_list)
    # 仅计算过滤后的样本
    scores = get_batch_scores(ref_name, ref_sorted, filtered_names, filtered_sorted)
    # 补全未过滤样本的分数为0
    full_scores = [0.0] * len(ref_names_list)
    for i, idx_ref in enumerate(filtered_indices):
        full_scores[idx_ref] = scores[i]
    screening_results.append(pd.DataFrame({
        'screened_name': ref_name,
        'scores': full_scores,
        'reference_name': ref_names_list
    }))

4. 用近似最近邻(ANN)将复杂度降至O(N*logM)

对于10万级数据,O(N*M)的复杂度完全不可行,推荐用FAISS构建向量索引,快速查找相似姓名:

import faiss
from sklearn.feature_extraction.text import TfidfVectorizer

# 用字符n-gram构建TF-IDF向量(适配姓名匹配场景)
vectorizer = TfidfVectorizer(analyzer='char', ngram_range=(2,3))
ref_vectors = vectorizer.fit_transform(df_name_reference['fullname']).toarray()

# 构建FAISS索引
index = faiss.IndexFlatL2(ref_vectors.shape[1])
index.add(ref_vectors)

# 查找Top-K相似姓名后再计算精确相似度
k = 100  # 取最相似的100个样本,可按需调整
screening_results = []
for _, row in df_name_to_screen.iterrows():
    ref_name = row['fullname']
    # 转换待筛查姓名为向量
    query_vec = vectorizer.transform([ref_name]).toarray()
    # 快速查找Top-K相似
    distances, indices = index.search(query_vec, k)
    # 对Top-K样本计算精确相似度
    top_k_refs = df_name_reference.iloc[indices[0]]
    scores = get_batch_scores(
        ref_name, row['sorted_fullname'],
        top_k_refs['fullname'].tolist(),
        top_k_refs['sorted_fullname'].tolist()
    )
    screening_results.append(pd.DataFrame({
        'screened_name': ref_name,
        'reference_name': top_k_refs['fullname'].tolist(),
        'scores': scores
    }))

5. 多进程并行处理

利用CPU多核并行处理独立的姓名匹配任务:

from multiprocessing import Pool

def process_single_row(row):
    ref_name = row['fullname']
    ref_sorted = row['sorted_fullname']
    filtered_indices, filtered_names, filtered_sorted = pre_filter(ref_name, ref_names_list, ref_sorted_list)
    scores = get_batch_scores(ref_name, ref_sorted, filtered_names, filtered_sorted)
    full_scores = [0.0] * len(ref_names_list)
    for i, idx_ref in enumerate(filtered_indices):
        full_scores[idx_ref] = scores[i]
    return pd.DataFrame({
        'screened_name': ref_name,
        'scores': full_scores,
        'reference_name': ref_names_list
    })

# 转换为列表并行处理
rows_list = [row for _, row in df_name_to_screen.iterrows()]
with Pool(processes=4) as pool:  # 进程数根据CPU核心数调整
    screening_results = pool.map(process_single_row, rows_list)

额外建议

  • 统一姓名格式:提前转换为小写、去除特殊字符(.、-)、标准化中间名缩写,减少无效差异
  • 设置相似度阈值:仅保留分数高于阈值(如0.8)的结果,减少数据量和计算量

内容的提问来源于stack exchange,提问作者jsv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 21:45:35