【Python】MeCab+TermExtractで抽出した重要語でMeCabの出力するテキストを置換する


初めに

termextractについて以下の記事を読ませてもらいました。
termextractを使って保持データから専門用語を抽出しmecabのユーザ辞書を作成する - Qiita

形態素解析を行うにあたっては、その業界ならではの単語などをまとめた専門用語辞書を作っておくと分かち書きされる際に良い形となりやすいと言うことで、termextractを使ってmecabのユーザ辞書を作ることにした。

今の抽出結果を反映させて確認したいだけだったので、辞書を出力するほどではないな…と。
というわけで、MeCabの出力と同じ形式で文字列を吐き出せるようなクラスを作成しました。

環境

Python 3.7.5
mecab-python 0.996.3
termextract 0.12b0

使い方

`MeCab.parse()の結果を受けながらオブジェクトを作成し、getそれと同じ形式で文字列を返します。

main
import MeCab
text = "羅生門が、朱雀大路にある以上は、この男のほかにも、雨やみをする市女笠や揉烏帽子が、もう二三人はありそうなものである。"

mecab = MeCab.Tagger()
mecab_text = mecab.parse(text)

# MeCabの結果を渡す
TX = TermExtract(mecab_text)
extracted = TX.get_extracted_words()  # 重要な語を抽出
modified_text = TX.get_modified_mecab_text()  # 重要な語を元に単語を連結したテキスト

print(modified_text)
実行結果
羅生門   名詞,固有名詞,一般,*,*,*,羅生門,ラショウモン,ラショーモン
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
、 記号,読点,*,*,*,*,、,、,、
朱雀大路    名詞,一般,*,*,*,*,朱雀大路,スザクオオジ,スザクオージ
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
ある  動詞,自立,*,*,五段・ラ行,基本形,ある,アル,アル
以上  名詞,非自立,副詞可能,*,*,*,以上,イジョウ,イジョー
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
、 記号,読点,*,*,*,*,、,、,、
...

朱雀大路は、MeCabでは朱雀大路に分けられるのですが、termextractで連続した語として抽出されるため、連結されます。

ソースコード

全体はgithubに上げてあります。
自分好みにコーディングしたところについて書いておきます。

形態素の連結について

複数の形態素を連結する時、[表層系, 原形, 読み, 発音] のみ文字列結合しています。
その他を文字列結合しない理由は、例えば「名詞名詞」のような新しい品詞ができてしまうのを避けるためです。
文字列結合しない場合、連結する最後の語の値を採用しています。

my_termextract.py
def concat_morph(morphs):
    '''
    複数の形態素の結合を行う。
    結合するのは [表層系, 原形, 読み, 発音] のみ。
    その他はリストの最後の要素に合わせる。

    Input: 形態素のリスト
    Output: 結合された形態素
    '''
    import copy
    new_morph = list(copy.deepcopy(morphs[-1]))

    # 表層系
    new_morph[0] = "".join(x[0] for x in morphs)
    # 原形
    new_morph[7] = "".join(x[7] for x in morphs if x[7]!="*")
    # 読み
    new_morph[8] = "".join(x[8] for x in morphs if x[8]!="*")
    # 発音
    new_morph[9] = "".join(x[9] for x in morphs if x[9]!="*")
    return tuple(new_morph)

朱雀  名詞,固有名詞,地域,一般,*,*,朱雀,スザク,スザク
大路  名詞,一般,*,*,*,*,大路,オオジ,オージ

朱雀大路    名詞,一般,*,*,*,*,朱雀大路,スザクオオジ,スザクオージ

連結する単語の選定について

extracted_wordsに含まれる「2以上の語で構成される語」が対象となります。
基本的にはtermextractの結果を格納しているのですが、他に連結したい語がある場合や連結したくない語がある場合は、extracted_wordsを上書きすることで対応可能です。

my_termextract.py
for cmp_noun in self.extracted_words:
    # 表層系の取得
    surfaces, *_ = zip(*self.morphs)

    # スペースで区切る
    cmp_list = cmp_noun.split(" ")
    len_cmp = len(cmp_list)
    # 連結語でない場合はcontinue
    if len_cmp < 2:
        continue

    # 連結語とマッチしたインデックス
    match_indeces = [i for i in range(len(surfaces)-len_cmp+1) if surfaces[i:i+len_cmp]==tuple(cmp_list)]

termextractのパラメータについて

冒頭の記事を参考にしています。
termextractを使って保持データから専門用語を抽出しmecabのユーザ辞書を作成する - Qiita

my_termextract.py
# 複合語を抽出し、重要度を算出
frequency = termextract.mecab.cmp_noun_dict(self.mecab_text)
LR = termextract.core.score_lr(frequency,
    ignore_words=termextract.mecab.IGNORE_WORDS,
    lr_mode=1, average_rate=1
    )
term_imp = termextract.core.term_importance(frequency, LR)

最後に

MeCabの結果を受けてその後の処理を実装した場合に、差し込んで使えたら便利かなぁと思って作成しました。
とりあえずの確認には使えると思います。
※コードが汚いのでリファクタリング予定です。記事に書いた部分で変更した場合、書き換えるようにします。

参考にしたページ

termextractを使って保持データから専門用語を抽出しmecabのユーザ辞書を作成する - Qiita