3

我正在寻找可以让我识别出两个名字是同一个人的 gem 或项目。例如

J.R. Smith == John R. Smith == John Smith == John Roy Smith == Johnny Smith

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

4

8 回答 8

4

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

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

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

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

仍然有很多漏洞,但我认为在这种情况下,每个人都可以做的最好的就是给你替代品让你测试,看看什么最有效

于 2011-01-19T04:36:02.427 回答
4

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

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

names = Namae.parse('J.R. Smith and John R. Smith and John Smith and John Roy Smith and Johnny Smith ')
names.map { |n| [n.given, n.family] }
#=> => [["J.R.", "Smith"], ["John R.", "Smith"], ["John", "Smith"], ["John Roy", "Smith"], ["Johnny", "Smith"]]
names.map { |n| n.initials expand: true }
#=> ["J.R. Smith", "J.R. Smith", "J. Smith", "J.R. Smith", "J. Smith"]
于 2013-06-17T14:15:00.993 回答
3

就像是:

1:将名称转换为数组:

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

2:身份的一些功能:

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,该函数确定名称相同的部分。


升级版:

aim = 'Rob Bobbie Johnson'
candidates = [
    "Bob Robbie John",
    "Bobbie J. Roberto",
    "R.J.B.",
]

$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, "R.J.B."]            # 0.5 + 0.5 + 0.5
于 2011-01-19T04:45:15.037 回答
2

对于必须尝试匹配来自不同数据源的人名的任何人来说,这是一个非常难以解决的问题。使用 3 颗宝石的组合似乎效果很好。

我们有一个应用程序,列表 A 中有 100 万人,需要将他们与数十个不同的数据源进行匹配。(尽管一些更迂腐的评论声称,这并不是一个“设计缺陷”,而是处理“现实世界”混乱数据的本质。)

到目前为止,我们发现工作得相当好的唯一方法是使用namaegem 的组合(用于将名称解析为标准化的 first、middle、last、suffix 表示)和textgem 来计算 levenshtein、soundex、metaphone 和 porter 分数,并且还fuzzy-string-match计算 JaroWinkler 分数(这通常是最好的)。

  1. 解析成使用 namae 分隔最后、第一、中间、后缀的标准格式。我们使用正则表达式进行预处理以在格式化时提取昵称John "JJ" DoeSamuel (Sammy) Smith
  2. 计算全名的净化版本的所有分数(全部大写,删除标点符号,姓氏在前)...... jarowinkler,soundex,levenshtein,metaphone,white,porter。(JaroWinkler 和 Soundex 通常做得最好。)
  3. 如果 N 分数超过单独设置的阈值,则声明匹配。(我们使用任何通过的2个作为通过)
  4. 如果不匹配,则仅使用姓氏、名字、中间名首字母、具有更高阈值(例如,更严格的匹配)再试一次。
  5. 仍然不匹配,将名字替换为昵称(如果有),然后重试。

通过对每种评分方法的分数阈值进行一些调整,我们得到了相当不错的结果。YMMV。

顺便说一句,将姓氏放在首位非常重要,至少对于 JaroWinkler 而言,因为姓氏的变化通常较小(Smithe 几乎总是 Smithe,但在不同的数据源中名字可能是 Tom 或 Tommy 或 Thomas),并且字符串在 JaroWinkler 中最“敏感”。对于“ROB SMITHE / ROBIN SMITHE,如果你先写名字,则 JaroWinkler 距离为 0.91,如果你先写姓氏,则为 0.99。

于 2019-02-20T07:53:15.653 回答
1

您可能会为此找到的最好的预编码是刚刚称为“文本”的宝石。

https://github.com/threedaymonk/text

它有许多匹配算法:Levenshtein Distance、Metaphone、Soundex 等等。

于 2013-12-28T02:07:36.853 回答
0

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

我无意冒犯,但这个问题似乎是由糟糕的设计引起的。也许如果您发布有关您要解决的一般问题的更多详细信息,人们可以提出更好的方法。

于 2011-01-19T04:32:55.533 回答
0

Ruby 有一个非常好的 gem text,我自己也发现Text::WhiteSimilarity它非常好,但它还实现了许多其他测试

于 2013-07-16T17:06:43.917 回答
0

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

于 2016-12-12T02:35:22.197 回答