0

複数の部分を含む複数の単語のセットの検索を実装しようとしています。たとえば、これらの医学用語があります。

R Deep Transverse Metatarsal Ligament 4 GEODE
R Distal JointCapsule 1 GEODE
R Dorsal Calcaneocuboid Ligament GEODE
R Dorsal Carpometacarpal Ligament 2 GEODE
R Dorsal Cuboideavicular Ligament GEODE
R Dorsal Tarsometatarsal Ligament 5 GEODE
R Elbow Capsule GEODE
R F Distal JointCapsule 1 GEODE
R Fibular Collateral Bursa GEODE
R Fibular Collateral Ligament GEODE
R Fibular Ligament GEODE

ユーザーは次のように検索語を入力できます。

例: "R De Me Li" の場合、"R Deep Transverse Metatarsal Ligament 4 GEODE" が検索されます。

例: "Fi Colla" ==> "R 腓骨側副滑液包 GEODE"、"R 腓骨側副靭帯 GEODE"

例: 「弓 ODE」 ==> 「R エルボー カプセル GEODE」

つまり、ユーザーが単語の一部を入力しても、答えが見つかるはずです。複数の回答がある場合は、すべて表示する必要があります。よろしくお願いします。

追加) あ、何か忘れてた。

例: "ral lar" ==> 検索語の順序を考慮する必要があるため、"R Fibular Collat​​eral Bursa GEODE" または "R Fibular Collat​​eral Ligament GEODE" を表示しないでください。

また、クエリ単語間のスペースは、各行(データベース)の異なる単語を意味します。

クエリ ワードの順序は、各行 (データベース) の単語と同じにする必要がありますが、クエリ ワードはデータベース ワードよりも短くてもかまいません。

例: "R De Me 4" ==> "R Deep Transverse Metatarsal Ligament 4 GEODE" ここで、「Metatarsal」と「Ligament」には「me」が含まれていることがわかりますが、「Metatarsal」との最初の一致は問題なく、4検索されます。

さらに、クエリ ワードのさまざまな組み合わせで同じ結果が返される場合があります。

例えば。、

'Car' ==> 'R 背側手根中手靭帯 2 GEODE'

'Do Car' ==> 'R 背側手根中手靭帯 2 GEODE'

'R Do Carp' ==> 'R 背側手根中手靭帯 2 GEODE'

注: 大文字と小文字は区別されません。

4

4 に答える 4

4

これは、標準ディストリビューションのdifflibで実行できます。

import difflib

s="""R Deep Transverse Metatarsal Ligament 4 GEODE
R Distal JointCapsule 1 GEODE
R Dorsal Calcaneocuboid Ligament GEODE
R Dorsal Carpometacarpal Ligament 2 GEODE
R Dorsal Cuboideavicular Ligament GEODE
R Dorsal Tarsometatarsal Ligament 5 GEODE
R Elbow Capsule GEODE
R F Distal JointCapsule 1 GEODE
R Fibular Collateral Bursa GEODE
R Fibular Collateral Ligament GEODE
R Fibular Ligament GEODE""".split('\n')

qs="""R De Me Li
Fi Colla
bow ODE""".split('\n')

for q in qs:
    print "results for '{}':".format(q)
    matches=difflib.get_close_matches(q,s,3,0.3)
    for i,e in enumerate(matches,1):
        print "\t{}. {}".format(i,e)

版画:

results for 'R De Me Li':
    1. R Deep Transverse Metatarsal Ligament 4 GEODE
    2. R Dorsal Calcaneocuboid Ligament GEODE
    3. R Dorsal Cuboideavicular Ligament GEODE
results for 'Fi Colla':
    1. R Fibular Collateral Bursa GEODE
    2. R Fibular Collateral Ligament GEODE
results for 'bow ODE':
    1. R Elbow Capsule GEODE

正規表現とdifflibの組み合わせに関するcblabの回答と組み合わせると、次のようになります。

