PythonでFlashTextを使って単語の検索と置換をする

このチュートリアルでは、FlashTextモジュールを使って、Pythonでテキスト中の単語を置換する方法を説明します。

FlashTextのアルゴリズムは?

FlashTextモジュールは、独自のアルゴリズムであるFlashTextアルゴリズムに基づいています。

要するに、Aho-CorasickアルゴリズムのPython実装をベースにしているのです。

このアルゴリズムの基本的なポイントは、テキストをスキャンする回数を最小限にすることで、テキスト内の多数のキーワードを見つけるのにかかる時間を短縮することです。

素朴なアルゴリズムでは、各キーワードをテキスト全体のすべての単語と比較することで、線形探索のようなものを行います。

この方法は、テキスト文書全体をn回スキャンする必要があるため、非常に非効率的です(ここで、nはキーワードの数です)。

ということです。

一方、FlashTextは、テキストを1回だけスキャンすることを意図して、異なるアプローチを選択します。

FlashTextのアルゴリズムの効率性の鍵は、すべてのキーワードとそれに対応する置換語を辞書に保存していることです。

そして、辞書にあるキーワードごとにテキストをスキャンするのではなく、テキストを1回だけスキャンするのです。

この1回のスキャンで、単語は辞書のキーと照合され、存在すればキーの値で置換されます。

FlashTextのインストール方法

FlashTextのインストールは非常に簡単で、pipを使用します。

pip install flashtext


FlashTextの使用方法

まず、FlashText APIとその中の主要なクラスについて見てみましょう。

KeywordProcessor クラス

キーワードの処理を行うメインクラスは KeywordProcessor クラスです。

これを FlashText から直接インポートして、初期化しましょう。

from flashtext import KeywordProcessor
keyword_processor = KeywordProcessor()


前の行では、大文字と小文字を区別せずに処理する KeywordProcessor オブジェクトを作成しています。

大文字小文字を区別しないモードでは、abcABC がマッチし、aBcABc などともマッチします。

大文字と小文字を区別するモードでは、aaaと全く同じ文字列 aaa にのみマッチします。

また、大文字小文字を区別するモードで KeywordProcessor インスタンスを作成することもできます。

keyword_processor= KeywordProcessor(case_sensitive=True)


キーワード辞書の定義

FlashText モジュールでは、置き換えが必要な単語を定義するためにキーワードを使用します。

