2019年3月30日土曜日

PythonとMeCabで形態素解析して品詞ごとに語を抽出する

テキストの分析をするときにMeCabなどで形態素解析をするが、すべての品詞を使わずに特定の品詞に絞って使うことが多い。MeCabではIPA辞書が使われるが、IPA辞書の品詞については、IPAdic legacyからダウンロードできるIPA辞書のアーカイブファイル内にユーザーズマニュアル(doc/ipadic-ja.pdf)があって、その中に説明がある。

MeCabで実際に使われる品詞一覧は、pos-id.defというファイルに記述されていて69ある。今回は、テキストをMeCabで形態素解析して、pos-id.defの品詞一覧をもとに品詞ごとにどういった語があるのか確認してみる。


環境


Windows10のWSL(Ubuntu 18.04)。


Python3のMeCabラッパーmecab-python3は、最新版の0.996.1ではなくバージョン0.7をインストールした。表層形が取得できない(バグ?)ためだが、ここでは表層形は使わないので最新版でも問題ないと思うが念のため。



MeCabの品詞一覧


MeCabで使われる品詞一覧はpos-id.defというファイルにある。場所は環境によって変わるようだが、WSL(Ubuntu 18.04)では/usr/share/mecab/dic/ipadic/pos-id.defにあった。ファイルの文字コードはEUC-JPで、品詞は全部で69ある。品詞名と詳細分類1~3、品詞IDの順で並んでいる。




品詞ごとに語を抽出する


テキストを形態素解析し、pos-id.defの品詞一覧をもとに語の品詞、詳細分類1をチェックして、品詞ごとに最大3語を抽出する。対象のテキストは、Pythonで青空文庫の作品テキストからルビなどを取り除くの方法でテキストデータにした青空文庫の『吾輩は猫である』。これをwagahaineko.txtとして保存し、以下のコードを実行する。

import codecs
from pprint import pprint
from collections import OrderedDict
import pandas as pd
import MeCab

def load_category(path):
    # 品詞一覧の取得
    print('Category list from ', path)

    cat_od = OrderedDict()

    try:
        with codecs.open(path, 'r', 'euc_jp') as f:
            lines = f.readlines()

        for l in lines:
            l_split = l.split()
            id = int(l_split[1].strip())
            cat_l = l_split[0].split(',')
            cat_od.update({id: cat_l})

    except Exception as e:
        if hasattr(e, 'message'):
            print(e.message)
        else:
            print(e)

        exit()

    return cat_od

def get_category_id(cat_od, features):
    # IPA辞書の品詞IDから一致する品詞IDを求める
    for id in range(len(cat_od)):
        if is_target_category(cat_od[id], features, level=2):
            return id

    # 「BOS/EOS」のときは該当IDなし
    return -1

def is_target_category(cat_l, features, level):
    # 形態素解析した語の品詞が対象の品詞と一致するか確認
    return all(list(map(lambda id_cat: id_cat[1] == features[id_cat[0]], enumerate(cat_l[0:level]))))


def main():
    # 品詞ごとの抽出最大数
    max_n = 3

    m = MeCab.Tagger('-Ochasen')
    m.parse('')

    with open('wagahaineko.txt', 'r') as f:
        text = f.read()

    node = m.parseToNode(text)

    # 品詞一覧の読み込み
    cat_od = load_category('/usr/share/mecab/dic/ipadic/pos-id.def')
    pprint(cat_od)

    df = pd.DataFrame(columns=['id', 'base', 'cat1', 'cat2'])

    while node:
        feature_split = node.feature.split(',')
        id = get_category_id(cat_od, feature_split)

        # 品詞ごとに最大3語を抽出
        if len(df[df['id']==id].index) < max_n and id >= 0:
            # 原型
            base_form = feature_split[6]

            # すでに抽出済みの語は除外する
            if len(df[(df['id']==id) & (df['base']==base_form)]) == 0:
                df = df.append({'id': id, 'base': base_form, 'cat1': feature_split[0], 'cat2': feature_split[1]}, ignore_index=True)

        node = node.next

    # DataFrameを省略せずに出力する
    with pd.option_context('display.max_rows', None, 'display.max_columns', None):
        print()
        print(df.sort_values('id').reset_index(drop=True))

if __name__ == '__main__':
    main()

結果は以下の通り。品詞ID、原型、品詞名、詳細分類1の順で並んでいる。



0 件のコメント:

コメントを投稿