Encontre a métrica de similaridade entre duas strings

283

Como obtenho a probabilidade de uma string ser semelhante a outra no Python?

Eu quero obter um valor decimal como 0,9 (significando 90%) etc. De preferência com Python e biblioteca padrão.

por exemplo

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

similar("Apple","Mango") #would have a lower prob.
tenstar
fonte
6
Não acho que "probabilidade" seja o termo certo aqui. De qualquer forma, consulte stackoverflow.com/questions/682367/…
NPE
1
A palavra que você procura é razão, não probabilidade.
Inbar Rose
1
Dê uma olhada na distância de Hamming .
317 Diana
2
A frase é 'métrica de similaridade' , mas existem várias métricas de similaridade (Jaccard, Cosine, Hamming, Levenshein etc.) ditas, portanto é necessário especificar qual. Especificamente, você deseja uma métrica de similaridade entre as strings; @hbprotoss listou vários.
SMCI

Respostas:

542

Há um construído em.

from difflib import SequenceMatcher

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

Usando isso:

>>> similar("Apple","Appel")
0.8
>>> similar("Apple","Mango")
0.0
Inbar Rose
fonte
42
Veja esta ótima resposta comparando SequenceMatchervs python-Levenshteinmódulo. stackoverflow.com/questions/6690739/…
ssoler
1
Artigo e ferramenta interessantes: chairnerd.seatgeek.com/…
Anthony Perot
7
Eu recomendo verificar toda a difflib doc docs.python.org/2/library/difflib.html há um get_close_matchesconstruído em, embora eu achei sorted(... key=lambda x: difflib.SequenceMatcher(None, x, search).ratio(), ...)mais confiável, com personalizados sorted(... .get_matching_blocks())[-1] > min_matchcheques
ThorSummoner
2
O @ThorSummoner chama a atenção para uma função muito útil ( get_closest_matches). É uma função de conveniência que pode ser o que você está procurando, leia também a documentação! No meu aplicativo em particular, eu estava executando algumas verificações / relatórios básicos de erros ao usuário, fornecendo informações incorretas, e essa resposta me permite relatar a eles as possíveis correspondências e qual a "semelhança". Se você não precisa exibir a semelhança, no entanto, definitivamente verifiqueget_closest_matches
svenevs
Isso funcionou perfeitamente. Simples e eficaz. Obrigado :)
Karthic Srinivasan
45

Solução 1: Python embutido

use SequenceMatcher do difflib

prós : biblioteca python nativa, sem necessidade de pacote extra.
contras : muito limitado, existem muitos outros bons algoritmos para a similaridade de strings por aí.

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

Solução # 2: biblioteca de medusas

é uma biblioteca muito boa, com boa cobertura e poucos problemas. Ele suporta:
- Distância Levenshtein - Distância
Damerau-Levenshtein
- Distância
Jaro - Distância Jaro-Winkler
- Comparação da abordagem de classificação por correspondência
- Distância Hamming

prós : fácil de usar, gama de algoritmos suportados, testado.
contras : biblioteca não nativa.

exemplo :

>>> 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
Iman Mirzadeh
fonte
26

Fuzzy Wuzzyé um pacote que implementa a distância de Levenshtein em python, com algumas funções auxiliares para ajudar em determinadas situações em que você pode querer que duas seqüências distintas sejam consideradas idênticas. Por exemplo:

>>> 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
BLT
fonte
9

Você pode criar uma função como:

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))
Saullo GP Castro
fonte
mas similar ('appel', 'apple') é mais alto que similar ('appel', 'ape')
tenstar
1
Sua função irá comparar uma determinada string com outras picadas. Eu quero uma maneira de retornar a string com a relação de semelhança maior
answerSeeker
1
@SaulloCastro, if self.similar(search_string, item.text()) > 0.80:funciona por enquanto. Obrigado,
answerSeeker
9

A distância do pacote inclui a distância de Levenshtein:

import distance
distance.levenshtein("lenvestein", "levenshtein")
# 3
Enrique Pérez Herrero
fonte
6

O builtin SequenceMatcheré muito lento em entradas grandes, eis como isso pode ser feito com o 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
Damio
fonte
5

Observe que difflib.SequenceMatcher somente encontra a subsequência de correspondência contígua mais longa; isso geralmente não é o desejado, por exemplo:

>>> 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

Encontrar a semelhança entre duas seqüências está intimamente relacionado ao conceito de alinhamento de sequência por pares em bioinformática. Existem muitas bibliotecas dedicadas para isso, incluindo o biopython . Este exemplo implementa o algoritmo Needleman Wunsch :

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

O uso de biopython ou outro pacote de bioinformática é mais flexível do que qualquer parte da biblioteca padrão do python, pois muitos esquemas e algoritmos de pontuação diferentes estão disponíveis. Além disso, você pode obter as seqüências correspondentes para visualizar o que está acontecendo:

>>> 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
Chris_Rands
fonte
0

Você pode encontrar a maioria dos métodos de similaridade de texto e como eles são calculados neste link: https://github.com/luozhouyang/python-string-similarity#python-string-similarity Aqui estão alguns exemplos;

  • Normalizado, métrico, similaridade e distância

  • Semelhança e distância (normalizada)

  • Distâncias métricas

  • Semelhança e distância baseadas em telhas (n-grama)
  • Levenshtein
  • Levenshtein normalizado
  • Levenshtein ponderado
  • Damerau-Levenshtein
  • Alinhamento ideal de strings
  • Jaro-Winkler
  • Subseqüência comum mais longa
  • Subseqüência comum mais longa da métrica
  • N-Gram
  • Algoritmos baseados em telha (n-grama)
  • Q-Gram
  • Semelhança cosseno
  • Índice Jaccard
  • Coeficiente de Sorensen-Dice
  • Coeficiente de sobreposição (por exemplo, Szymkiewicz-Simpson)
Mike
fonte