您最好使用哪种算法来实现字符串相似性?


23

我正在设计一个插件,用于根据地址唯一标识各种网页上的内容。

所以我可能有一个看起来像的地址:

1 someawesome street, anytown, F100 211

稍后我可能会发现此地址的格式略有不同。

1 someawesome street, F100 211,

或像

someawesome street F100

这些在技术上是相同的地址,但是具有相似的水平。我想a)为每个地址生成一个唯一的标识符以执行查找,b)找出何时出现非常相似的地址。

我应该查看哪些算法/技术/字符串指标?Levenshtein距离似乎是一个显而易见的选择,但对是否还有其他方法可以在这里使用感到好奇。


“ Levenshtein距离”不是一种算法。
gnasher729

除非您进行一些基本的解析,否则原始的Levenstein距离就不会那么好。您应该尝试至少识别可能是街道,城镇名称等的单词以及可能是街道编号或邮政编码的单词。然后,可以使用由真实地点/街道名称提供的一些统计模糊匹配器,对这些应用Levenstein。这不是一件容易的事:)

7
@gnasher:但是计算Levenshtein距离的函数一种算法。没有这样的功能,Levenshtein距离仅仅是出于好奇。
罗伯特·哈维

我通过以下示例找到了一个非常实用的解释:algortihms的比较。总之,他们建议使用Jaro-Winkler相似度,因为Levenstein的算法取决于字符串的长度,因此进行比较没有用。
桑德拉·梅内塞斯

Answers:


14

Levenstein的算法基于字符串中插入,删除和替换的次数。

不幸的是,它没有考虑常见的拼写错误,即2个字符的转换(例如,令人敬畏的vs令人敬畏的)。因此,我希望使用更鲁棒的Damerau-Levenstein算法

我认为将距离应用到整个弦上不是一个好主意,因为时间会随着弦的长度而突然增加。但是更糟糕的是,当删除ZIP之类的地址组成部分时,完全不同的地址可能会更好地匹配(使用在线Levenshtein计算器衡量):

1 someawesome street, anytown, F100 211       (reference) 
1 someawesome st.,anytown                     (difference of 15, same address)     
1 otherplaces street,anytown,F100211          (difference of 13, different ddress) 
1 sameawesome street, othertown, CA98200      (difference of 13, different ddress)
anytown, 1 someawesome street                 (28 different same address)
anytown, F100 211, 1 someawesome street       (37 different same address)

对于较短的街道名称,这些影响趋于恶化。

因此,您最好使用更智能的算法。例如,Arthur Ra​​tz在CodeProject上发布了一种用于智能文本比较的算法。该算法不会打印出距离(当然可以相应地进行丰富),但是它可以识别一些困难的事情,例如移动文本块(例如,第一个示例和最后一个示例之间的城镇和街道之间的交换)。

如果这种算法对于您的情况而言过于笼统,则应按组件实际工作,并仅比较可比较的组件。如果要解析世界上的任何地址格式,这都不容易。但是美国说,如果目标更加具体,那肯定是可行的。例如,“街道”,“街”,“地方”,“广场”及其通常的拼写错误可能会揭示地址的街道部分,原则上,其前导部分将是数字。邮政编码可以帮助您定位城镇,或者它可能是地址的最后一个元素,或者,如果您不喜欢猜测,则可以查找城市名称列表(例如,下载免费的邮政编码数据库)。然后,您可以仅在相关组件上应用Damerau-Levenshtein。


在比较之前对两个比较字符串进行排序怎么办?我发现这可以帮助换位。
openwonk

2

Levenshtein距离更适合单词

如果单词(主要是)拼写正确,则查看单词袋。我看起来似乎已经死了,但是TF-IDF余弦相似度

或者,您可以使用免费的Lucene。我认为它们确实存在余弦相似性。


1

首先,您必须解析网页中的地址,RegEx是写来的,但是使用RegEx解析地址可能非常困难。您可能最终不得不浏览一系列潜在的寻址格式以及与之匹配的出色一个或多个表达式。我对地址解析不太熟悉,但是我建议您看一下这个问题,该问题遵循类似的思路:自由格式文本的通用地址解析器。

Levenshtein距离很有用,但只有在您将地址分成几个部分之后才可以使用。考虑以下地址。123 someawesome st.并且124 someawesome st.这些地址的位置完全不同,但是它们的Levenshtein距离仅为1。这也可以应用于类似8th st.9th st.类似的街道名称通常不会出现在同一网页上,但并非闻所未闻。例如,学校的网页上可能有马路对面的图书馆地址,或者教堂只有几步之遥。这意味着Levenshtein距离唯一易于使用的数据是2个数据点之间的距离,例如街道与城市之间的距离。

至于弄清楚如何分隔不同的字段,一旦我们获得地址本身就很简单。值得庆幸的是,大多数地址都采用非常特定的格式,并通过一些RegEx向导可以将它们分为不同的数据字段。即使地址的格式不正确,仍然存在一些希望。地址始终(几乎)遵循数量级。您的地址应位于这样的线性网格中,具体取决于提供的信息量及其含义:

StreetNumber < Street < City < State < Country

如果地址从一个字段跳到一个不相邻的字段,这种情况很少发生。您不会经常看到街道,然后是国家,或街道号码,然后是城市。


2
除非街道地址不是正则,并且不能用正则表达式可靠地解析。如果仅将它们嵌入自由文本中,则肯定无法准确识别它们。当然,如果您已经知道要查找的位置,则可以编写一些不同的正则表达式来匹配不同的常用格式。
没用

@没用是的。从理论上讲这是可行的,但我低估了投入其中的工作量。尤其是在存在更好的选择时。我已经修改了答案以反映这一点。
Ucenna

1

您询问有关字符串相似性算法的信息,但您的字符串是地址。我会将地址提交给诸如Google Place Search之类的位置API,并将其formatted_address用作比较点。这似乎是最准确的方法。

对于无法通过API找到的地址字符串,则可以使用相似性算法。


1
+1外包,以便您获得专家​​的力量为您完成工作。不必一定是Google,因为那里有一些服务提供商。除非地址匹配是您的核心业务,否则不要浪费时间。
LoztInSpace

0

一种有用的但很酷的算法,但需要预先获得答案的预置数据库,称为:线编辑距离。

作为功​​能,行编辑距离可以返回“这两个词有多少不同”。

像“教条”和“狗”这样的词,您将获得3的值(表示3个额外的字符)。

或“猫”和“帽子”,取回值1(对于一个不同的字符)。

(来源:https : //en.wikipedia.org/wiki/Edit_distance


2
相对于OP提到的Levensthtein有什么优势?
Christophe

-1

确实,使用一些距离函数似乎是一个好方法。但是问题是要从给定的地址中找到最接近的字符串,而这并非易事。

您将在此处描述各种算法。查看最近邻居搜索

如评论中所述,如果您找到一种方法来分离地址的组成部分(街道名称,数字等),它将使任务变得更加容易。


-1

LongestCommonSubsequence(来自Apache commons-text)可以是尝试使用地址的另一种方法。如果您将“ 2”的相似性定义为“ 公共子序列长度/最大(地址长度) ”的比率,则可以应用公差阈值-例如0.8,它将定义匹配/不匹配。这样,您可以匹配“ 1 someawesome st。,anytown ”和“ 1 someawesome street。,anytown ”等地址。

它不是超快速算法,因此您可能需要应用快速故障回复以最大程度地减少比较。例如-如果邮政编码不匹配或提取的仅数字顺序不同,则避免比较。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.