KeywordProcessor` オブジェクトは、定義されたすべてのキーワードを含む辞書を持っています。

辞書にキーワードを追加する方法には、一括して追加する方法と、ひとつずつ追加する方法の 2 通りがあります。

まず、キーワードを1つずつ追加する方法を見てみましょう。

keyword_processor.add_keyword(<keyword, <replacementword)


<replacementword はオプションの引数です。

指定しない場合は、<キーワードだけが辞書に追加され、他の置換ワードに置き換える方法はありません。

ですから、FlashTextを使って文章中の単語を置換したい場合は、 <replacementword 引数を指定することを強くお勧めします。

キーワードが2つ以上ある場合、1つ1つキーワードを追加していくのは少し時間がかかるかもしれません。

別の方法として、小さなキーワードのリストであっても、キーワードを一括して追加する方法がよく使われます。

keyword_dictionary = {
    'replacementWord1': ['list', 'of', 'keywords', 'for', 'replacementWord1'],
    'replacementWord2': ['list', 'of', 'keywords', 'for', 'replacementWord2'],
    ...
    'replacementWordN': ['list', 'of', 'keywords', 'for', 'replacementWordN']
}


keyword_processor.add_keywords_from_dict(keyword_dictionary )


辞書の各 key は文字列キーワードです。

value はリストでなければなりません。

また、 List を使ってキーワードを指定することもできます。

keyword_processor.add_keywords_from_list(['list', 'of', 'keywords'])


しかし、この方法では、キーワードを追加するだけで、置換語はありません。

また、テキストファイルに key=&gt;value の構文に従ったキーと値のペアが含まれている場合、以下のようになります。

keyword1=&gt;value1
keyword2=&gt;value2


キーワードをインポートするには、 keywords_from_file() 関数を使用します。

keyword_processor.add_keywords_from_file('keyword_list.txt')


最も柔軟で可読性の高い方法は、辞書を使用することです。

また、最終的にすべてが辞書に収まるという事実を考えると、このアルゴリズムに最も自然にマッチしています。

では、簡単な例を見てみましょう。

テキスト文書があり、使用する語彙を標準化するために同義語の使用を最小限に抑えたいとします。

要するに、awful, terrible, horribleなどの単語(キーワードのリスト)をbadという単語(置換単語)に置き換え、fine, excellent, greatなどの単語をgoodという単語で置き換えるのである。

これらの keywordsreplacement_wordskeyword_dictionary に追加することになる。

keyword_dictionary = {
    "bad": ["awful", "terrible", "horrible"],
    "good": ["fine", "excellent", "great"]
}


そして最後に、 keyword_dictionarykeyword_processor オブジェクトに追加します。

keyword_processor.add_keywords_from_dict(keyword_dictionary)


キーワードを置換ワードに置き換える

キーワードとその置換語を KeywordProcessor インスタンスにロードしたら、 replace_keywords() 関数を実行して、与えられたテキストをスキャンし、置換を実行します。

new_text = keywordProcessor.replace_keywords("Passed String")


これは指定されたテキストを解析し、その中のすべてのキーワードをマッチした値に置き換えて、新しい文字列を返します。

さて、ここでは通常、文字列リテラルを扱うのではなく、ドキュメントを扱います。

ドキュメントを開いてその中の行を読み、それを文字列として replace_keywords() 関数に渡したいのです。

注意: 本当に長いファイルの場合、ローカルマシンのメモリに収まらないかもしれません。

– ファイルを一行ずつ読むことを検討した方がいいかもしれません。

いずれにせよ、テキストファイルを読み込んで、その内容に対して replace_keywords() 関数を実行してみましょう。

# Open the long textual document `data.txt`
with open('data.txt', 'r+') as file:
    # Load the content from `data.txt` to a variable as a string
    content = file.read()
    # Replace all desired keywords from `data.txt` and store it in the new variable
    new_content = keyword_processor.replace_keywords(content)
    # Replace the old content
    file.seek(0)
    file.truncate()
    # Write the alternated content to the original file 
    file.write(new_content)


例えば、text.txtのようなテキストファイルを読み込むと、次のようになります。

The breakfast was terrific! I really loved the eggs, you're a great cook.


というテキストファイルに、次のようなキーワードと置換語を入力する。

from flashtext import KeywordProcessor
keyword_processor = KeywordProcessor()


keyword_dictionary = {
    "good": ["terrific", "great"],
    "eggs": ["hash browns"]
}


keyword_processor.add_keywords_from_dict(keyword_dictionary)


with open('data.txt', 'r+') as file:
    content = file.read()
    new_content = keyword_processor.replace_keywords(content)
    file.seek(0)
    file.truncate()
    file.write(new_content)


その結果、text.txtファイルは変更されます。

The breakfast was good! I really loved the hash browns, you're a good cook.


FlashTextモジュールのその他の便利な機能

FlashTextモジュールのその他の便利な機能を説明するために、ダミーのkeyword_processorkeyword_dictionary` を作ってみましょう。

keywordProcessor = KeywordProcessor()
keywordDictionary = {
    "bad": ["awful", "terrible", "horrible"],
    "good": ["fine", "excellent", "great"]
}
keywordProcessor.add_keywords_from_dict(keywordDictionary)


