Разбор человеческих имен и сопоставление их в Ruby - PullRequest
3 голосов
/ 19 января 2011

Я ищу камень или проект, который позволил бы мне определить, что два имени - это одно и то же лицо. Например

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

Я думаю, вы поняли идею. Я знаю, что ничто не будет на 100% точным, но я хотел бы получить что-то, что, по крайней мере, обрабатывает большинство случаев. Я знаю, что последнему, вероятно, понадобится база данных псевдонимов.

Ответы [ 8 ]

4 голосов
/ 17 июня 2013

Это немного поздно (и бесстыдный плагин для загрузки), но для этого я написал парсер имен во время проекта 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"]
4 голосов
/ 19 января 2011

Я думаю, что одним из вариантов было бы использование рубиновой реализации расстояния Левенштейна

Расстояние Левенштейна между двумя строками определяется как минимальное количество правок, необходимых для преобразования одной строки в другую, при этом допустимыми операциями редактирования являются вставка, удаление или замена одного символа.

Тогда вы можете определить, что имена с расстоянием, меньшим X (если X - это число, которое вам нужно настроить), принадлежат одному и тому же человеку.

EDIT Путем небольшого поиска я смог найти другой алгоритм, основанный на фонетике под названием Метафон

В ней все еще много дыр, но я думаю, что в этом случае лучшее, что может сделать каждый, - это дать вам альтернативы для тестирования и посмотреть, что работает лучше

3 голосов
/ 19 января 2011

Что-то вроде:

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
2 голосов
/ 20 февраля 2019

Для тех, кто пытается сопоставить имена людей из разных источников данных, это ОЧЕНЬ сложная проблема.Использование комбинации из 3 драгоценных камней, кажется, работает очень хорошо.

У нас есть приложение, в котором миллион людей в списке А, и нам нужно сопоставить их с десятками различных источников данных.(И, несмотря на то, что утверждают некоторые из более педантичных комментариев, это не «недостаток дизайна», а природа работы с грязными данными «реального мира».)

Единственное, что мы нашли, чтобы работать разумнодо сих пор используется комбинация гема namae (для разбора имен в стандартизированное представление первого, среднего, последнего, суффикса) и гема text для вычисления баллов levenshtein, soundex, metaphone и porter, а также fuzzy-string-match, который вычисляет счет JaroWinkler (который часто является лучшим из лота).

  1. разбирается в стандартный формат, разделяющий последний, первый, средний суффикс с использованием namae.Мы предварительно обрабатываем регулярное выражение для извлечения псевдонимов при форматировании John "JJ" Doe или Samuel (Sammy) Smith
  2. , рассчитываем ВСЕ баллы для продезинфицированной версии полного имени (все заглавные буквы, удаляем пунктуацию, сначала фамилию) ...Яровинклер, Соундекс, Левенштейн, Метафон, Белый, Портер.(JaroWinkler и Soundex часто делают все возможное.)
  3. объявляет совпадение, если N баллов превышает индивидуально установленные пороги.(Мы используем любые 2, которые передаются в качестве пропуска)
  4. , если не найдено совпадений, попробуйте еще раз, используя только фамилию, имя, отчество начальный , с более высокими порогами (например, более строгое сопоставление).
  5. По-прежнему нет совпадений, замените имя на псевдоним (если есть) и повторите попытку.

С некоторыми изменениями пороговых значений для каждого метода оценки мы получаем довольно хорошие результаты.YMMV.

Кстати, очень важно ввести фамилию в первую очередь, по крайней мере, для JaroWinkler, поскольку в общем случае фамилии менее различны (Smithe почти всегда Smithe, но в разных данных имя может быть Tom или Tommy или Thomasисточники), и начало строки является наиболее «чувствительным» в JaroWinkler.Для "ROB SMITHE / ROBIN SMITHE" расстояние JaroWinkler составляет 0,91, если вы сначала делаете имя первым, и 0,99, если вы делаете фамилию первым.

1 голос
/ 28 декабря 2013

Наилучшим предкодированным символом, который вы, вероятно, найдете для этого, является самоцвет, называемый просто «текст».

https://github.com/threedaymonk/text

Имеет ряд подходящих алгоритмов: Расстояние Левенштейна, Метафон, Soundex и др.

0 голосов
/ 12 декабря 2016

Одна первоначальная попытка создания надежного решения для сопоставления имен людей / кластеризации в Ruby: https://github.com/adrianomitre/match_author_names

0 голосов
/ 16 июля 2013

В Ruby есть очень хороший гем под названием text, и я обнаружил, что Text::WhiteSimilarity тоже очень хорош, но он также реализует кучу других тестов

0 голосов
/ 19 января 2011

Я не думаю, что такая библиотека существует.

Я не хочу обидеть, но эта проблема, кажется, возникает из-за плохого дизайна.Возможно, если вы опубликуете более подробную информацию об общей проблеме, которую вы пытаетесь решить, люди могут предложить лучший способ.

...