查找两个字符串之间的相似度度量

Posted

技术标签:

【中文标题】查找两个字符串之间的相似度度量【英文标题】:Find the similarity metric between two strings 【发布时间】:2013-06-27 14:44:54 【问题描述】:

如何在 Python 中获得一个字符串与另一个字符串相似的概率?

我想得到一个十进制值,比如 0.9(意思是 90%)等。最好使用标准 Python 和库。

例如

similar("Apple","Appel") #would have a high prob.

similar("Apple","Mango") #would have a lower prob.

【问题讨论】:

我认为“概率”在这里用词不太恰当。无论如何,请参阅***.com/questions/682367/… 您要查找的词是比率,而不是概率。 看看Hamming distance。 短语是'similarity metric',但是有多个相似度度量(Jaccard、Cosine、Hamming、Levenshein 等)表示所以你需要指定哪个。具体来说,您需要字符串之间的相似性度量; @hbprotoss 列出了几个。 我喜欢***.com/questions/653157/…的“bigrams” 【参考方案1】:

有一个内置的。

from difflib import SequenceMatcher

def similar(a, b):
    return SequenceMatcher(None, a, b).ratio()

使用它:

>>> similar("Apple","Appel")
0.8
>>> similar("Apple","Mango")
0.0

【讨论】:

查看比较 SequenceMatcherpython-Levenshtein 模块的出色答案。 ***.com/questions/6690739/… 有趣的文章和工具:chairnerd.seatgeek.com/… 我强烈建议检查整个 difflib 文档 docs.python.org/2/library/difflib.html 有一个内置的 get_close_matches,虽然我发现 sorted(... key=lambda x: difflib.SequenceMatcher(None, x, search).ratio(), ...) 更可靠,自定义 sorted(... .get_matching_blocks())[-1] > min_match 检查 @ThorSummoner 带来了一个非常有用的功能 (get_closest_matches)。这可能是您正在寻找的便利功能,AKA 阅读文档!在我的特定应用程序中,我正在对提供错误输入的用户进行一些基本的错误检查/报告,这个答案允许我向他们报告潜在的匹配“相似性”是什么。但是,如果您不需要显示相似性,请务必查看get_closest_matches 这非常有效。简单有效。谢谢你:)【参考方案2】:

我想也许您正在寻找一种描述字符串之间距离的算法。这里有一些你可以参考:

    Hamming distance Levenshtein distance Damerau–Levenshtein distance Jaro–Winkler distance

【讨论】:

【参考方案3】:

解决方案 #1:Python 内置

从difflib 使用SequenceMatcher

优点: 原生 python 库,不需要额外的包。缺点:太有限了,还有很多其他好的字符串相似度算法。

示例
>>> from difflib import SequenceMatcher
>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75

解决方案 #2:jellyfish 库

它是一个非常好的库,覆盖范围广,问题少。 它支持: - Levenshtein 距离 - Damerau-Levenshtein 距离 - 加罗距离 - Jaro-Winkler 距离 - 匹配评分方法比较 - 汉明距离

优点: 易于使用,支持的算法范围广泛,经过测试。缺点:不是原生库。

例子

>>> import jellyfish
>>> jellyfish.levenshtein_distance(u'jellyfish', u'smellyfish')
2
>>> jellyfish.jaro_distance(u'jellyfish', u'smellyfish')
0.89629629629629637
>>> jellyfish.damerau_levenshtein_distance(u'jellyfish', u'jellyfihs')
1

【讨论】:

【参考方案4】:

TheFuzz 是一个package,它在 python 中实现了 Levenshtein 距离,在某些情况下,您可能希望两个不同的字符串被视为相同,它具有一些帮助函数。例如:

>>> fuzz.ratio("fuzzy wuzzy was a bear", "wuzzy fuzzy was a bear")
    91
>>> fuzz.token_sort_ratio("fuzzy wuzzy was a bear", "wuzzy fuzzy was a bear")
    100

【讨论】:

更新链接github.com/seatgeek/thefuzz 更新了链接和包名,谢谢。【参考方案5】:

您可以创建如下函数:

def similar(w1, w2):
    w1 = w1 + ' ' * (len(w2) - len(w1))
    w2 = w2 + ' ' * (len(w1) - len(w2))
    return sum(1 if i == j else 0 for i, j in zip(w1, w2)) / float(len(w1))

