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

如何计算两个DataFrame元素的字符串相似度并优化匹配效率

商品名称模糊匹配优化问题

问题背景

  • 现有两个Excel文件:商品分类目录表(包含商品名与对应分类)、海量商品数据条目表
  • 数据条目中的商品名称与目录表中的名称存在不一致,需要通过字符串相似度算法实现模糊匹配,找到对应分类
  • 最初使用fuzzywuzzy的Levenshtein距离实现,运行速度极慢;尝试多进程优化后,速度未提升且出现异常

现有代码

from fuzzywuzzy import fuzz
import pandas as pd
import os
import multiprocessing

data = pd.read_excel(r"...\data_items.xlsx")
catalog = pd.read_excel(r"...\catalog.xlsx")


def find_best_match(string, catalog_tokens):
    # 计算输入字符串的token集合
    string_tokens = set(string.split())
    # 计算输入字符串与目录中每个字符串的相似度得分
    scores = catalog_tokens.apply(lambda x: fuzz.token_set_ratio(string_tokens, x))
    # 找到得分最高的目录项索引
    index = scores.idxmax()
    # 返回最佳匹配的商品名和分类
    return catalog.loc[index, "Item name"], catalog.loc[index, "Category name"]

if __name__ == "__main__":
    multiprocessing.freeze_support()

    # 预计算目录中每个商品名的token集合
    catalog_tokens = catalog["Item name"].apply(lambda x: set(x.split()))

    # 使用多进程并行计算
    pool = multiprocessing.Pool()
    results = pool.starmap(find_best_match, [(string, catalog_tokens) for string in data["item"]])
    pool.close()
    pool.join()

    # 拆分结果
    best_matches, categories = zip(*results)
    print(f"Best_matches: {best_matches}   |  Categories: {categories}")

    # 添加结果列到数据中
    data["Item_name"] = best_matches
    data["Category"] = categories

data.to_excel(r"...\New_data_items.xlsx", index=False)

问题分析与优化方案

1. 多进程失效的核心原因

  • catalog_tokens是Pandas Series对象,在多进程中传递时会被完整拷贝到每个子进程,带来巨大的内存开销和序列化时间,抵消了并行计算的优势
  • fuzzywuzzy是纯Python实现,本身计算效率低下,即使并行也难有明显提升

2. 优先提升单进程速度

替换fuzzywuzzy为rapidfuzz(fuzzywuzzy的C++重写版,速度提升10-100倍):

pip install rapidfuzz

修改代码中的导入和匹配逻辑,用process.extractOne简化实现:

from rapidfuzz import fuzz, process

def find_best_match(string, catalog_items):
    best_match, score, _ = process.extractOne(string, catalog_items["Item name"], scorer=fuzz.token_set_ratio)
    category = catalog_items[catalog_items["Item name"] == best_match]["Category name"].iloc[0]
    return best_match, category

3. 正确的多进程优化方式

将目录数据转换为可序列化的列表/字典,避免传递大对象:

if __name__ == "__main__":
    multiprocessing.freeze_support()

    # 预整理目录数据为字典,方便进程传递
    catalog_dict = dict(zip(catalog["Item name"], catalog["Category name"]))
    catalog_names = list(catalog_dict.keys())

    def match_wrapper(string):
        best_match, _, _ = process.extractOne(string, catalog_names, scorer=fuzz.token_set_ratio)
        return best_match, catalog_dict[best_match]

    # 使用imap_unordered减少内存占用(注意:若需保留原数据顺序,需传递索引后再映射)
    pool = multiprocessing.Pool()
    results = list(pool.imap_unordered(match_wrapper, data["item"]))
    pool.close()
    pool.join()

    # 合并结果到原数据
    data["Item_name"], data["Category"] = zip(*results)
    data.to_excel(r"...\New_data_items.xlsx", index=False)

4. 进一步优化思路

  • 字符串预处理:统一转换为小写、去除特殊字符、过滤停用词,减少匹配干扰项
    import re
    def preprocess(s):
        s = s.lower()
        s = re.sub(r'[^\w\s]', '', s)
        stop_words = {"官方", "正品", "新款", "爆款"}
        return ' '.join([word for word in s.split() if word not in stop_words])
    # 预处理所有商品名
    data["item_clean"] = data["item"].apply(preprocess)
    catalog["Item name_clean"] = catalog["Item name"].apply(preprocess)
    
  • 候选集过滤:先通过倒排索引找出包含共同token的目录项,再在候选集中计算相似度,减少计算量
  • 嵌入向量匹配:对于超大规模数据,可使用FastText/Word2Vec将商品名转换为向量,通过余弦相似度快速匹配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 23:20:30