文档章节

使用 Levenshtein 寻找彼此相似的字符串对

xh4n3
 xh4n3
发布于 2015/08/18 17:18
字数 1507
阅读 503
收藏 15

我们爬来了一些数据,接下来以豆瓣畅销书为例。

爬虫爬来的数据有

['艾伦•图灵传','深入理解计算机系统(原书第2版)','C++ Primer 中文版(第 5 版)','深入理解计算机系统','Web性能权威指南']

而我们系统中原有的数据有

['艾伦·图灵传','深入理解计算机系统(原书第2版)','C++ Primer 中文版(第 4 版)','深入理解计算机系统']

做前端的同志可能一眼就看出来了,两个数组中有三个元素是因为全半角的缘故,是不能全词匹配的,而前两本书事实上是同一本书。而《深入理解计算机系统》是可以全词匹配到的,《Web性能权威指南》一书是可以直接添加到数据库的。


解决方案一:

这个任务大可以交给编辑去做,但是时间复杂度为 N^2,连程序都吃不消跑,更别提让编辑做了。

解决方案二:

去除所有的标点符号,或者将所有全角符号转化为半角。 去掉所有空格。 然后进行全词匹配,这样做有些鲁莽,但是速度一点也不慢。

解决方案三:

我想到了用 jieba 进行中文分词,

import jieba
book = '艾伦·图灵传'
word = jieba.cut(book)
words = list(word)
# words = ['艾伦', '·', '图灵', '传']

对于每本书我们都可以进行这样一个分词操作,并可以考虑将标点符号去除。 然后看每两个数组的元素的重合情况,但要考虑归一化,原因如下:

  1. ‘Python 开发指南’ 和 ‘皇家 Python 开发指南’ 元素的重合数为 3 个单词。
  2. ‘Python 开发指南’ 和 ‘皇家 Python 超级无敌开发指南’ 元素的重合数为 3 个单词。

显然第 1 组看起来会比第 2 组相似一些,但是重合数是一样的。使用重合数显然不科学,我们可以通过除以分词后单词总数来计算它们的重合率。

解决方案四:

在想解决方案三的时候,我回忆起了以前做聚类算法的时候用的各种距离算法,对于实数的欧几里得距离,用在 k-modes 中的对于类型数据的相异度量算法,前者在这里显然不适用,后者又显得有点小题大做。然后我发现了一种叫做编辑距离 Edit Distance 的算法,又称 Levenshtein 距离。[1]

编辑距离是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。 维基上给出了一个例子: 'kitten' 和 'sitting' 的编辑距离为3:

  1. kitten → sitten (s 替换成 k)
  2. sitten → sittin (i 替换成 e)
  3. sittin → sitting (加上一个 g)

碰巧的是有一个开源的 python 包刚好可以计算 Levenshtein 距离,可以通过以下安装:

pip install python-Levenshtein

然后就可以计算编辑距离了:

import Levenshtein
texta = '艾伦·图灵传'
textb = '艾伦•图灵传'
print Levenshtein.distance(texta,textb)
# 3

很自然地会有这个疑问,为什么这里的编辑距离会是3,我们来看看具体进行的编辑操作。

Levenshtein.editops('艾伦·图灵传', '艾伦•图灵传')
[('insert', 6, 6), ('replace', 6, 7), ('replace', 7, 8)]

看起来只有一个字符的区别,但是这里做了三次编辑操作,为什么?我们来打印一下这两个字符串的具体表达:

In [4]: a='艾伦·图灵传'
In [5]: a
Out[5]: '\xe8\x89\xbe\xe4\xbc\xa6\xc2\xb7\xe5\x9b\xbe\xe7\x81\xb5\xe4\xbc\xa0'
In [6]: a='艾'
In [7]: a
Out[7]: '\xe8\x89\xbe'

看到这里就明白了,我们犯了一个错误,把这两个字符串存成了 string 类型,而在 string 类型中,默认的 utf-8 编码下,一个中文字符是用三个字节来表示的。于是这里又牵扯到了 Python 中的 string 和 unicode 的区别。然而在这个字节串中,我们的编辑距离,的的确确是3。

现在重新来计算距离。

print Levenshtein.distance(u'艾伦·图灵传',u'艾伦•图灵传')
# 1

