2011-01-19 2 views
3

나는 두 사람의 이름이 같은 사람이라는 것을 알려주는 보석이나 프로젝트를 찾고 있습니다. 인간 이름을 파싱하여 루비에서 일치시키기

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

예를

들어 난 당신이 생각을 생각합니다. 아무 것도 100 % 정확하지는 않을 것이라는 것을 알고 있지만 적어도 대다수의 경우를 처리하는 것을 얻고 싶습니다. 마지막으로 닉네임 데이터베이스가 필요할 것입니다.

+3

어떻게 이런이 같은 사람임을 알 수 있습니까? –

+0

신원을 밝히기를 원하는 인간은 이름의 일관된 철자법을 사용합니다. 신원을 밝히지 않으려는 사람은 크게 다르며 알고리즘은 이름 문자열 만 사용하여이를 포착하지 않습니다. 주소, 전화 번호, 우편 번호, 신용 카드 번호, 이메일 주소 또는 고유하게 식별 할 수있는 다른 주소도 일치시켜야합니다. 또한, "J." "John", "James", "Jerry"또는 "Jon"과 같은 다른 철자가 될 수 있습니다. –

+0

나는 그것들이 동일하지만 100 % 회사 경영진의 맥락에서 이것을하고있는 것을 모른다. 그래서 대부분 나는 그들이 상대적으로 똑같을 수 있다고 생각한다. 일반적으로 볼 수있는 이름에는 한 가지 변형 만 존재하며 다른 방법으로 삭제할 수 있습니다. 이름이 일치하는 경우 인간의 관점에서 알 필요가 있습니다. – hadees

답변

4

나는 하나의 옵션이 허용 편집 작업으로, 두 문자열 사이의 Levenshtein 거리가 다른에 하나의 문자열을 변환하는 데 필요한 편집의 최소 개수로 정의된다 Levenshtein distance

의 루비 구현을 사용하는 것입니다 생각 단일 문자의 삽입, 삭제 또는 대체입니다.

그러면 거리가 X보다 작은 이름 (X는 조정해야하는 숫자 임)은 같은 사람으로부터 정의 할 수 있습니다.

편집 내가 아직 거기에 구멍을 많이 가지고 있지만이 경우 가장 좋은 사람이 할 수 있다고 생각 Metaphone

라고 발음 기호에 따라 다른 알고리즘을 찾을 수 있었다 약간의 검색을 통해

1 : 테스트하고 가장 좋아

+1

필자는 Levenshtein 거리가 이것을 모델링하는 가장 좋은 방법이라고 동의하지 않습니다. "Athanasios Theodorou"는 "Tom Theodorou"와 같은 사람인 반면, "K. Smith"는 확실히 "M. Smith"와 같은 사람이 아닙니다. 불행히도이 대답에 -1을 주어야합니다. –

+0

나는 당신과 동의한다, 그러나 적어도 몇몇 결과 권리를 얻기의 방법이다. 이것은 매우 복잡한 과정이며, 불가능하다고 말하지 않습니다. – nunopolonia

+0

그럼 당신은 당신의 대답에 언급해야하고 "최고 ** ** 옵션"에 관한 비트를 제거해야합니다.이 경우에는 내 downvote를 제거 할 것입니다. –

1

나는 그런 도서관이 존재하지 않는다고 생각한다.

불쾌감을주는 것이 아니지만 잘못된 디자인으로 인해 발생하는 것 같습니다. 어쩌면 해결하려는 일반적인 문제에 대한 자세한 내용을 게시하면 사람들은 더 나은 방법을 제안 할 수 있습니다.

+0

디자인에 문제가 없습니다. 회사에 대한 여러 출처에서 데이터를 가져오고 있으며 임원 명칭이 약간 다를 수 있습니다. – hadees

+0

@hadees :이 경우 손에 힘든 문제가 있습니다. 아마도 잠시 생각하고 코드를 작성하는 기능을 코딩해야합니다. –

+0

그래, 나는 그런 생각을 해봤지만 누군가가 이미 해보았 으면 좋겠다. 닉네임이있는 데이터베이스를 찾았으므로 도움이됩니다. – hadees

3

뭔가를 작동하는 것을 볼 수 있도록 당신에게 대안을 제공하는 것입니다 배열에 이름을 변환 :

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

2 : 정체성의 일부 기능 : 동일한 이름의 부분이 .permutation + .zip + .max 결정 일부 사용자 지정 기능을 적용하는 데 사용할 수있는

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"]] 

또는 대신 &의이다.


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 
+1

문자열을 대상 크기를 늘리기 위해 소문자로 접은 다음 일치 후 원래 이름 케이스로 다시 매핑해야합니다. –

+0

@ 틴 맨, 완료) – Nakilon

4

이 조금 늦게 (그리고 부팅 할 뻔뻔한 플러그)이지만, 가치가 무엇을, 나는 GSoC 프로젝트 중 human name parser을 썼다 이 파일은 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"] 
0

루비는 매우 좋은 보석을 가지고 text이라고 불렀고 나는 Text::WhiteSimilarity이라는 것을 찾았습니다.하지만 다른 테스트도 많이 구현했습니다.

1

"텍스트"라고 불리는 보석이 가장 잘 미리 코딩되어 있습니다. Levenshtein 거리 메타 폰, Soundex와,보다 :

https://github.com/threedaymonk/text 

그것은 매칭 알고리즘들을 갖는다.