MeCabの出力をPandasのMultiIndexのDataFrameに変換してみた

スポンサーリンク
プログラミング言語 データ解析
スポンサーリンク

 pandasは使わないとすぐに忘れてしまうものです。今日はMeCabで出力したテキストデータをpythonのpandasのMultiIndexのDataFrameに変換して、利用しやすくしてみましたので、その手順をメモに残します。

MeCabの出力データの確認

 今回の環境は前回までにDockerで作ったubuntuにPythonとMeCabを入れたものを使います。その環境をJupyterLabで起動して、ウェブブラウザから使います。MeCab-Pythonの環境構築はこちら(前半後半)の記事をご覧ください。
 対象データは首相官邸HPにある『令和4年6月15日の岸田内閣総理大臣記者会見』の内容をテキストでコピーしたものを用います。(テキストファイル:kaiken.txt

文書については、著作権上差しさわりのない、政府発表のものを使わせていただきますm(_ _)m

まず、今回使用するMeCab、Pandas、Reの各ライブラリのimportとMeCabの処理のインスタンス化、文書の読み込みをしていきます。

# ライブラリのimport
import MeCab
import pandas as pd
import re

# MeCabのインスタンス化
mecab = MeCab.Tagger()

# 文書の読み込み
with open('./kaiken.txt', mode='r') as f:
    s = f.read()

# 文書のスペースを削除して、句点『。』で切って、リスト化
s = "".join(s.split())
text_list = s.split("。")

# センテンスの数を表示
print('センテンスの数={}'.format(len(text_list)))
センテンスの数=282

文書の7文目はこんな感じです。

# 9文目を出力
print(text_list[8])
我が国は、欧米諸国に比べ、感染を極めて低いレベルに抑え込んでいます

この文書をMeCabで解析(parse)してやるとこんな感じになります。

# MeCabでの解析例
example = mecab.parse(text_list[8])
print(example)
我が国	名詞,一般,*,*,*,*,我が国,ワガクニ,ワガクニ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
、	記号,読点,*,*,*,*,、,、,、
欧米	名詞,固有名詞,地域,一般,*,*,欧米,オウベイ,オーベイ
諸国	名詞,一般,*,*,*,*,諸国,ショコク,ショコク
に	助詞,格助詞,一般,*,*,*,に,ニ,ニ
比べ	動詞,自立,*,*,一段,連用形,比べる,クラベ,クラベ
、	記号,読点,*,*,*,*,、,、,、
感染	名詞,サ変接続,*,*,*,*,感染,カンセン,カンセン
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
極めて	副詞,一般,*,*,*,*,極めて,キワメテ,キワメテ
低い	形容詞,自立,*,*,形容詞・アウオ段,基本形,低い,ヒクイ,ヒクイ
レベル	名詞,一般,*,*,*,*,レベル,レベル,レベル
に	助詞,格助詞,一般,*,*,*,に,ニ,ニ
抑え込ん	動詞,自立,*,*,五段・マ行,連用タ接続,抑え込む,オサエコン,オサエコン
で	助詞,接続助詞,*,*,*,*,で,デ,デ
い	動詞,非自立,*,*,一段,連用形,いる,イ,イ
ます	助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
EOS

この出力結果の型を見ると文字列で、各要素は、Tab(\t)、コンマ(,)、改行(\n)で区切られているのが分かります。

print(type(example))
example
<class 'str'>
'我が国\t名詞,一般,*,*,*,*,我が国,ワガクニ,ワガクニ\nは\t助詞,係助詞,*,*,*,*,は,ハ,ワ\n、\t記号,読点,*,*,*,*,、,、,、\n欧米\t名詞,固有名詞,地域,一般,*,*,欧米,オウベイ,オーベイ\n諸国\t名詞,一般,*,*,*,*,諸国,ショコク,ショコク\nに\t助詞,格助詞,一般,*,*,*,に,ニ,ニ\n比べ\t動詞,自立,*,*,一段,連用形,比べる,クラベ,クラベ\n、\t記号,読点,*,*,*,*,、,、,、\n感染\t名詞,サ変接続,*,*,*,*,感染,カンセン,カンセン\nを\t助詞,格助詞,一般,*,*,*,を,ヲ,ヲ\n極めて\t副詞,一般,*,*,*,*,極めて,キワメテ,キワメテ\n低い\t形容詞,自立,*,*,形容詞・アウオ段,基本形,低い,ヒクイ,ヒクイ\nレベル\t名詞,一般,*,*,*,*,レベル,レベル,レベル\nに\t助詞,格助詞,一般,*,*,*,に,ニ,ニ\n抑え込ん\t動詞,自立,*,*,五段・マ行,連用タ接続,抑え込む,オサエコン,オサエコン\nで\t助詞,接続助詞,*,*,*,*,で,デ,デ\nい\t動詞,非自立,*,*,一段,連用形,いる,イ,イ\nます\t助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス\nEOS\n'

MeCab出力の整理

上記から、MeCabの解析結果の出力について、以下のことが分かります。
・各単語は、改行コード(\n)で区切られる。
・区切った形態素の表層形はTab(\t)で区切られる。
・各解析結果はコンマ(,)で区切られる。

なお、MeCabの作者のwebサイトでは出力フォーマットは以下の通りだと記載があります。

表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音

なお、MeCabには出力フォーマットをいろいろ変更できる便利な機能もあるようですが、今回はそこを変更せずにやります。

最終的に、出力値を表層形から発音までの10個のデータを要素として、インデックスにセンテンスNoと単語Noを付与したMultiIndexのDataFrameを出力することにします。

まずは、『我が国は、欧米諸国に比べ、感染を極めて低いレベルに抑え込んでいます』の1文の最初の形態素の表層形『我が国』の出力結果を取ってきます。各形態素の解析結果は改行コードで区切られていますので、
・改行コードで区切ってリスト化
・そのリストの1要素を、Tab(\t)とコンマ(,)で区切ってリスト化
してやります。

# 1文を解析
example = mecab.parse(text_list[8])
# 改行で区切る
sep_sen = re.split('\n', example)
# 最終行のEOSを削除
sep_sen.pop()
# 解析結果をリスト化
print(re.split('\t|,', sep_sen[0]))
['我が国', '名詞', '一般', '*', '*', '*', '*', '我が国', 'ワガクニ', 'ワガクニ']

 ここで、「re.split(‘\n’, example)」は解析結果の文字列exampleを改行コード(\n)で分けて、リスト化しています。
 さらに「re.split(‘\t|,’, sep_sen[0])」では、リストの「0番目」の形態素をTab(\t)、もしくはコンマ(,)で分けて、リスト化しています。

re.split(‘条件A|条件B’,文字列)

上の式で、条件A or 条件Bの部分で分けてリスト化するという正規表現での記述になります。

処理関数の実装

うまく文字列をリスト化できたので、上の式を関数に実装していきます。

# 各要素名をリスト化
cols = ['sentence','word']+re.split('\t|,', '表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音')

# 上記要素名に合わせたリストを出力する関数
#引数1:センテンスNo 引数2:センテンスの文字列
def class_separator(num0, mecab_sen):
    sep_sen = re.split('\n', mecab_sen)
    sep_sen.pop()
    sep_words_list = []
    for num1 in range(len(sep_sen)-1):
        sep_words_list.append([num0,num1]+re.split('\t|,', sep_sen[num1]))
    return sep_words_list

試しに関数class_separater()を使って、リスト化し、できたリストの先頭2列をインデックスとしたMultiIndexのDataFrameを作成します。

# 関数例
example = mecab.parse(text_list[8])
l = class_separator(8,example)
# リストをDataFrameに変換。最初の2列をMultiIndexに指定。
df = pd.DataFrame(l, columns = cols).set_index(cols[0:2])
df

うまくDataFrameに出力できました。

まとめ

最後に文章を入力したらMultiIndexのDataFrameを出力する関数を作成します。

# ライブラリのimport
import MeCab
import pandas as pd
import re

# MeCabのインスタンス化
mecab = MeCab.Tagger()

# 各要素名をリスト化
cols = ['sentence','word']+re.split('\t|,', '表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音')

# 上記要素名に合わせたリストを出力する関数
#引数1:センテンスNo 引数2:センテンスの文字列
def class_separator(num0, mecab_sen):
    sep_sen = re.split('\n', mecab_sen)
    sep_sen.pop()
    sep_words_list = []
    for num1 in range(len(sep_sen)-1):
        sep_words_list.append([num0,num1]+re.split('\t|,', sep_sen[num1]))
    return sep_words_list

# text_listからMeCabの解析結果をDataFrameで返す関数
def mecab_df_maker(text_list):
    df = pd.DataFrame([])
    for n in range(len(text_list)):
        l = class_separator(n, mecab.parse(text_list[n]))
        df = pd.concat([df, pd.DataFrame(l, columns = cols).set_index(cols[0:2])])
    return df
# 文書の読み込み
with open('./kaiken.txt', mode='r') as f:
    s = f.read()

# 文書のスペースを削除して、句点『。』で切って、リスト化
s = "".join(s.split())
text_list = s.split("。")

# 処理の実行
df =mecab_df_maker(text_list)
df

文章全体をDataFrameに変換できました。

コメント