文档章节

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

xh4n3
 xh4n3
发布于 2015/08/18 17:18
字数 1507
阅读 2.5K
收藏 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
杭州
程序员
私信 提问
加载中
请先登录后再评论。
浅入浅出Android(003):使用TextView类构造文本控件

基础: TextView是无法供编辑的。 当我们新建一个项目MyTextView时候,默认的布局(/res/layout/activity_main.xml)中已经有了一个TextView: <TextView 运行效果如下: 修改其文本内容...

樂天
2014/03/22
688
1
SQLServer实现split分割字符串到列

网上已有人实现sqlserver的split函数可将字符串分割成行,但是我们习惯了split返回数组或者列表,因此这里对其做一些改动,最终实现也许不尽如意,但是也能解决一些问题。 先贴上某大牛写的s...

cwalet
2014/05/21
9.7K
0
CDH5: 使用parcels配置lzo

一、Parcel 部署步骤 1 下载: 首先需要下载 Parcel。下载完成后,Parcel 将驻留在 Cloudera Manager 主机的本地目录中。 2 分配: Parcel 下载后,将分配到群集中的所有主机上并解压缩。 3 激...

cloud-coder
2014/07/01
6.8K
1
beego API开发以及自动化文档

beego API开发以及自动化文档 beego1.3版本已经在上个星期发布了,但是还是有很多人不了解如何来进行开发,也是在一步一步的测试中开发,期间QQ群里面很多人都问我如何开发,我的业余时间实在...

astaxie
2014/06/25
2.7W
22
DAAttributedStringUtils

DAAttributedStringUtils 包含 NSAttributedStrings 的一些常用方法,包括: 使用类 printf 的格式化方法创建 NSAttributedString 实例 一个简单的 UI Label 类用来显示 NSAttributedString...

匿名
2013/02/18
231
0

没有更多内容

加载失败,请刷新页面

加载更多

Hacker News 简讯 2020-08-12

最后更新时间: 2020-08-12 00:01 Single Page Applications using Rust - (sheshbabu.com) 使用Rust的单页应用程序 得分:126 | 评论:68 The case for why Google should be regulated as a ......

FalconChen
8分钟前
0
0
在关系数据库中存储分层数据有哪些选择? [关闭]

问题: Good Overviews 良好的概述 Generally speaking, you're making a decision between fast read times (for example, nested set) or fast write times (adjacency list). 一般来说,您......

fyin1314
昨天
7
0
创建myBatis项目

1、简介 1.1、核心组件 SqlSessionFactoryBuilder(构造器):根据配置信息或者代码生成SqlSessionFactory SqlSessionFactory(工厂接口):依靠工厂来生成SqlSession(会话) SqlSession(会话): ...

执键走天涯
昨天
5
0
Tomcat集群带来的问题和解决思路

Tomcat集群 存在问题 解决方案 基于Redis+Cookie+Jackson+Filter的原生解决集群Session共享问题 使用Spring Session零侵入解决Session共享 单点登录实现 Redis构建Session服务器 使用Redis+C...

code-ortaerc
昨天
17
0
小福利

点击有福利 本文分享自微信公众号 - V5codings(gh_c1ec2d16ec93)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。...

V5codings
2019/11/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部