Python中高效模糊字符串比较,使用Levenshtein或difflib

196 投票
2 回答
105170 浏览
提问于 2025-04-16 21:30

我正在进行临床信息的标准化(类似拼写检查),也就是要把每个给定的单词和一个包含90万个医学单词的字典进行对比。我最关心的是这个过程的时间复杂度和性能。

我想进行模糊字符串比较,但不太确定该用哪个库。

选项1:

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

Result: 0.625

选项2:

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

Result: 0.625

在这个例子中,两者的结果是一样的。你觉得在这种情况下,两者的性能相似吗?

2 个回答

135
  • difflib.SequenceMatcher使用的是一种叫做Ratcliff/Obershelp的算法,它通过计算两个字符串中匹配字符的数量的两倍,再除以这两个字符串的总字符数来得出结果。

  • Levenshtein使用的是Levenshtein算法,它计算的是将一个字符串变成另一个字符串所需的最少编辑次数。

复杂度

SequenceMatcher在最坏情况下的时间复杂度是平方级别的,而它的实际表现又复杂地依赖于两个序列中有多少元素是相同的。 (来源)

Levenshtein的复杂度是O(m*n),其中n和m分别是两个输入字符串的长度。

性能

根据Levenshtein模块的源代码,Levenshtein和difflib(SequenceMatcher)有一些重叠的地方。它只支持字符串,而不支持其他类型的序列,但另一方面,它的速度要快得多。

233

如果你对Levenshtein和Difflib相似度的快速视觉比较感兴趣,我计算了大约230万本书名的相似度:

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

然后我用R语言把结果画了出来:

这里输入图片描述

为了满足好奇心,我还比较了Difflib、Levenshtein、Sørensen和Jaccard的相似度值:

library(ggplot2)
require(GGally)

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

ggpairs(difflib)

结果是: 这里输入图片描述

Difflib和Levenshtein的相似度确实很有意思。

2018年更新:如果你正在研究识别相似字符串的工作,可以看看minhashing——这里有一个很好的概述。Minhashing在大文本集合中寻找相似性方面非常出色,速度很快。我的实验室还开发了一个应用,使用minhashing来检测和可视化文本重用,链接在这里:https://github.com/YaleDHLab/intertext

撰写回答