解析人名并在Ruby中匹配它们

我正在寻找一个gem或项目,让我发现两个名字是同一个人。 例如

JR Smith == John R. Smith == John Smith == John Roy Smith == Johnny Smith

我想你应该已经明白了。 我知道没有什么是100%准确的,但我想得到的东西至少可以处理大多数情况。 我知道最后一个可能需要一个昵称数据库。

我认为一种选择是使用Levenshtein距离的ruby实现

两个字符串之间的Levenshtein距离定义为将一个字符串转换为另一个字符串所需的最小编辑数,允许的编辑操作是单个字符的插入,删除或替换。

然后你可以定义距离小于X的名字(X是你必须调整的数字)来自同一个人。

编辑通过一点点搜索,我能够找到另一种算法,基于名为Metaphone的语音

它还有很多漏洞,但我认为在这种情况下,每个人都可以做的最好的事情就是为你提供替代方案来测试并看看什么效果最好

这有点晚了(并且是一个无耻的启动插件),但是为了它的价值,我在GSoC项目期间编写了一个人名解析器 ,你可以使用gem install namae namae gem install namae 。 它显然无法可靠地检测到您的重复项,但它可以帮助您完成此类任务。

例如,您可以解析示例中的名称并使用显示forms使用首字母来检测其首字母相同的名称,依此类推:

 names = Namae.parse('JR Smith and John R. Smith and John Smith and John Roy Smith and Johnny Smith ') names.map { |n| [n.given, n.family] } #=> => [["JR", "Smith"], ["John R.", "Smith"], ["John", "Smith"], ["John Roy", "Smith"], ["Johnny", "Smith"]] names.map { |n| n.initials expand: true } #=> ["JR Smith", "JR Smith", "J. Smith", "JR Smith", "J. Smith"] 

就像是:

1:将名称转换为数组:

 irb> names.map!{|n|n.scan(/[^\s.]+\.?/)} ["J.", "R.", "Smith"] ["John", "R.", "Smith"] ["John", "Smith"] ["John", "Roy", "Smith"] ["Johnny", "Smith"] 

2:身份的某些function:

 for a,b in names.combination(2) p [(a&b).size,a,b] end [2, ["J.", "R.", "Smith"], ["John", "R.", "Smith"]] [1, ["J.", "R.", "Smith"], ["John", "Smith"]] [1, ["J.", "R.", "Smith"], ["John", "Roy", "Smith"]] [1, ["J.", "R.", "Smith"], ["Johnny", "Smith"]] [2, ["John", "R.", "Smith"], ["John", "Smith"]] [2, ["John", "R.", "Smith"], ["John", "Roy", "Smith"]] [1, ["John", "R.", "Smith"], ["Johnny", "Smith"]] [2, ["John", "Smith"], ["John", "Roy", "Smith"]] [1, ["John", "Smith"], ["Johnny", "Smith"]] [1, ["John", "Roy", "Smith"], ["Johnny", "Smith"]] 

或者代替和&你可以使用.permutation + .zip + .max来应用一些自定义函数,它决定了相同名称的部分。


UPD:

 aim = 'Rob Bobbie Johnson' candidates = [ "Bob Robbie John", "Bobbie J. Roberto", "RJB", ] $synonyms = Hash[ [ ["bob",["bobbie"]], ["rob",["robbie","roberto"]], ] ] def prepare name name.scan(/[^\s.]+\.?/).map &:downcase end def mf a,b # magick function a.zip(b).map do |i,j| next 1 if i == j next 0.9 if $synonyms[i].to_a.include?(j) || $synonyms[j].to_a.include?(i) next 0.5 if i[/\.$/] && j.start_with?(i.chomp '.') next 0.5 if j[/\.$/] && i.start_with?(j.chomp '.') -10 # if some part of name appears to be different - # it's bad even if another two parts were good end.inject :+ end for c in candidates results = prepare(c).permutation.map do |per| [mf(prepare(aim),per),per] end p [results.transpose.first.max,c] end 

 [-8.2, "Bob Robbie John"] # 0.9 + 0.9 - 10 # Johnson != John # I think ..) [2.4, "Bobbie J. Roberto"] # 1 + 0.9 + 0.5 # Rob == Roberto, Bobbie == Bobbie, Johnson ~~ J. [1.5, "RJB"] # 0.5 + 0.5 + 0.5 

你可能会发现最好的预编码就是这个名为“text”的gem。

 https://github.com/threedaymonk/text 

它有许多匹配的算法:Levenshtein距离,Metaphone,Soundex等。

我不认为这样的图书馆存在。

我不是故意冒犯,但这个问题似乎源于糟糕的设计。 也许如果你发布关于你试图解决的一般问题的更多细节,人们可以建议一个更好的方法。

Ruby有一个非常漂亮的gem叫做text ,我发现Text::WhiteSimilarity非常好,但它也实现了一堆其他的测试

Ruby中一个强大的人名匹配器/集群解决方案的初步尝试: https : //github.com/adrianomitre/match_author_names