TLDR
最速のソリューションが必要な場合は、このメソッドを(set lookupを使用して)使用します。OPに似たデータセットの場合、受け入れられた回答よりも約2000倍高速です。
検索に正規表現を使用する場合は、このトライベースのバージョンを使用してください。これは、正規表現の和集合よりも1000倍高速です。
理論
あなたの文章が巨大な文字列でない場合は、1秒あたり50を超える数を処理することはおそらく実行可能です。
禁止されているすべての単語をセットに保存すると、そのセットに別の単語が含まれているかどうかを確認するのが非常に速くなります。
ロジックを関数にパックし、この関数を引数として渡せre.sub
ば完了です。
コード
import re
with open('/usr/share/dict/american-english') as wordbook:
banned_words = set(word.strip().lower() for word in wordbook)
def delete_banned_words(matchobj):
word = matchobj.group(0)
if word.lower() in banned_words:
return ""
else:
return word
sentences = ["I'm eric. Welcome here!", "Another boring sentence.",
"GiraffeElephantBoat", "sfgsdg sdwerha aswertwe"] * 250000
word_pattern = re.compile('\w+')
for sentence in sentences:
sentence = word_pattern.sub(delete_banned_words, sentence)
変換された文は次のとおりです。
' . !
.
GiraffeElephantBoat
sfgsdg sdwerha aswertwe
ご了承ください:
- 検索は大文字と小文字を区別しません(おかげで
lower()
)
- 単語を置き換えると、
""
(コードのように)2つのスペースが残る場合があります
- python3では、
\w+
アクセント付き文字(例:)にも一致し"ångström"
ます。
- 単語以外の文字(タブ、スペース、改行、マークなど)はそのまま残ります。
パフォーマンス
100万の文章があり、banned_words
ほぼ100,000の単語があり、スクリプトは7秒未満で実行されます。
比較すると、Liteyeの回答では1万文に160 秒必要でした。
n
言葉の合計amoundとされてm
禁止された単語の量、OPのとLiteyeのコードがありますO(n*m)
。
比較すると、私のコードはで実行されるはずO(n+m)
です。禁止されている単語よりも多くの文があることを考えると、アルゴリズムはになりO(n)
ます。
正規表現の結合テスト
と正規表現検索の複雑さは何ですか '\b(word1|word2|...|wordN)\b'
パターンですか?それはありますO(N)
かO(1)
?
正規表現エンジンの動作を把握するのはかなり難しいので、簡単なテストを作成しましょう。
このコードは、10**i
ランダムな英語の単語をリストに抽出します。対応する正規表現共用体を作成し、別の単語でテストします。
- 1つは明らかに単語ではない(それはで始まる
#
)
- 1つはリストの最初の単語です
- 1つはリストの最後の単語です
- 単語のように見えますが、そうではありません
import re
import timeit
import random
with open('/usr/share/dict/american-english') as wordbook:
english_words = [word.strip().lower() for word in wordbook]
random.shuffle(english_words)
print("First 10 words :")
print(english_words[:10])
test_words = [
("Surely not a word", "#surely_NöTäWORD_so_regex_engine_can_return_fast"),
("First word", english_words[0]),
("Last word", english_words[-1]),
("Almost a word", "couldbeaword")
]
def find(word):
def fun():
return union.match(word)
return fun
for exp in range(1, 6):
print("\nUnion of %d words" % 10**exp)
union = re.compile(r"\b(%s)\b" % '|'.join(english_words[:10**exp]))
for description, test_word in test_words:
time = timeit.timeit(find(test_word), number=1000) * 1000
print(" %-17s : %.1fms" % (description, time))
それは出力します:
First 10 words :
["geritol's", "sunstroke's", 'fib', 'fergus', 'charms', 'canning', 'supervisor', 'fallaciously', "heritage's", 'pastime']
Union of 10 words
Surely not a word : 0.7ms
First word : 0.8ms
Last word : 0.7ms
Almost a word : 0.7ms
Union of 100 words
Surely not a word : 0.7ms
First word : 1.1ms
Last word : 1.2ms
Almost a word : 1.2ms
Union of 1000 words
Surely not a word : 0.7ms
First word : 0.8ms
Last word : 9.6ms
Almost a word : 10.1ms
Union of 10000 words
Surely not a word : 1.4ms
First word : 1.8ms
Last word : 96.3ms
Almost a word : 116.6ms
Union of 100000 words
Surely not a word : 0.7ms
First word : 0.8ms
Last word : 1227.1ms
Almost a word : 1404.1ms
したがって、'\b(word1|word2|...|wordN)\b'
パターンのある単一の単語の検索は次のようになります。
O(1)
最良の場合
O(n/2)
平均的なケース、それはまだです O(n)
O(n)
最悪の場合
これらの結果は、単純なループ検索と一致しています。
正規表現ユニオンのはるかに速い代替手段は、トライから正規表現パターンを作成することです。