Paramètres de similarité des chaînes de caractères en Python

je veux trouver une similarité de chaîne entre deux chaînes. la page a des exemples de certains d'entre eux. Python a une implémentation de algorithme de Levenshtein. Y a-t-il un meilleur algorithme, (et avec un peu de chance une bibliothèque python), sous ces contraintes.

  1. je veux faire correspondances floues entre les chaînes. par exemple correspond à('Bonjour, vous Tous les gens', 'bonjour, Vous tous d') doit retourner True
  2. les faux négatifs sont acceptables, Les faux positifs, sauf dans des cas extrêmement rares ne le sont pas.
  3. ceci est fait dans un réglage non temps réel, donc la vitesse n'est pas (beaucoup) préoccupante.
  4. [Edit] je compare des chaînes de mots multiples.

est-ce que quelque chose d'autre que la distance Levenshtein(ou le rapport Levenshtein) serait un meilleur algorithme pour mon cas?

38
demandé sur Legend 2009-09-24 15:43:00

6 réponses

il y a une excellente ressource pour les mesures de similarité des cordes à L'Université de Sheffield. Il a une liste de différents paramètres (au-delà de Levenshtein) et a des implémentations open-source de ceux-ci. On dirait 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:

  • Hamming distance
  • Levenshtein
  • Needleman-Wunch distance or Sellers algorithme
  • et bien d'autres...
18
répondu ariddell 2016-02-16 13:45:46

je réalise que c'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 le faire comme une 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
79
répondu Nadia Alramli 2009-09-24 13:10:55

cet extrait calcule les valeurs de similarité de difflib, Levenshtein, Sørensen et Jaccard pour deux chaînes. Dans l'extrait ci-dessous, je répétais sur 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
12
répondu duhaime 2015-07-06 00:51:12

j'utiliserais la distance de Levenshtein, ou la distance dite de Damerau (qui prend en compte les transpositions) plutôt que le truc difflib pour deux raisons (1) "assez rapide" (programmation dynamique algo) et "whoooosh" (bit-bashing) code C est disponible et (2) le comportement bien compris par exemple Levenshtein satisfait l'inégalité du triangle et peut donc être utilisé dans un arbre de Burkhard-Keller par exemple.

Seuil: vous devez traiter comme "positif" seulement les cas où la distance < (1-X) * max (Len (string1), len(string2)) et ajuster X (le facteur de similarité) à votre convenance. Une façon de choisir X est d'obtenir un échantillon de correspondances, calculer X pour chacun, ignorer les cas où X < Par exemple 0.8 ou 0.9, puis trier le reste dans l'ordre décroissant de X et eye-ball eux et insérez le résultat correct et calculez une mesure du coût des erreurs pour divers niveaux de X.

N.B. votre exemple a une distance de 2, donc X est 0.6 ... Je n'utiliserais un seuil aussi bas que 0,75 que si je étaient désespérément à la recherche de quelque chose et avait une forte faux négatifs pénalité

6
répondu John Machin 2009-09-24 14:41:31

c'Est ce 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

4
répondu Tzury Bar Yochay 2009-09-24 11:46:47

je sais que ce n'est pas la même, mais vous pouvez ajuster le ratio de filtrer les chaînes qui ne sont pas assez similaire et le retour de la correspondance la plus proche de la chaîne que vous recherchez.

peut-être seriez-vous plus intéressé par les mesures sémantiques de similarité.

https://www.google.com/search?client=ubuntu&channel=fs&q=semantic+similarity+string+match&ie=utf-8&oe=utf-8

je me rends compte que vous avez dit que la vitesse n'est pas un problème mais si vous traitez beaucoup de les cordes de votre algorithme ci-dessous 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

2
répondu John 2014-11-13 23:21:27