Разбор человеческих имен и сопоставление их в Ruby

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

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

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


person hadees    schedule 19.01.2011    source источник
comment
Как узнать, что это один и тот же человек?   -  person Karl Knechtel    schedule 19.01.2011
comment
Люди, которые хотят, чтобы их идентифицировали, будут использовать довольно последовательное написание своего имени. Те, кто не хочет, чтобы их идентифицировали, будут совершенно разными, и никакой алгоритм не поймает их, используя только строку имени. Вам также нужно будет сопоставить адреса, номера телефонов, почтовые индексы, номера кредитных карт, адреса электронной почты или все, что у вас есть, что может однозначно идентифицировать их. Также учтите, что J. может быть Джон, Джеймс, Джерри или любое другое написание, например Джон.   -  person the Tin Man    schedule 19.01.2011
comment
Я не знаю на 100%, что они одинаковы, но я делаю это в контексте руководителей компании, поэтому по большей части я думаю, что могу быть относительно уверен, что это один и тот же человек. Обычно я вижу только одну вариацию имен, и у меня есть другие способы дедупликации. Мне просто нужно знать с человеческой точки зрения, совпадают ли имена.   -  person hadees    schedule 19.01.2011
comment
Не используйте имена в качестве идентификаторов. Используйте другое поле; например, номера социального страхования или даже идентификаторы баз данных.   -  person kikito    schedule 19.01.2011
comment
У меня нет ничего, кроме имени и должности, но должности, кажется, тоже немного различаются.   -  person hadees    schedule 19.01.2011
comment
Попробуйте это, братан: https://github.com/mericson/people. Это может помочь.   -  person mjnissim    schedule 25.08.2012
comment
Обнаружил это при попытке решить эту же проблему: w3.org/International/questions/ qa-personal-names Это показывает, насколько сложно найти правила для синтаксического анализа имен. Тем не менее попытки это сделать есть. Гугл назвал драгоценный камень синтаксического анализа, чтобы найти пару. mjnissim указывает на одного здесь. Ответ @inukshuk - другой. Удачи!   -  person nandilugio    schedule 07.02.2017


Ответы (8)


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

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

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

РЕДАКТИРОВАТЬ Путем небольшого поиска мне удалось найти другой алгоритм, основанный на фонетике, под названием Метафон

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

person nunopolonia    schedule 19.01.2011
comment
Я не согласен с тем, что расстояние Левенштейна - лучший способ смоделировать это. Афанасиос Теодору, вероятно, тот же человек, что и Том Теодору, тогда как К. Смит определенно не то же лицо, что и М. Смит. К сожалению, за этот ответ я должен дать вам -1. - person Brennan Vincent; 19.01.2011
comment
Я согласен с вами, но это способ получить хоть какие-то результаты. Это очень сложный процесс, если не сказать невозможный. - person nunopolonia; 19.01.2011
comment
Затем вы должны упомянуть это в своем ответе и удалить бит о том, что это лучший и единственный вариант, и в этом случае я удалю свой голос против. - person Brennan Vincent; 19.01.2011
comment
Основываясь на комментарии @hadees, я привожу данные о компаниях из разных источников, и имена руководителей иногда немного различаются, я думаю, что использование расстояния Левенштейна может сработать, поскольку задача сильно сужается в зависимости от компании. - person the Tin Man; 19.01.2011
comment
Как я сказал в своей редакции, лучшее, что мы можем сделать, - это предложить методы, которые могут помочь. Никакого нестандартного решения не будет. - person nunopolonia; 19.01.2011
comment
Определенно есть место для проекта сопоставления имен Ruby, который представляет собой не просто расстояние Левенштейна (например, интеллектуальный анализ данных, онлайн-ответ на свадьбу и т. Д.), Но как минимум вы можете сделать точное совпадение по фамилии плюс Левенштейн для всего, что ему предшествует. Будьте готовы к иногда неожиданным результатам. - person Matthew Ratzloff; 21.04.2012

Это немного поздно (и бессовестная затычка для загрузки), но, чего бы это ни стоило, я написал парсер человеческих имен во время проекта 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"]
person inukshuk    schedule 17.06.2013

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

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
person Nakilon    schedule 19.01.2011
comment
строки должны быть загнуты в нижний регистр, чтобы увеличить целевой размер, а затем преобразованы обратно в исходный регистр имени после сопоставления. - person the Tin Man; 19.01.2011

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

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

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

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

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

Кстати, указание фамилии на первом месте очень важно, по крайней мере для JaroWinkler, поскольку обычно меньше вариаций в фамилиях (Смит почти всегда Смит, но имя может быть Том или Томми или Томас в разных источниках данных), и начало строка является наиболее "чувствительной" в JaroWinkler. Для "ROB SMITHE / ROBIN SMITHE" расстояние ДжароВинклера составляет 0,91, если вы сначала указываете имя, и 0,99, если вы сначала указываете фамилию.

person jpw    schedule 20.02.2019
comment
Я считаю, что нечеткое совпадение строк излишне, поскольку гем DidYouMean (который, как я считаю, является частью публикации стандартной библиотеки Ruby Ruby 2.3) содержит реализацию JaroWinkler: github.com/yuki24/did_you_mean/blob/ - person Kevin Trowbridge; 05.04.2019

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

https://github.com/threedaymonk/text

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

person Lonny Eachus    schedule 28.12.2013

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

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

person Brennan Vincent    schedule 19.01.2011
comment
Это не проблема дизайна. Я привожу данные о компаниях из разных источников, и имена руководителей иногда немного различаются. - person hadees; 19.01.2011
comment
@hadees: В таком случае перед вами стоит серьезная проблема. Вам, вероятно, просто придется немного подумать и написать для этого свою собственную функцию. - person Brennan Vincent; 19.01.2011
comment
Да, я вроде как подумал, но надеялся, что кто-то уже это сделал. Я нашел базу данных, в которой есть псевдонимы, так что это должно помочь. - person hadees; 19.01.2011

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

person Adam    schedule 16.07.2013

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

person Adriano Mitre    schedule 12.12.2016