KeywordProcessorインスタンスに含まれるすべてのキーワードのリストを取得するには、get_all_keywords()` 関数を使用します。

# List all added keywords
print(keywordProcessor.get_all_keywords())


その結果は

{'awful': 'bad', 'terrible': 'bad', 'horrible': 'bad', 'fine': 'good', 'excellent': 'good', 'great': 'good'}


キーワードが KeywordProcessor に存在するかどうかを調べるには、 in 演算子を用います。

'bad' in keywordProcessor
# Output: true
# keyword `bad` is PRESENT in the keywordProcessor


'red' in keywordProcessor
# Output: false
# keyword `red` is NOT PRESENT in the keywordProcessor


'awful' in keywordProcessor
# Output: false
# keyword `awful` is NOT THE KEYWORD in the keywordProcessor
# instead, it IS REPLACEMENT WORD


そして、ある keyword に基づいて replacement_word にアクセスするには、次のようにします。

keywordProcessor['fine']
# Output: 'good'


keywordProcessor['excelent']
# Output: 'good'


keywordProcessor['goood']
# Output: None
# There is no keyword `goood` in the keywordProcessor


そして最後に、KeywordProcessor からキーワードを削除するには、 remove_keyword() 関数を使用します。

keyword_processor.remove_keyword('fine')
# This will remove `fine` from the keywordProcessor


また、削除したいキーワードと値のペアのリストや辞書を指定し、それを使って指定した要素を削除することもできます。

# Using a dictionary to remove keywords
keywordProcessor.remove_keywords_from_dict({"bad": ["awful", "terrible"]})
# This will remove keywords `awful` and `terrible` from the keywordProcessor


# Using a list to remove keywords
keywordProcessor.remove_keywords_from_list(["fine", "excellent"])
# This will remove keywords `fine` and `excellent` from the keywordProcessor


FlashTextと正規表現の比較

FlashTextは主に正規表現の代替品として作られたので、この2つを比較すると便利でしょう。

実際、StackOverflowでの質問に対する回答として作成されました。

実行速度を比較すると、明らかにFlashTextに軍配が上がります。

同じテキストで、キーワードの数が少ない場合も多い場合も、ほぼ同じ時間がかかります。

一方、正規表現では、置換するキーワードの数に比例して、実行時間が長くなります。

FlashTextの作者は、「大規模なクエリの場合、正規表現の実行には数日かかるが、FlashTextは15分で実行できる」と述べています。

出典:FlashTextの作者、Vikash Singh氏(FreeCodeCampにて

ベンチマークによると、FlashTextはキーワードの数が500を超えると、キーワードの検索と置換にRegExより短い時間で済むことがわかった。

しかし、特殊文字のマッチングに関しては、FlashTextは正規表現に勝てる見込みがありません。

しかも、FlashTextは、そのようなマッチングをサポートしていないのです。

ということです。

特殊文字マッチングとは、単語中の一部の文字が特別な意味を持ち、複数の異なる文字にマッチングできる状況を表します。

例えば、文字 d は0から9までの数字を表し、キーワード numberd は単語 number0, number1, number2, などにマッチすることを意味しています。

FlashTextは特殊文字のマッチングを念頭に置いて設計されていないため、等しい文字列リテラルにしかマッチしません。

単語 number1 にはキーワード number1 しかマッチせず、1つのキーワードで複数の異なる単語をマッチさせる方法はありません。

結論

ここまで見てきたように、FlashTextは非常にシンプルかつパワフルなツールです。

非常に軽量で、習得しやすく、置換するキーワードの数に関係なく、非常に時間効率の良いツールです。

他のツールと同様、重要なのは、どのようなケースで使用するのがベストなのかを知ることです。

500以上のキーワードを置換する場合、そのキーワードが特殊文字にマッチしない単純なものであれば、正規表現よりもFlashTextを使わない理由はありません。

一方、キーワードが 500 未満であったり、何らかの特殊な文字のマッチングがある場合は、FlashText を捨てて、柔軟性と構文を考慮した古き良き正規表現を使用すべきでしょう。

.

タイトルとURLをコピーしました