求助:基于文本相似度分数映射Python不等长字符串列表中的字符串对
解决方案:字符串列表的唯一相似度匹配
没问题,我来帮你搞定这个需求——把列表A里的每个字符串唯一映射到列表B中相似度最高的字符串,同时处理两个列表长度不同的情况。下面是具体的实现思路和代码:
核心思路
- 文本预处理:先统一文本格式(转小写、去标点、去停用词),避免无关因素干扰相似度计算。
- 相似度计算:支持两种常用算法:
- 余弦相似度:适合捕捉文本语义相关性,尤其适合长文本;
- 杰卡德相似度:通过词集合的交集/并集计算,适合看重词重叠度的场景。
- 唯一匹配:用匈牙利算法解决一对一分配问题,保证全局最优的匹配结果(比贪心算法更可靠,不会出现局部最优导致的浪费)。
完整代码实现
首先需要安装依赖库(如果没装的话):
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




