Comparaison de chaînes floues hautes performances en Python, utilisez Levenshtein ou difflib [fermé]

130

Je fais la normalisation des messages cliniques (vérification orthographique) dans laquelle je vérifie chaque mot donné par rapport au dictionnaire médical de 900 000 mots. Je suis plus préoccupé par la complexité du temps / les performances.

Je veux faire une comparaison floue de chaînes, mais je ne suis pas sûr de la bibliothèque à utiliser.

Option 1:

import Levenshtein
Levenshtein.ratio('hello world', 'hello')

Result: 0.625

Option 2:

import difflib
difflib.SequenceMatcher(None, 'hello world', 'hello').ratio()

Result: 0.625

Dans cet exemple, les deux donnent la même réponse. Pensez-vous que les deux fonctionnent de la même manière dans ce cas?

Maggie
la source

Réponses:

154

Au cas où vous seriez intéressé par une comparaison visuelle rapide de la similitude entre Levenshtein et Difflib, j'ai calculé les deux pour ~ 2,3 millions de titres de livres:

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'ai ensuite tracé les résultats avec R:

entrez la description de l'image ici

Pour les curieux, j'ai également comparé les valeurs de similarité Difflib, Levenshtein, Sørensen et Jaccard:

library(ggplot2)
require(GGally)

difflib <- read.table("similarity_measures.txt", sep = " ")
colnames(difflib) <- c("difflib", "levenshtein", "sorensen", "jaccard")

ggpairs(difflib)

Résultat: entrez la description de l'image ici

La similitude Difflib / Levenshtein est vraiment très intéressante.

Édition 2018: Si vous travaillez sur l'identification de chaînes similaires, vous pouvez également consulter minhashing - il y a un excellent aperçu ici . Minhashing est incroyable pour trouver des similitudes dans de grandes collections de texte en temps linéaire. Mon laboratoire a mis en place une application qui détecte et visualise la réutilisation de texte à l'aide de minhashing ici: https://github.com/YaleDHLab/intertext

duhaime
la source
2
C'est super cool! Quelle est votre opinion à ce sujet alors? Levenshtein est-il juste mauvais pour les chaînes de longueur de titre?
Ulf Aslak
3
Cela dépend vraiment de ce que vous essayez de capturer dans votre métrique de similarité ...
Duhaime
2
Je pense qu'une partie du désaccord entre difflib et levenshtein peut s'expliquer à cause de l'heuristique d'autojunk utilisée par difflib. Que se passe-t-il si vous le désactivez?
Michael
2
C'est une bonne question. Le filtre autojunk ne prend effet que si le nombre d'observations est> 200, donc je ne suis pas sûr si cet ensemble de données particulier (titres de livres) aurait été grandement affecté, mais cela vaut la peine d'être étudié ...
Duhaime
2
@duhaime, merci pour cette analyse détaillée. Je suis nouveau dans ce genre de parcelles et je ne sais pas comment les interpréter. Comment s'appellent les parcelles, pour que je puisse les rechercher et en apprendre davantage sur elles?
Zach Young
104
  • difflib.SequenceMatcher utilise l' algorithme Ratcliff / Obershelp , il calcule le nombre doublé de caractères correspondants divisé par le nombre total de caractères dans les deux chaînes.

  • Levenshtein utilise l' algorithme de Levenshtein, il calcule le nombre minimum de modifications nécessaires pour transformer une chaîne en une autre

Complexité

SequenceMatcher est le temps quadratique pour le pire des cas et a un comportement de cas attendu dépendant d'une manière compliquée du nombre d'éléments que les séquences ont en commun. ( d'ici )

Levenshtein est O (m * n), où n et m sont la longueur des deux chaînes d'entrée.

Performance

Selon le code source du module Levenshtein: Levenshtein a un certain chevauchement avec difflib (SequenceMatcher). Il ne prend en charge que les chaînes, pas les types de séquence arbitraires, mais d'un autre côté, c'est beaucoup plus rapide.

Ghassen Hamrouni
la source
Merci beaucoup pour l'info. J'ai ajouté plus de détails. le voici: I am doing clinical message normalization (spell check) in which I check each given word against 900,000 word medical dictionary. I am more concern about the time complexity/performance.Pensez-vous que les deux fonctionnent de la même manière dans ce cas.
Maggie le