s="""R Deep Transverse Metatarsal Ligament 4 GEODE
R Distal JointCapsule 1 GEODE
R Dorsal Calcaneocuboid Ligament GEODE
R Dorsal Carpometacarpal Ligament 2 GEODE
R Dorsal Cuboideavicular Ligament GEODE
R Dorsal Tarsometatarsal Ligament 5 GEODE
R Elbow Capsule GEODE
R F Distal JointCapsule 1 GEODE
R Fibular Collateral Bursa GEODE
R Fibular Collateral Ligament GEODE
R Fibular Ligament GEODE""".split('\n')
s=set(s)
qs="""R De Me Li
Fi Colla
bow ODE
Car
Do Car
ral lar
R De Me 4
R Do Carp""".split('\n')

for q in sorted(qs):
    print "results for '{}':".format(q)
    pattern = r'.*' + re.sub(r'\W', '.*', q.strip()) + '.*'
    matches=[item for item in s if re.match(pattern, item, re.I)]
    for e in difflib.get_close_matches(q,s,3,0.33):
        if e not in matches: 
            matches.append(e)

    for i,e in enumerate(matches,1):
        print "\t{}. {}".format(i,e)
    else:
        if len(matches)==0:
            print "\tNo matches"    

版画:

results for 'Car':
    1. R Dorsal Carpometacarpal Ligament 2 GEODE
results for 'Do Car':
    1. R Dorsal Carpometacarpal Ligament 2 GEODE
results for 'Fi Colla':
    1. R Fibular Collateral Bursa GEODE
    2. R Fibular Collateral Ligament GEODE
results for 'R De Me 4':
    1. R Deep Transverse Metatarsal Ligament 4 GEODE
results for 'R De Me Li':
    1. R Deep Transverse Metatarsal Ligament 4 GEODE
    2. R Dorsal Calcaneocuboid Ligament GEODE
results for 'R Do Carp':
    1. R Dorsal Carpometacarpal Ligament 2 GEODE
    2. R Elbow Capsule GEODE
    3. R Distal JointCapsule 1 GEODE
results for 'bow ODE':
    1. R Elbow Capsule GEODE
results for 'ral lar':
    No matches
于 2012-06-07T01:49:19.740 に答える
3

答えられたように仕事をし、大文字と小文字を区別しない単純なpythonicソリューション:

import re

def search(request, base):
    pattern = r'.*' + re.sub(r'\W', '.*', request.strip()) + '.*'
    return [item for item in base if re.match(pattern, item, re.I)]

基本的に、リクエストのすべての部分文字列 (単語以外の文字で区切られたすべて) を元の順序で含む任意の文字列に一致する単純な正規表現を作成します。

たとえば、リクエスト'R De Me Li'がパターンになるr'.*R.*De.*Me.Li.*'

次に、一致するすべての結果のリストを返します。のフラグのおかげで、大文字と小文字は区別されません。re.Ire.match()

次に、期待どおりに動作します。ベースで試すことができます:

>>> base = ['R Deep Transverse Metatarsal Ligament 4 GEODE',
'R Distal JointCapsule 1 GEODE',
'R Dorsal Calcaneocuboid Ligament GEODE',
'R Dorsal Carpometacarpal Ligament 2 GEODE',
'R Dorsal Cuboideavicular Ligament GEODE',
'R Dorsal Tarsometatarsal Ligament 5 GEODE',
'R Elbow Capsule GEODE',
'R F Distal JointCapsule 1 GEODE',
'R Fibular Collateral Bursa GEODE',
'R Fibular Collateral Ligament GEODE',
'R Fibular Ligament GEODE']

リクエストの例:

>>> search('R De Me Li', base)
['R Deep Transverse Metatarsal Ligament 4 GEODE']
>>> search('Fi Colla', base)
['R Fibular Collateral Bursa GEODE', 'R Fibular Collateral Ligament GEODE']
>>> search('bow ODE', base)
['R Elbow Capsule GEODE']
>>> search('Car', base)
['R Dorsal Carpometacarpal Ligament 2 GEODE']
>>> search('F', base)
['R F Distal JointCapsule 1 GEODE', 'R Fibular Collateral Bursa GEODE', 'R Fibular Collateral Ligament GEODE', 'R Fibular Ligament GEODE']
>>> search('F Ca', base)
['R F Distal JointCapsule 1 GEODE']
>>> search('F Co', base)
['R Fibular Collateral Bursa GEODE', 'R Fibular Collateral Ligament GEODE']