【讨论】:

但similar('appel','apple')高于similar('appel','ape') 您的函数会将给定的字符串与其他字符串进行比较。我想要一种方法来返回具有最高相似率的字符串 @SaulloCastro, if self.similar(search_string, item.text()) > 0.80: 现在可以使用。谢谢,【参考方案6】:

包裹distance 包含Levenshtein 距离:

import distance
distance.levenshtein("lenvestein", "levenshtein")
# 3

【讨论】:

【参考方案7】:

注意,difflib.SequenceMatcher找到最长的连续匹配子序列,这通常不是我们想要的,例如:

>>> a1 = "Apple"
>>> a2 = "Appel"
>>> a1 *= 50
>>> a2 *= 50
>>> SequenceMatcher(None, a1, a2).ratio()
0.012  # very low
>>> SequenceMatcher(None, a1, a2).get_matching_blocks()
[Match(a=0, b=0, size=3), Match(a=250, b=250, size=0)]  # only the first block is recorded

寻找两个字符串之间的相似性与生物信息学中成对序列比对的概念密切相关。有许多专门的库,包括biopython。这个例子实现了Needleman Wunsch algorithm:

>>> from Bio.Align import PairwiseAligner
>>> aligner = PairwiseAligner()
>>> aligner.score(a1, a2)
200.0
>>> aligner.algorithm
'Needleman-Wunsch'

使用 biopython 或其他生物信息学软件包比 python 标准库的任何部分都更灵活,因为有许多不同的评分方案和算法可用。此外,您实际上可以获取匹配序列以可视化正在发生的事情:

>>> alignment = next(aligner.align(a1, a2))
>>> alignment.score
200.0
>>> print(alignment)
Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-Apple-
|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-|||-|-
App-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-elApp-el

【讨论】:

【参考方案8】:

内置的SequenceMatcher 在大输入时非常慢,下面是使用diff-match-patch 的方法:

from diff_match_patch import diff_match_patch

def compute_similarity_and_diff(text1, text2):
    dmp = diff_match_patch()
    dmp.Diff_Timeout = 0.0
    diff = dmp.diff_main(text1, text2, False)

    # similarity
    common_text = sum([len(txt) for op, txt in diff if op == 0])
    text_length = max(len(text1), len(text2))
    sim = common_text / text_length

    return sim, diff

【讨论】:

【参考方案9】:

BLEUscore

BLEU,或双语评估研究,是用于比较的分数 文本到一个或多个参考翻译的候选翻译。

完美匹配的得分为 1.0,而完美的不匹配 结果得分为 0.0。

虽然是为翻译而开发的,但它可以用来评估文本 为一套自然语言处理任务生成。

代码:

import nltk
from nltk.translate import bleu
from nltk.translate.bleu_score import SmoothingFunction
smoothie = SmoothingFunction().method4

C1='Text'
C2='Best'

print('BLEUscore:',bleu([C1], C2, smoothing_function=smoothie))

示例:通过更新 C1 和 C2。

C1='Test' C2='Test'

BLEUscore: 1.0

C1='Test' C2='Best'

BLEUscore: 0.2326589746035907

C1='Test' C2='Text'

BLEUscore: 0.2866227639866161

还可以比较句子相似度:

C1='It is tough.' C2='It is rough.'

BLEUscore: 0.7348889200874658

C1='It is tough.' C2='It is tough.'

BLEUscore: 1.0

【讨论】:

【参考方案10】:

您可以在此链接下找到大多数文本相似度方法以及它们的计算方法:https://github.com/luozhouyang/python-string-similarity#python-string-similarity 这里有一些例子;

标准化、度量、相似性和距离

(归一化)相似度和距离

公制距离

基于带状疱疹(n-gram)的相似性和距离 列文斯坦 规范化的 Levenshtein 加权莱文斯坦 Damerau-Levenshtein 最佳字符串对齐方式 雅罗-温克勒 最长公共子序列 公制最长公共子序列 N-Gram 基于 Shingle(n-gram) 的算法 Q-Gram 余弦相似度 杰卡德索引 Sorensen-Dice 系数 重叠系数(即 Szymkiewicz-Simpson)

【讨论】:

【参考方案11】:

