You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

求助:基于文本相似度分数映射Python不等长字符串列表中的字符串对

解决方案:字符串列表的唯一相似度匹配

没问题,我来帮你搞定这个需求——把列表A里的每个字符串唯一映射到列表B中相似度最高的字符串,同时处理两个列表长度不同的情况。下面是具体的实现思路和代码:

核心思路

  1. 文本预处理:先统一文本格式(转小写、去标点、去停用词),避免无关因素干扰相似度计算。
  2. 相似度计算:支持两种常用算法:
    • 余弦相似度:适合捕捉文本语义相关性,尤其适合长文本;
    • 杰卡德相似度:通过词集合的交集/并集计算,适合看重词重叠度的场景。
  3. 唯一匹配:用匈牙利算法解决一对一分配问题,保证全局最优的匹配结果(比贪心算法更可靠,不会出现局部最优导致的浪费)。

完整代码实现

首先需要安装依赖库(如果没装的话):

pip install nltk scikit-learn scipy

然后运行下面的代码:

import string
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from scipy.optimize import linear_sum_assignment

# 下载nltk的停用词和分词模型(第一次运行需要)
import nltk
nltk.download('punkt')
nltk.download('stopwords')

# 预处理文本:转小写、去标点、去停用词、分词
def preprocess_text(text):
    text = text.lower()
    text = text.translate(str.maketrans('', '', string.punctuation))
    tokens = word_tokenize(text)
    stop_words = set(stopwords.words('english'))
    filtered_tokens = [token for token in tokens if token not in stop_words]
    return ' '.join(filtered_tokens)

# 计算相似度矩阵
def compute_similarity_matrix(list_a, list_b, similarity_type='cosine'):
    processed_a = [preprocess_text(text) for text in list_a]
    processed_b = [preprocess_text(text) for text in list_b]
    
    if similarity_type == 'cosine':
        vectorizer = TfidfVectorizer()
        tfidf_matrix = vectorizer.fit_transform(processed_a + processed_b)
        tfidf_a = tfidf_matrix[:len(list_a)]
        tfidf_b = tfidf_matrix[len(list_a):]
        similarity_matrix = cosine_similarity(tfidf_a, tfidf_b)
    elif similarity_type == 'jaccard':
        similarity_matrix = np.zeros((len(list_a), len(list_b)))
        for i, text_a in enumerate(processed_a):
            set_a = set(text_a.split())
            for j, text_b in enumerate(processed_b):
                set_b = set(text_b.split())
                intersection = len(set_a & set_b)
                union = len(set_a | set_b)
                similarity_matrix[i][j] = intersection / union if union != 0 else 0.0
    else:
        raise ValueError("相似度类型只能是'cosine'或'jaccard'")
    
    return similarity_matrix

# 生成唯一映射结果
def unique_string_mapping(list_a, list_b, similarity_type='cosine'):
    similarity_matrix = compute_similarity_matrix(list_a, list_b, similarity_type)
    # 匈牙利算法默认找最小化分配,所以取负来最大化相似度
    cost_matrix = -similarity_matrix
    row_indices, col_indices = linear_sum_assignment(cost_matrix)
    
    mapping = {}
    # 匹配成功的对
    for a_idx, b_idx in zip(row_indices, col_indices):
        mapping[list_a[a_idx]] = {
            '匹配字符串': list_b[b_idx],
            '相似度得分': round(similarity_matrix[a_idx][b_idx], 4)
        }
    
    # 处理A中未匹配的元素(当A比B长时)
    if len(list_a) > len(list_b):
        unmatched_a = [text for idx, text in enumerate(list_a) if idx not in row_indices]
        mapping['未匹配的A元素'] = unmatched_a
    
    # 处理B中未匹配的元素(当B比A长时)
    if len(list_b) > len(list_a):
        unmatched_b = [text for idx, text in enumerate(list_b) if idx not in col_indices]
        mapping['未匹配的B元素'] = unmatched_b
    
    return mapping

测试你的示例

用你给出的A和B列表测试:

# 你的示例数据
A = [
    'I love in eating apple every Tuesday',
    'I went to the bank to withdraw money',
    'Is python a snake or a programming language'
]
B = [
    'Apple is good for your health, endeavour to eat one once a week',
    'I bank with a local institution to manage my finances'
]

# 余弦相似度匹配结果
print("=== 余弦相似度匹配结果 ===")
cosine_result = unique_string_mapping(A, B, 'cosine')
for k, v in cosine_result.items():
    if isinstance(v, dict):
        print(f"A字符串:{k}")
        print(f"匹配B字符串:{v['匹配字符串']}")
        print(f"得分:{v['相似度得分']}\n")
    else:
        print(f"{k}:{v}\n")

# 杰卡德相似度匹配结果
print("=== 杰卡德相似度匹配结果 ===")
jaccard_result = unique_string_mapping(A, B, 'jaccard')
for k, v in jaccard_result.items():
    if isinstance(v, dict):
        print(f"A字符串:{k}")
        print(f"匹配B字符串:{v['匹配字符串']}")
        print(f"得分:{v['相似度得分']}\n")
    else:
        print(f"{k}:{v}\n")

关键说明

  • 预处理的重要性预处理是提升相似度准确性的核心,比如把"Apple"和"apple"统一成小写,去掉逗号、句号等标点,移除"the"、"is"这类无意义的停用词。
  • 算法选择:如果你的文本是长句子/段落,优先用余弦相似度;如果是短文本(比如关键词列表),杰卡德相似度更合适。
  • 唯一匹配保障:匈牙利算法会确保每个A的元素最多匹配一个B的元素,每个B的元素也最多被一个A的元素匹配,完美满足你的唯一映射需求。
  • 长度差异处理:当两个列表长度不同时,多余的元素会被标记为"未匹配",你可以根据需求决定是否要进一步处理这些元素。

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

火山引擎 最新活动