注:リクエストとアイテムの順序が同じ場合にのみ一致します (つまり'ode bow'、リクエストは一致しませんが、一致['R Elbow Capsule GEODE']'bow ode'ます)。

注:あいまい検索は、レーベンシュタイン (編集距離) などの距離に基づいているため、少なくとも最初は、あいまい検索があまり役に立たないと思います。たとえば、「Fi」と「Fibular」の間で非常に大きくなります (単語 7 で 5 の距離 ... 35% 一致するのは良い考えだとは思いません ... リクエストに完全な単語のみが含まれており、タイプミスがほとんどない可能性があると確信している場合は、それを使用できます)

于 2012-06-07T01:36:40.010 に答える
1

「正規表現」の問題ではありません。文字列のファジー比較、つまりレーベンシュタイン距離または差分を見ている必要があります。

https://stackoverflow.com/questions/682367/good-python-modules-for-fuzzy-string-comparisonを参照してください

編集:いくつかのサンプルコード:

import Levenshtein

base_strings = [
    "R Deep Transverse Metatarsal Ligament 4 GEODE",
    "R Distal JointCapsule 1 GEODE",
    "R Dorsal Calcaneocuboid Ligament GEODE",
    "R Dorsal Carpometacarpal Ligament 2 GEODE",
    "R Dorsal Cuboideavicular Ligament GEODE",
    "R Dorsal Tarsometatarsal Ligament 5 GEODE",
    "R Elbow Capsule GEODE",
    "R F Distal JointCapsule 1 GEODE",
    "R Fibular Collateral Bursa GEODE",
    "R Fibular Collateral Ligament GEODE",
    "R Fibular Ligament GEODE"
]

def main():
    print("Medical term matcher:")
    while True:
        t = raw_input('Match what? ').strip()
        if len(t):
            print("Best match: {}".format(sorted(base_strings, key = lambda x: Levenshtein.ratio(x, t), reverse=True)[0]))
        else:
            break

if __name__=="__main__":
    main()

実際の出力:

Medical term matcher:
Match what? R De Me Li
Best match: R Deep Transverse Metatarsal Ligament 4 GEODE
Match what? Fi Colla
Best match: R Fibular Collateral Bursa GEODE
Match what? bow ODE
Best match: R Elbow Capsule GEODE
Match what? 

編集 2: 「複数の回答がある場合は、すべて表示する必要があります」 - 基本文字列はすべて、さまざまな程度の回答です。問題は、どのような種類の類似性値のカットオフを使用するかです。たぶん、「すべての回答が最良の一致よりも少なくとも90%優れている」のようなものですか?

于 2012-06-07T00:30:04.523 に答える
1

次のコードは、すべてのパーティクル (入力内の空白で区切られた文字列の断片) が結果に存在する場合に「一致」と見なすことを前提としています。この例ではループを使用していましたが、もちろん、使用するように調整する必要がありますraw_input

(複数の空白を許可するために)正規表現を使用しますが、使用される主な機能は次のif particle in lineとおりです。

import re

entry = """R Deep Transverse Metatarsal Ligament 4 GEODE
R Distal JointCapsule 1 GEODE
R Dorsal Calcaneocuboid Ligament GEODE
R Dorsal Carpometacarpal Ligament 2 GEODE
R Dorsal Cuboideavicular Ligament GEODE
R Dorsal Tarsometatarsal Ligament 5 GEODE
R Elbow Capsule GEODE
R F Distal JointCapsule 1 GEODE
R Fibular Collateral Bursa GEODE
R Fibular Collateral Ligament GEODE
R Fibular Ligament GEODE
"""

searches = """R De Me Li
Fi Colla
bow ODE"""

for search in searches.split('\n'):
    print search, ':'
    termlist = re.split('\s', search)
    for line in entry.split('\n'):
        match = True
        for term in termlist:
            if not term in line:
                match = False
        if match:
            print '\t', line
    print
于 2012-06-07T01:04:40.787 に答える