質問

2 つの名前が同一人物であることを識別できる宝石またはプロジェクトを探しています。例えば

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

ご理解いただけると思います。100% 正確なものは何もないことはわかっていますが、少なくとも大部分のケースを処理できるものを入手したいと考えています。おそらく最後のものにはニックネームのデータベースが必要になることはわかっています。

役に立ちましたか?

解決

私は1つの選択肢がルビーの実装を使用することだと思います levenshtein距離

2つの文字列間のLevenshtein距離は、1つの文字列を他の文字列に変換するために必要な編集の最小数として定義され、許容編集操作は挿入、削除、または単一の文字の置換です。

その後、x未満の距離でその名前を定義することができます(xを調整する必要があるx xである)は、同じ人からです。

編集ちょっとした検索を通して、私は呼ばれる音声学に基づいて別のアルゴリズムを見つけることができました メタホン

まだたくさんの穴がありますが、この場合、誰もができる最善のことは、あなたがテストして何が最善かを見るための代替手段を提供することだと思います

他のヒント

これは少し遅れています(そして起動するのは恥知らずなプラグです)が、それが価値があることのために、私は書いた 人名パーサー 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"]

何かのようなもの:

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 決定するいくつかのカスタム関数を適用することは、同じ名前の一部に同一のものです。


UPD:

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

異なるデータソースから人間名を一致させようとする必要がある人にとっては、これは対処するのが非常に難しい問題です。を使って 組み合わせ 3つの宝石のうち、かなりうまくいくようです。

リストAに100万人の人々がいるアプリケーションがあり、それらを多数の異なるデータソースと一致させる必要があります。 (そして、より多くのPedanticコメントが主張するものにもかかわらず、それは「現実世界」の乱雑なデータを扱う性質である「デザインの欠陥」ではありません。)

私たちがこれまでに適度にうまく機能することがわかった唯一のことは、の組み合わせを使用することです namae gem(名前を最初に、中央、最後、接尾辞表現に標準化するために)および text levenshtein、soundex、メタホン、ポーターのスコアを計算するgem、および fuzzy-string-match Jarowinklerスコア(多くの場合、ロットの中で最高です)を計算します。

  1. namaeを使用して、最後、最初、中央、接尾辞を分離する標準形式に解析します。フォーマットされたときにニックネームを抽出するためにregexで前処理します John "JJ" Doe また Samuel (Sammy) Smith
  2. フルネームのサニタイズバージョンのすべてのスコアを計算します(すべてのキャップ、句読点を削除し、姓を最初に削除)... Jarowinkler、Soundex、Levenshtein、メタホン、ホワイト、ポーター。 (JarowinklerとSoundexはしばしば最善を尽くします。)
  3. nスコアが個別に設定されたしきい値を超えた場合、一致を宣言します。 (パスとしてパスする2つを使用します)
  4. 一致しない場合は、姓、名、中央のみを使用して再試行してください イニシャル, 、より高いしきい値(例えば、より厳しい一致)。
  5. それでも一致しないで、ファーストネームをニックネーム(ある場合)に置き換えて、再試行してください。

スコアリング方法ごとにスコアしきい値を微調整すると、かなり良い結果が得られます。 ymmv。

ところで、姓を最初に入れることは非常に重要です。少なくともJarowinklerにとっては、一般的に姓の変動が少ないためです(ほとんどの場合、SmitheはSmitheですが、異なるデータソースではTomまたはTommyまたはThomasである可能性があります)、そして文字列はJarowinklerで最も「敏感」です。 「Rob Smithe / Robin Smitheの場合、最初の名前を最初に行う場合はJarowinkler距離が0.91ですが、最初に姓を作成する場合は0.99です。

おそらくあなたが見つける最高の事前にコードされたものは、「テキスト」と呼ばれる宝石です。

https://github.com/threedaymonk/text

Levenshtein距離、メタホン、Soundexなど、一致するアルゴリズムが多数あります。

そんな図書館は存在しないと思います。

気分を害するつもりはありませんが、この問題は設計が不十分なために発生しているように思えます。おそらく、解決しようとしている一般的な問題についてさらに詳しく投稿すると、より良い方法を提案してくれるでしょう。

ルビーにはとても素敵な宝石があります text そして、私はそれを見つけました Text::WhiteSimilarity 自分で非常に良いことですが、他のテストも多くのテストを実装しています

Rubyの堅牢なヒューマンネームマッチャー /クラスタリングソリューションでの最初の試み: https://github.com/adrianomitre/match_author_names

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top