Je veux trouver une similitude de chaîne entre deux chaînes. This page contient des exemples de certains d'entre eux. Python a une implémentation de algorithme Levenshtein . Existe-t-il un meilleur algorithme, (et avec un peu de chance une python), sous ces contraintes) .
Est-ce que quelque chose d'autre que la distance de Levenshtein (ou le rapport de Levenshtein) serait un meilleur algorithme pour mon cas?
Il existe une excellente ressource pour les mesures de similitude des chaînes à l'Université de Sheffield. Il a une liste de diverses métriques (au-delà de Levenshtein) et en a des implémentations open-source. Il semble que beaucoup d'entre eux devraient être faciles à adapter en Python.
http://web.archive.org/web/20081224234350/http://www.dcs.shef.ac.uk/~sam/stringmetrics.html
Voici un peu de la liste:
Je me rends compte que ce n'est pas la même chose, mais c'est assez proche:
>>> import difflib
>>> a = 'Hello, All you people'
>>> b = 'hello, all You peopl'
>>> seq=difflib.SequenceMatcher(a=a.lower(), b=b.lower())
>>> seq.ratio()
0.97560975609756095
Vous pouvez faire cela en fonction
def similar(seq1, seq2):
return difflib.SequenceMatcher(a=seq1.lower(), b=seq2.lower()).ratio() > 0.9
>>> similar(a, b)
True
>>> similar('Hello, world', 'Hi, world')
False
Cet extrait calculera les valeurs de similitude difflib, Levenshtein, Sørensen et Jaccard pour deux chaînes. Dans l'extrait ci-dessous, je parcourais un tsv dans lequel les chaînes d'intérêt occupaient des colonnes [3]
et [4]
du tsv. (pip install python-Levenshtein
et pip install distance
):
import codecs, difflib, Levenshtein, distance
with codecs.open("titles.tsv","r","utf-8") as f:
title_list = f.read().split("\n")[:-1]
for row in title_list:
sr = row.lower().split("\t")
diffl = difflib.SequenceMatcher(None, sr[3], sr[4]).ratio()
lev = Levenshtein.ratio(sr[3], sr[4])
sor = 1 - distance.sorensen(sr[3], sr[4])
jac = 1 - distance.jaccard(sr[3], sr[4])
print diffl, lev, sor, jac
J'utiliserais la distance de Levenshtein, ou la distance dite de Damerau (qui prend en compte les transpositions) plutôt que la substance difflib pour deux raisons (1) "assez rapide" (algo de programmation dynamique) et "whoooosh" (bas-bithing) C le code est disponible et (2) un comportement bien compris, par exemple Levenshtein satisfait l'inégalité du triangle et peut donc être utilisé par ex. un arbre Burkhard-Keller.
Seuil: vous devez traiter comme "positif" uniquement les cas où la distance <(1 - X) * max (len (string1), len (string2)) et ajuster X (le facteur de similitude) selon vos besoins. Une façon de choisir X consiste à obtenir un échantillon de correspondances, à calculer X pour chacune, à ignorer les cas où X <par exemple 0,8 ou 0,9, puis à trier le reste par ordre décroissant de X et à les observer et à insérer le résultat correct et à calculer certains mesure du coût des erreurs pour différents niveaux de X.
N.B. Votre exemple de singe/Apple a une distance de 2, donc X est de 0,6 ... Je n'utiliserais qu'un seuil aussi bas que 0,75 si je cherchais désespérément quelque chose et que ma pénalité en faux négatif était élevée
C'est ça que tu veux dire?
>>> get_close_matches('appel', ['ape', 'Apple', 'Peach', 'puppy'])
['Apple', 'ape']
>>> import keyword
>>> get_close_matches('wheel', keyword.kwlist)
['while']
>>> get_close_matches('Apple', keyword.kwlist)
[]
>>> get_close_matches('accept', keyword.kwlist)
['except']
regardez http://docs.python.org/library/difflib.html#difflib.get_close_matches
Je sais que ce n'est pas la même chose, mais vous pouvez ajuster le rapport pour filtrer les chaînes qui ne sont pas assez similaires et renvoyer la correspondance la plus proche à la chaîne que vous recherchez.
Peut-être seriez-vous plus intéressé par les métriques de similitude sémantique.
Je sais que vous avez dit que la vitesse n'est pas un problème, mais si vous traitez beaucoup de chaînes pour votre algorithme, ce qui suit est très utile.
def spellcheck(self, sentence):
#return ' '.join([difflib.get_close_matches(Word, wordlist,1 , 0)[0] for Word in sentence.split()])
return ' '.join( [ sorted( { Levenshtein.ratio(x, Word):x for x in wordlist }.items(), reverse=True)[0][1] for Word in sentence.split() ] )
C'est environ 20 fois plus rapide que difflib.
https://pypi.python.org/pypi/python-Levenshtein/
importer Levenshtein