如上所述,有许多指标可以定义字符串之间的相似性和距离。我将通过展示Jaccard similarityQ-Grams 的示例以及edit distance 的示例来给出我的5 美分。

图书馆

from nltk.metrics.distance import jaccard_distance
from nltk.util import ngrams
from nltk.metrics.distance  import edit_distance

Jaccard 相似度

1-jaccard_distance(set(ngrams('Apple', 2)), set(ngrams('Appel', 2)))

我们得到:

0.33333333333333337

对于AppleMango

1-jaccard_distance(set(ngrams('Apple', 2)), set(ngrams('Mango', 2)))

我们得到:

0.0

编辑距离

edit_distance('Apple', 'Appel')

我们得到:

2

最后,

edit_distance('Apple', 'Mango')

我们得到:

5

Q-Grams 上的余弦相似度 (q=2)

另一个解决方案是使用textdistance 库。我将提供Cosine Similarity的示例

import textdistance
1-textdistance.Cosine(qval=2).distance('Apple', 'Appel')

我们得到:

0.5

【讨论】:

【参考方案12】:

文字距离:

TextDistance - python 库,用于通过许多算法比较两个或多个序列之间的距离。它有Textdistance

30 多种算法 纯python实现 简单的用法 比较两个以上的序列 有些算法在一个类中有多个实现。 可选使用 numpy 以获得最大速度。

示例 1:

import textdistance
textdistance.hamming('test', 'text')

输出:

1

示例 2:

import textdistance

textdistance.hamming.normalized_similarity('test', 'text')

输出:

0.75

感谢和干杯!!!

【讨论】:

【参考方案13】:

这是我的想法:

import string

def match(a,b):
    a,b = a.lower(), b.lower()
    error = 0
    for i in string.ascii_lowercase:
            error += abs(a.count(i) - b.count(i))
    total = len(a) + len(b)
    return (total-error)/total

if __name__ == "__main__":
    print(match("pple inc", "Apple Inc."))

【讨论】:

【参考方案14】:

Python3.6+=

未导入库

在大多数情况下都能正常工作

在堆栈溢出中,当您尝试添加标签或发布问题时,它会显示所有相关内容。这非常方便,正是我正在寻找的算法。因此,我编写了一个查询集相似度过滤器。

def compare(qs, ip):
    al = 2
    v = 0
    for ii, letter in enumerate(ip):
        if letter == qs[ii]:
            v += al
        else:
            ac = 0
            for jj in range(al):
                if ii - jj < 0 or ii + jj > len(qs) - 1: 
                    break
                elif letter == qs[ii - jj] or letter == qs[ii + jj]:
                    ac += jj
                    break
            v += ac
    return v


def getSimilarQuerySet(queryset, inp, length):
    return [k for tt, (k, v) in enumerate(reversed(sorted(it: compare(it, inp) for it in queryset.items(), key=lambda item: item[1])))][:length]
        


if __name__ == "__main__":
    print(compare('apple', 'mongo'))
    # 0
    print(compare('apple', 'apple'))
    # 10
    print(compare('apple', 'appel'))
    # 7
    print(compare('dude', 'ud'))
    # 1
    print(compare('dude', 'du'))
    # 4
    print(compare('dude', 'dud'))
    # 6

    print(compare('apple', 'mongo'))
    # 2
    print(compare('apple', 'appel'))
    # 8

    print(getSimilarQuerySet(
        [
            "java",
            "jquery",
            "javascript",
            "jude",
            "aja",
        ], 
        "ja",
        2,
    ))
    # ['javascript', 'java']

说明

compare 接受两个字符串并返回一个正整数。 您可以编辑compare中的al允许变量,它表示我们需要搜索的范围有多大。它的工作原理是这样的:迭代两个字符串,如果在相同的索引处找到相同的字符,则将累加器添加到最大值。然后,我们在allowed的索引范围内搜索,如果匹配,则根据字母的距离添加到累加器中。 (越远越小) length 表示您想要多少项作为结果,与输入字符串最相似。

【讨论】:

以上是关于查找两个字符串之间的相似度度量的主要内容,如果未能解决你的问题,请参考以下文章

协同过滤用户相似度度量

常见的相似度度量算法

如何从 TfidfVectorizer 计算余弦相似度?

Gensim Word2vec:语义相似度

大数据学习笔记:距离度量和相似度度量

判断两个数组的相似性