0

文字列のターゲットリストから引き出すために使用している名前のリストがあります。例えば:

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kim','Christmas is here', 'CHRIS']

output = ['Chris Smith', 'Kim', 'CHRIS']

したがって、これまでのルールは次のとおりです。

  • 大文字小文字を区別しません
  • 部分的な単語と一致することはできません(つまり、クリスマス/ハイジャックされたものはクリス/ジャックと一致しないはずです)
  • 上記の基準に従って文字列に名前が含まれている限り、文字列内の他の単語は問題ありません。

これを達成するために、別のSOユーザーがこのスレッドでこのコードを提案しました:

[targ for targ in target_list if any(re.search(r'\b{}\b'.format(name), targ, re.I) for name in first_names)]

これはこれまでのところ非常に正確に機能しますが、名前リストの長さが最大5,000で、ターゲットリストの長さが20〜100行で、文字列の長さが30文字までの場合は非常に遅くなります。

ここでパフォーマンスを改善する方法について何か提案はありますか?

解決策:両方の正規表現ベースの解決策でオーバーフローエラーが発生したため、残念ながらテストできませんでした。(@mglisonの回答から)機能した解決策は次のとおりです。

new_names = set(name.lower() for name in names)
[ t for t in target if any(map(new_names.__contains__,t.lower().split())) ]

これにより、パフォーマンスが15秒から1秒未満に大幅に向上しました。

4

3 に答える 3

5

それらすべてを1つのスーパー正規表現に組み合わせることができるようです:

import re

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kim','Christmas is here', 'CHRIS']

regex_string = '|'.join(r"(?:\b"+re.escape(x)+r"\b)" for x in names)
print regex_string
regex = re.compile(regex_string,re.I)
print [t for t in target if regex.search(t)]

名前が単一の単語(空白なし)の場合にのみ機能する非正規表現ソリューション:

new_names = set(name.lower() for name in names)
[ t for t in target if any(map(new_names.__contains__,t.lower().split())) ]

式は次のanyように書くこともできます。

any(x in new_names for x in t.lower().split())

また

any(x.lower() in new_names for x in t.split())

または、set.intersection(以下の@DSMによって提案されている)に依存する別のバリ​​アント:

[ t for t in target if new_names.intersection(t.lower().split()) ]

プロファイルを作成して、パフォーマンスが本当に重要である場合に最もパフォーマンスが高いかどうかを確認できます。それ以外の場合は、読みやすく理解しやすいと思われるものを選択します

* python2.xを使用している場合は、上記のルートを使用して遅延評価を行うitertools.imap代わりに、おそらく使用することをお勧めします-Pythonが、パフォーマンスを向上さmapせる遅延を提供するかどうかも疑問に思いますstr.split非レイジーバージョンと同等..。

于 2012-12-17T14:04:25.217 に答える
4

これは私が考えることができる最も単純なものです:

[item for item in target if re.search(r'\b(%s)\b' % '|'.join(names), item)]

すべて一緒に:

import re

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kim','Christmas is here', 'CHRIS']

results = [item for item in target if re.search(r'\b(%s)\b' % '|'.join(names), item)]

print results
>>> 
['Chris Smith', 'Kim']

より効率的にするために、最初に正規表現をコンパイルできます。

regex = re.compile( r'\b(%s)\b' % '|'.join(names) )
[item for item in target if regex.search(item)]

編集

質問を検討し、いくつかのコメントを見た後、私は「解決策」を次のように修正しました。

import re
names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kim','Christmas is here', 'CHRIS']
regex = re.compile( r'\b((%s))\b' % ')|('.join([re.escape(name) for name in names]), re.I )
results = [item for item in target if regex.search(item)]

結果:

>>> 
['Chris Smith', 'Kim', 'CHRIS']
于 2012-12-17T14:06:08.573 に答える
-1

現在、1つのループを別のループ内で実行しており、2つのリストを繰り返し処理しています。それは常に二次パフォーマンスを提供します。

ローカル最適化の1つは、各名前の正規表現をコンパイルすることです(これにより、各正規表現の適用が高速化されます)。ただし、大きなメリットは、すべての正規表現を1つの正規表現に結合し、入力の各項目に適用することです。その方法については、@mgilsonの回答を参照してください。その後、コードのパフォーマンスは、O(M * N)ではなくO(M + N)として線形にスケーリングする必要があります。

于 2012-12-17T14:06:33.247 に答える