私は企業の大規模なデータベースを扱っています。
2つの商号の類似性を比較して、重複している可能性があるかどうかを確認できるようにしたいと思います。
以下は、重複する可能性が高いとテストする必要がある商号のリストです。これを行うための良い方法は何ですか?
ジョージワシントンミドルシュル ジョージワシントンスクール サンタフェイーストインク サンタフェイースト チョップ't Creative Salad Co Chop't Creative Salad Company Manny and Olga's Pizza Manny's&Olga's Pizza Ray'sヘルバーガートゥー レイズヘルバーガー エルソル エルソルデアメリカ オルニーシアターセンターフォージアーツ オルニーシアター 21Mラウンジ 21Mラウンジ ホリデイインホテルワシントン ホリデイインワシントン-ジョージタウン Residence Inn Washington、DC/Dupont Circle Residence Inn Marriott Dupont Circle Jimmy John's Gourmet Sandwiches Jimmy John's ワシントンDCのオムニショアハムホテル オムニショアハムホテル
私は最近、同様のタスクを実行しましたが、1つのセット内で重複を探すのではなく、データベース内の既存の名前と新しいデータを照合していました。名前の照合は実際にはよく研究されたタスクであり、一般的な文字列を照合するために考慮するものを超える多くの要因があります。
まず、論文「NamesGame」のプレイ方法:RaffoとLhuilleryによるさまざまなヒューリスティックを比較した特許検索をご覧になることをお勧めします。公開されているバージョンは ここ であり、a PDFは無料で入手できます ここ です。著者は、いくつかの異なるマッチングを比較して、素晴らしい要約を提供します戦略:彼らは、解析、マッチング、フィルタリングと呼ばれる3つの段階を検討します。
解析は、さまざまなクリーニング手法を適用することで構成されます。いくつかの例:
私の場合、すべての文字を小文字に折り、すべての句読点を空白に置き換え、アクセント付きの文字をアクセントのない対応する文字に置き換え、他のすべての特殊文字を削除し、リストに続く名前の最初と最後から法的管理用語を削除しました。
マッチングとは、解析された名前の比較です。これは、単純な文字列照合、編集距離、SoundexまたはMetaphone、名前を構成する単語のセットの比較、または文字のセットまたはn-gram(文字シーケンス)の比較である可能性があります。長さn)。 n-gramアプローチは、語順を無視するため、実際には名前に非常に適しています。「例の部門」と「例の部門」のようなものに大いに役立ちます。実際、 Jaccard index のような単純なものを使用してバイグラム(2グラム、文字ペア)を比較することは非常に効果的です。他のいくつかの提案とは対照的に、レーベンシュタイン距離は、名前の一致に関しては貧弱なアプローチの1つです。
私の場合、2つのステップでマッチングを行いました。最初に、解析された名前が等しいかどうかを比較し、次に残りのバイグラムのセットにJaccardインデックスを使用しました。名前のすべてのペアのすべてのJaccardインデックス値を実際に計算するのではなく、最初に、指定されたサイズの2つのセットのJaccardインデックスの可能な最大値に制限を設定し、その上限が十分に高い場合にのみJaccardインデックスを計算しました。潜在的に役立つ。ほとんどの名前のペアはまだ十分に類似していないため、一致しませんでしたが、比較の数が大幅に減少しました。
フィルタリングとは、補助データを使用して、解析および照合段階からの誤検知を拒否することです。簡単なバージョンは、一致する名前がさまざまな都市の企業、つまりさまざまな企業に対応しているかどうかを確認することです。この例は、一種の事前フィルタリングとして、マッチングの前に適用できます。その後、より複雑または時間のかかるチェックが適用される場合があります。
フィルタリングはあまりしませんでした。国をチェックして、会社が同じかどうかを確認しました。それだけでした。データにはそれほど多くの可能性はありませんでした。時間の制約により、フィルタリングを強化するための追加データの広範な検索が除外され、とにかく手動チェックが計画されていました。
受け入れられた優れた回答にいくつかの例を追加したいと思います。 Python 2.7でテスト済み。
この奇妙な名前を例として使用しましょう。
name = "THE | big,- Pharma: LLC" # example of a company name
法的管理条件(ここではLLC)を削除することから始めることができます。それを行うには、素晴らしい cleanco Pythonライブラリがあります。これはまさにそれを行います:
from cleanco import cleanco
name = cleanco(name).clean_name() # 'THE | big,- Pharma'
すべての句読点を削除します。
name = name.translate(None, string.punctuation) # 'THE big Pharma'
(Unicode文字列の場合、代わりに次のコードが機能します( source 、 regex ):
import regex
name = regex.sub(ur"[[:punct:]]+", "", name) # u'THE big Pharma'
[〜#〜] nltk [〜#〜] を使用して、名前をトークンに分割します。
import nltk
tokens = nltk.Word_tokenize(name) # ['THE', 'big', 'Pharma']
すべてのトークンを小文字にします。
tokens = [t.lower() for t in tokens] # ['the', 'big', 'pharma']
ストップワードを削除します。 Mars
はストップワードであるため、On Mars
がOn
と誤って一致するなどの企業で問題が発生する可能性があることに注意してください。
from nltk.corpus import stopwords
tokens = [t for t in tokens if t not in stopwords.words('english')] # ['big', 'pharma']
ここでは、アクセント付きの特殊文字については説明しません(改善を歓迎します)。
ここで、すべての会社名をトークンにマップしたら、一致するペアを見つけたいと思います。間違いなく、このタスクでは、Jaccard(またはJaro-Winkler)の類似性はLevensteinよりも優れていますが、それでも十分ではありません。その理由は、名前に含まれる単語の重要性が考慮されていないためです(TF-IDFのように)。そのため、「会社」などの一般的な単語は、会社名を一意に識別する単語と同じようにスコアに影響を与えます。
それを改善するために、これで提案されている名前の類似性のトリックを使用することができます 素晴らしい一連の投稿 (私のものではありません)。これがそのコード例です:
# token2frequency is just a Word counter of all words in all names
# in the dataset
def sequence_uniqueness(seq, token2frequency):
return sum(1/token2frequency(t)**0.5 for t in seq)
def name_similarity(a, b, token2frequency):
a_tokens = set(a.split())
b_tokens = set(b.split())
a_uniq = sequence_uniqueness(a_tokens)
b_uniq = sequence_uniqueness(b_tokens)
return sequence_uniqueness(a.intersection(b))/(a_uniq * b_uniq) ** 0.5
これを使用して、特定のしきい値を超える類似性を持つ名前を照合できます。より複雑なアプローチとして、いくつかのスコア(たとえば、この一意性スコア、JaccardとJaro-Winkler)を取得し、いくつかのラベル付きデータを使用してバイナリ分類モデルをトレーニングすることもできます。これにより、スコアの数が与えられると、候補ペアの場合に出力されます。一致するかどうか。これについての詳細は、同じブログ投稿にあります。
Pythonの類似/あいまい文字列を検索するための優れたライブラリがあります: fuzzywuzzy 。これは、前述のレーベンシュタイン距離測定の優れたラッパーライブラリです。ここであなたの名前を分析する方法は次のとおりです。
#!/usr/bin/env python
from fuzzywuzzy import fuzz
names = [
("George Washington Middle Schl",
"George Washington School"),
("Santa Fe East Inc",
"Santa Fe East"),
("Chop't Creative Salad Co",
"Chop't Creative Salad Company"),
("Manny and Olga's Pizza",
"Manny's & Olga's Pizza"),
("Ray's Hell Burger Too",
"Ray's Hell Burgers"),
("El Sol",
"El Sol de America"),
("Olney Theatre Center for the Arts",
"Olney Theatre"),
("21 M Lounge",
"21M Lounge"),
("Holiday Inn Hotel Washington",
"Holiday Inn Washington-Georgetown"),
("Residence Inn Washington,DC/Dupont Circle",
"Residence Inn Marriott Dupont Circle"),
("Jimmy John's Gourmet Sandwiches",
"Jimmy John's"),
("Omni Shoreham Hotel at Washington D.C.",
"Omni Shoreham Hotel"),
]
if __name__ == '__main__':
for pair in names:
print "{:>3} :: {}".format(fuzz.partial_ratio(*pair), pair)
>>> 79 :: ('George Washington Middle Schl', 'George Washington School')
>>> 100 :: ('Santa Fe East Inc', 'Santa Fe East')
>>> 100 :: ("Chop't Creative Salad Co", "Chop't Creative Salad Company")
>>> 86 :: ("Manny and Olga's Pizza", "Manny's & Olga's Pizza")
>>> 94 :: ("Ray's Hell Burger Too", "Ray's Hell Burgers")
>>> 100 :: ('El Sol', 'El Sol de America')
>>> 100 :: ('Olney Theatre Center for the Arts', 'Olney Theatre')
>>> 90 :: ('21 M Lounge', '21M Lounge')
>>> 79 :: ('Holiday Inn Hotel Washington', 'Holiday Inn Washington-Georgetown')
>>> 69 :: ('Residence Inn Washington,DC/Dupont Circle', 'Residence Inn Marriott Dupont Circle')
>>> 100 :: ("Jimmy John's Gourmet Sandwiches", "Jimmy John's")
>>> 100 :: ('Omni Shoreham Hotel at Washington D.C.', 'Omni Shoreham Hotel')
このような種類の問題を解決する別の方法は、 Elasticsearch である可能性があります。これは、あいまい検索もサポートします。
レーベンシュタイン距離を使用できます。これは、2つのシーケンス間の差(基本的には編集距離)を測定するために使用できます。
def levenshtein_distance(a,b):
n, m = len(a), len(b)
if n > m:
# Make sure n <= m, to use O(min(n,m)) space
a,b = b,a
n,m = m,n
current = range(n+1)
for i in range(1,m+1):
previous, current = current, [i]+[0]*n
for j in range(1,n+1):
add, delete = previous[j]+1, current[j-1]+1
change = previous[j-1]
if a[j-1] != b[i-1]:
change = change + 1
current[j] = min(add, delete, change)
return current[n]
if __name__=="__main__":
from sys import argv
print levenshtein_distance(argv[1],argv[2])
「pythoneditdistance」を検索したところ、このライブラリが最初の結果として表示されました: http://www.mindrot.org/projects/py-editdist/
同じ仕事をする別のPythonライブラリはここにあります: http://pypi.python.org/pypi/python-Levenshtein/
距離の編集 は、単純な(通常は文字ベースの)編集操作のみに従って、ある文字列を別の文字列に変換するために実行する必要のある作業量を表します。すべての操作(置換、削除、挿入、場合によっては転置)には関連するコストがあり、2つの文字列間の最小編集距離は、2つの文字列の相違度の尺度です。
特定のケースでは、長い文字列から短い文字列への距離を見つけ、文字の削除によるペナルティを少なくするように文字列を並べ替えることができます(多くの場合、文字列の1つが他の文字列のほとんど部分文字列であることがわかります) 。したがって、削除によって大きなペナルティが課されることはありません。
このサンプルコードを利用することもできます: http://norvig.com/spell-correct.html
Diff-Match-Patchライブラリ の使用を検討してください。差分プロセスに興味があるでしょう。テキストに差分を適用すると、プログラムによる違いとともに、違いをよく理解できます。
レーベンシュタイン距離に基づくアルゴリズムは優れています(完全ではありません)が、主な欠点は、比較ごとに非常に遅いことと、考えられるすべての組み合わせを比較する必要があるという事実です。
問題を解決する別の方法は、埋め込みまたは単語の袋を使用して、各会社名を(いくつかのクリーニングと事前設定の後で)数値のベクトルに変換することです。その後、利用可能なものに応じて、教師なしまたは教師ありML法を適用します。
単語を空白やコンマなどで区切ってから、別の名前と共通する単語の数を数え、「類似」と見なされる前に再販された単語の数を追加します。
もう1つの方法は、同じことを行うことですが、単語を取得して、各キャラクターにつなぎ合わせます。次に、単語ごとに、文字がx個の文字(またはパーセンテージ)で同じ順序(両側から)で見つかった場合に比較する必要があります。その場合、単語も類似していると言えます。
例:あなたは正方形と正方形を持っています
次に、キャラクターで確認すると、正方形がすべて正方形で同じ順序になっていることがわかります。これは似たような単語です。