【问题标题】:Find the similarity metric between two strings查找两个字符串之间的相似度度量
【发布时间】:2021-10-12 03:51:07
【问题描述】:

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

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

例如

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

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

【问题讨论】:

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

标签: python probability similarity metric


【解决方案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 模块的出色答案。 stackoverflow.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】:

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

  1. Hamming distance
  2. Levenshtein distance
  3. Damerau–Levenshtein distance
  4. 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
      

      【讨论】:

      【解决方案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 表示你想要多少项作为结果,与输入字符串最相似。

                      【讨论】:

                        猜你喜欢
                        • 2018-07-15
                        • 2015-12-05
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2010-10-09
                        相关资源
                        最近更新 更多