现在就正确了,现在的编辑距离1就代表了两本书的名字的差别度量。这时候就要开始做归一化了,而巧的是,我们发现这个 Levenshtein 包中自带了一个相似度函数 jaro(),它可以接受两个字符串并给出从0到1范围内的相似度。下面所显示的0.888888888889便表示了这两个字符串的相似度。

print Levenshtein.jaro(u'艾伦·图灵传',u'艾伦•图灵传')
# 0.888888888889

然后我们可以写个嵌套的 for 循环,设置一个阈值 threshold,计算每一对书本的相似度,当他们超过某一阈值时,进行处理。这里要注意的就是,不要重复检验书本对,即检测(书本 A,书本 B)和(书本 B,书本 A),这样可以避免 N^2 的时间复杂度。


当然最后其实还是需要人工介入的,比如遇到第三本书这种情况,版次不一样并不算同一本书。再当然,我们是可以对所有版次有关的字符进行特殊处理,比如出现版次字符,而且两本书不相同的时候,把距离函数的输出值加上一个修正参数。不过,这种情况太多太多,还是用人工处理好了,此时的两本书为同一本书的机率是很高的。再不然,只能上机器学习了,但是也是要你有训练样本的。

参考文献: [1] wikipedia Levenshtein_distance [2] Levenshtein Document

© 著作权归作者所有

共有 人打赏支持
xh4n3
粉丝 14
博文 28
码字总数 16847
作品 0
杭州
程序员
Scala的字符串相似性度量算法库--stringmetric

stringmetric是Scala的字符串相似性度量算法的库。(如:Dice/Sorensen, Hamming, Jaccard, Jaro, Jaro-Winkler, Levenshtein, Metaphone, N-Gram, NYSIIS, Overlap, Ratcliff/Obershelp, R......

匿名
2016/03/21
269
0
ElasticSearch fuzziness 模糊查询

[[模糊]] === 模糊 模糊匹配 视两个单词 模糊'' 相似,正好像它们是同一个词. ((("typoes and misspellings", "fuzziness, defining"))) 首先, 我们需要通过fuzziness 来定义什么是((("fuzzi...

键走偏锋
2017/08/01
0
0
计算两个字符串相(或句子)似度的方法

主要方法有:编辑距离、余弦相似度、模糊相似度百分比 1 编辑距离 编辑距离(Levenshtein距离)详解(附python实现) 使用Python计算文本相似性之编辑距离 2 余弦相似度 余弦计算相似度度量 ...

致Great
05/11
0
0
人们讨厌你开源文档的13个原因

【IT168 评论】大多数程序员都非常在乎他们开发的软件的质量,但是他们却很少关注相应的文档。虽然很少有人提及这一点,但是一份好的文档确实能为你的软件的成功插上翅膀。没有好文档,大家不...

开源中国社区
2013/01/16
0
0
计算字符串相似度算法——Levenshtein

1.百度百科介绍: Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。 许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除...

SomaLihq
06/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Shell编程(expect同步文件、指定host和同步文件、构建文件分发系统、批量执行命令)

expect脚本同步文件 需求:自动同步文件 实验准备: A机器:192.168.248.130 B机器:192.168.248.129 实现: 1.A机器编写4.expect脚本文件,内容如下所示: #!/usr/bin/expectset passwd "...

蛋黄_Yolks
36分钟前
2
0
ppwjs之bootstrap颜色:背景颜色

<!DOCTYPT html><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><title>ppwjs欢迎您</title><link rel="icon" href="/favicon.ico" ......

ppwjs
37分钟前
1
0
Ubuntu与 Fedora之对比

大家好。今天我将重点介绍两个流行的Linux发行版之间的一些特性和差异; Ubuntu 18.04和Fedora 28。它们都有自己的包管理; Ubuntu使用DEB,而Fedora使用RPM,但它们都具有相同的桌面环境(GNO...

linuxprobe16
40分钟前
2
0
线性代数入门

线性代数的概念对于理解机器学习背后的原理非常重要,尤其是在深度学习领域中。它可以帮助我们更好地理解算法内部到底是怎么运行的,借此,我们就能够更好的做出决策。所以,如果你真的希望了...

牛奋Debug
昨天
3
0
开发5分钟,调试2小时 - 该如何debug?

几年来我在答疑群、论坛、公众号、知乎回答的各种问题,没有一万也有八千。其中有三分之二以上都是在帮人看报错,帮人 debug(调试代码)。 可以说,会不会 debug,有没有 debug 的意识,懂不...

crossin
昨天
4
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部