1

我有 2 列疾病名称,我必须尝试匹配最佳选项。我尝试在 python 中使用“SequenceMatcher”模块和“fuzzywuzzy”模块,结果令人惊讶。我在下面粘贴了结果和我的疑问:

考虑有一种疾病“肝脏肿瘤”,我需要匹配最佳匹配名称“癌症,肝脏”或“癌症,乳房”。现在很明显,由于肝脏是一个匹配词,它应该很容易选择“癌症,肝脏”作为答案,但这并没有发生。我想知道在python中匹配的原因和更好的方法。

from difflib import SequenceMatcher

s1 = 'liver neoplasms'
s2 = 'cancer, liver'

SequenceMatcher(None, s1, s2).ratio() 
# Answer = 0.3571

s2 = 'cancer, breast'
SequenceMatcher(None, s1, s2).ratio()
# Answer = 0.4137 

# fuzzy.ratio also has the same results.

我的疑问是癌症,乳房如何比癌症,肝脏更匹配。我可以使用哪些其他技术来正确完成这项工作?

谢谢 :)

4

4 回答 4

2

这是两个字符串之间的余弦相似度匹配算法。

理论解释请参考以下链接

https://blog.nishtahir.com/2015/09/19/fuzzy-string-matching-using-cosine-similarity/

import re
import math
from collections import Counter


def get_cosine(vec1, vec2):
    intersection = set(vec1.keys()) & set(vec2.keys())
    numerator = sum([vec1[x] * vec2[x] for x in intersection])

    sum1 = sum([vec1[x]**2 for x in vec1.keys()])
    sum2 = sum([vec2[x]**2 for x in vec2.keys()])
    denominator = math.sqrt(sum1) * math.sqrt(sum2)

    if not denominator:
        return 0.0
    else:
        return float(numerator) / denominator


def text_to_vector(text):
    word = re.compile(r'\w+')
    words = word.findall(text)
    return Counter(words)


def get_result(content_a, content_b):
    text1 = content_a
    text2 = content_b

    vector1 = text_to_vector(text1)
    vector2 = text_to_vector(text2)

    cosine_result = get_cosine(vector1, vector2)
    return cosine_result


print(get_result('liver neoplasms', 'cancer, liver'))
print(get_result('liver neoplasms', 'cancer, breast'))
于 2019-12-23T03:47:46.220 回答
2

这些类型的匹配器没有语义理解。他们只是计算有多少字符匹配。有些比其他的更复杂。

距离levenshtein可能会有所帮助。请参阅https://github.com/ztane/python-Levenshtein

from difflib import SequenceMatcher from Levenshtein import distance

s1 = 'liver neoplasms' s2 = 'cancer, liver'

print('Sequence-matcher: ', SequenceMatcher(None, s1, s2).ratio()) 
# Answer = 0.35...

print('Levenshtein: ', distance(s1, s2))
# Answer = 13

s2 = 'cancer, breast' 

print('Sequence-matcher: ', SequenceMatcher(None, s1, s2).ratio()) 
# Answer = 0.41...

print('Levenshtein: ', distance(s1, s2))
# Answer = 12
于 2019-12-23T03:53:39.520 回答
1

似乎两者都difflib.SequenceMatcher使用fuzzywuzzy相同的机制来确定相似性。即Levenshtein Distance,可以有效地概括为“将一个字符串转换为另一个字符串所需的修改次数”。

在这里,根据这个计算器'liver neoplasms',和之间的 Levenshtein 距离是 13。同时,和'cancer, liver'之间的距离是 12 - 稍微小一些。'liver neoplasms''cancer, breast'

Levenshtein 距离似乎不是解决这个问题的理想方法。


在您的情况下,我会尝试使用某种形式的关键字匹配。我没有很好地了解这样做的正确技术,但我的直觉是将输入分成关键字,将可能的输出分成关键字:

input_keywords = 'liver neoplasms'.split()
possibility_keywords = {title: title.split(', ') for title in ('cancer, breast', 'cancer, liver')}

然后进行某种加权匹配(无论哪一组可能的关键字最接近输入的一组关键字——您可能需要创造性地找出有效的计算方法)或关键字检测。例如:

def ratio(input_keywords, possibility_keywords):
    return sum(
        min(
            SequenceMatcher(None, inp_kw, poss_kw).ratio() for poss_kw in possibility_keywords
        ) for inp_kw in input_keywords
     )

快速粗略地一瞥发现这篇论文可能是相关的。或者其他答案提到的余弦相似度算法。

于 2019-12-23T03:48:37.063 回答
0

我们需要在这里使用语义相似性算法,Neoplasm并且Cancer是相似的术语,但是如果我们进行基于 Levenshtein 距离或关键字的匹配,它将匹配不佳或根本不匹配。

在此类术语的语料库上训练word2vec模型,并使用此模型来获取词向量。在这个阶段,我们可以使用余弦相似度、软余弦相似度等从词向量中创建相似度索引,并得到两个语义匹配词之间的相似度。

参考:链接

于 2021-06-16T07:40:57.040 回答