見出し画像

Pythonライブラリ(形態素解析):mecab

1.概要

 MeCabは 京都大学情報学研究科−NTTコミュニケーション科学基礎研究所 共同研究ユニットプロジェクトを通じて開発されたオープンソース 形態素解析(文を最小の意味単位である形態素に分解すること)エンジンです。 言語, 辞書,コーパスに依存しない汎用的な設計を基本方針としています。パラメータの推定に Conditional Random Fields (CRF) を用 いており, ChaSenが採用している 隠れマルコフモデルに比べ性能が向上しています。また平均的に ChaSen, Juman, KAKASIより高速に動作します。

1-1.テキスト解析の基礎

 テキスト解析は、テキストデータから有用な情報を抽出したり、テキストを理解したりするための手法です。形態素解析はその一つで、文章を意味を持つ最小単位である形態素に分けることで、テキストの構造を理解することが可能になります。
 テキスト解析の概要は「テキスト解析(スライド・東京大学)(CC-BY)」から抜粋しました。各種用語の説明があります。

2.環境構築

 mecabの環境構築はmecab-python3というPythonラッパーをインストールします。mecab本体とは別に辞書が必要なため合わせて"unidic-lite"もインストールします。

[Terminal]
pip install unidic-lite
pip install mecab-python3

3.形態素解析:Tagger()

 MeCabを使って形態素解析を行う基本的な方法を説明します。

3-1.形態素・品詞確認

 最も基本的な形態素解析の方法は、MeCabのインスタンスを作成して、テキストデータを渡すだけです。実行すると、テキストが形態素に分割され、各形態素の基本形、品詞、読みなどが表示されます。出力フォーマット一覧は下記の通りです。

  1. 表層形

  2. 品詞

  3. 品詞細分類1

  4. 品詞細分類2

  5. 品詞細分類3

  6. 活用型

  7. 活用形

  8. 原形

  9. 読み

  10. 発音

[IN]
import MeCab

tagger = MeCab.Tagger()
text = "すもももももももものうち"

print(tagger.parse(text))
[OUT]
すもも	スモモ	スモモ	李	名詞-普通名詞-一般			0
も	モ	モ	も	助詞-係助詞			
もも	モモ	モモ	桃	名詞-普通名詞-一般			0
も	モ	モ	も	助詞-係助詞			
もも	モモ	モモ	桃	名詞-普通名詞-一般			0
の	ノ	ノ	の	助詞-格助詞			
うち	ウチ	ウチ	内	名詞-普通名詞-副詞可能			0
EOS

 なおparseされた出力はprint文で出力した時に綺麗になるようにタブ(\t)や改行(\n)を含んだ文字列です。よって扱いに関してはPythonのテキスト処理が可能です。

[IN]
print(type(tagger.parse(text)))
display(tagger.parse(text))

[OUT]
print(type(m.parse(text)))
'すもも\tスモモ\tスモモ\t李\t名詞-普通名詞-一般\t\t\t0\nも\tモ\tモ\tも\t助詞-係助詞\t\t\t\nもも\tモモ\tモモ\t桃\t名詞-普通名詞-一般\t\t\t0\nも\tモ\tモ\tも\t助詞-係助詞\t\t\t\nもも\tモモ\tモモ\t桃\t名詞-普通名詞-一般\t\t\t0\nの\tノ\tノ\tの\t助詞-格助詞\t\t\t\nうち\tウチ\tウチ\t内\t名詞-普通名詞-副詞可能\t\t\t0\nEOS\n'

3-2.オプション表示:-O

 ”MeCab.Tagger()”の引数に-o オプションを指定すると複数の形式で出力を得ることが出来ます。

 3-2ー1.分かち書きモード(単語のみ抽出):-Owakati

 単語だけを抽出したい場合は、MeCabのインスタンスを作成する際にオプションとして"-Owakati"を指定します。なお"-O"はoutputの略であり出力形式を設定します。
 実行するとテキストが形態素に分割され、形態素がスペースで区切られて表示されます。

[IN]
tagger = MeCab.Tagger("-Owakati")

print(tagger.parse(text))
[OUT]
すもも も もも も もも の うち 

 3-2ー2.全情報出力:-Odump

 全情報を出力する場合は"Oyomi"を指定します。

[IN]
tagger = MeCab.Tagger("-Odump")
print(tagger.parse(text))
[OUT]
0 BOS BOS/EOS,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,* 0 0 0 0 0 0 2 1 0.000000 0.000000 0.000000 0
11 すもも 名詞,普通名詞,一般,*,*,*,スモモ,李,すもも,スモモ,すもも,スモモ,和,*,*,*,*,スモモ,スモモ,スモモ,スモモ,*,*,0,C2,* 0 9 5142 5142 1 6 0 1 0.000000 0.000000 0.000000 10280
24 も 助詞,係助詞,*,*,*,*,モ,も,も,モ,も,モ,和,*,*,*,*,モ,モ,モ,モ,*,*,*,"動詞%F2@-1,形容詞%F4@-2,名詞%F1",* 9 12 665 665 1 6 0 1 0.000000 0.000000 0.000000 12536
34 もも 名詞,普通名詞,一般,*,*,*,モモ,桃,もも,モモ,もも,モモ,和,*,*,*,*,モモ,モモ,モモ,モモ,*,*,0,C3,* 12 18 5142 5142 1 6 0 1 0.000000 0.000000 0.000000 21286
42 も 助詞,係助詞,*,*,*,*,モ,も,も,モ,も,モ,和,*,*,*,*,モ,モ,モ,モ,*,*,*,"動詞%F2@-1,形容詞%F4@-2,名詞%F1",* 18 21 665 665 1 6 0 1 0.000000 0.000000 0.000000 23542
52 もも 名詞,普通名詞,一般,*,*,*,モモ,桃,もも,モモ,もも,モモ,和,*,*,*,*,モモ,モモ,モモ,モモ,*,*,0,C3,* 21 27 5142 5142 1 6 0 1 0.000000 0.000000 0.000000 32292
62 の 助詞,格助詞,*,*,*,*,ノ,の,の,ノ,の,ノ,和,*,*,*,*,ノ,ノ,ノ,ノ,*,*,*,名詞%F1,* 27 30 802 802 1 6 0 1 0.000000 0.000000 0.000000 33019
78 うち 名詞,普通名詞,副詞可能,*,*,*,ウチ,内,うち,ウチ,うち,ウチ,和,*,*,*,*,ウチ,ウチ,ウチ,ウチ,*,*,0,C3,* 30 36 5148 5148 1 6 0 1 0.000000 0.000000 0.000000 35080
86 EOS BOS/EOS,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,* 36 36 0 0 0 0 3 1 0.000000 0.000000 0.000000 34986

 3-2ー3.Chasen形式:-Ochasen

 Chasen形式(日本語形態素解析ツールの一つである「ChaSen」が出力する形式)で出力する場合は下記の通りです。ただし私のPCではエラーが出ており原因不明ですが、前述の環境構築に+αで追加処理が必要かもしれません。

[IN]
tagger = MeCab.Tagger("-Ochasen")
print(tagger.parse(text))
[OUT]
エラー中

 3-2ー4.ヨミ付与:-Oyomi

 カタカタ形式で出力する場合は"Oyomi"を指定します。ただし私のPCではエラーが出ており原因不明ですが、前述の環境構築に+αで追加処理が必要かもしれません。

[IN]
tagger = MeCab.Tagger("-Oyomi")
print(tagger.parse(text))
[OUT]
エラー中

3-4.サンプルコード

 テキストを複数用意してどのように形態素解析されるか確認しました。全部ひらがなだと精度が低いですが、漢字も合わせた文章では比較的よい結果が得られました。

[IN]
import MeCab
import pandas as pd

texts = [] #文章を格納するリスト
texts.append('赤巻紙青巻紙黄巻紙')
texts.append('あかまきがみあおまきがみきまきがみ')
texts.append('カエルぴょこぴょこ三ぴょこぴょこあわせてぴょこぴょこ六ぴょこぴょこ')
texts.append('かえるぴょこぴょこみぴょこぴょこあわせてぴょこぴょこむぴょこぴょこ')
texts.append('骨粗鬆症訴訟勝訴')
texts.append('こつそしょうしょうそしょうしょうそ')
texts.append('新人歌手新春シャンソンショー')
texts.append('しんじんかしゅしんしゅんしゃんそんしょー')
texts.append('スモモも桃も桃のうち 桃もスモモももものうち')
texts.append('すもももももももものうち もももすももももものうち')
texts.append('東京特許許可局長今日急遽休暇許可拒否')
texts.append('とうきょうとっきょきょかきょくちょうきょうきゅうきょきゅうかきょかきょひ')
texts.append('隣の客はよく柿食う客だ')
texts.append('となりのきゃくはよくかきくうきゃくだ')
texts.append('生麦生米生卵')
texts.append('なまむぎなまごめなまたまご')
texts.append('にわの庭には二羽の鶏は鰐を食べた')
texts.append('にわのにわにはにわのにわとりはわにをたべた')
texts.append('庭には鶏が二羽いました')
texts.append('にわにはにわとりがにわいました')
texts.append('坊主が屏風に上手に坊主の絵を描いた')
texts.append('ぼうずがびょうぶにじょうずにぼうずのえをかいた')

outputs = [] #出力するリスト
m = MeCab.Tagger("-Owakati")
for text in texts:
    outputs.append(m.parse(text))
    
pd.DataFrame(outputs).to_excel('outputs_mecab.xlsx',
                             index=False,
                             header=False,
                             encoding='shift-jis')
[OUT]

4.各形態素の詳細情報:node

 形態素解析(tagger.parse())の結果に関して、より詳細に情報抽出したいときはMeCab.Node オブジェクトを使用して解析します。

4-1.MeCab.Nodeの取得:parseToNode

 MeCab.Node オブジェクトの取得は”parseToNode(text)”を使用します。MeCab.Node オブジェクト自体は複数の情報は持っておりますがイテラブルオブジェクトではないためfor文で情報抽出はできません。

[IN]
import MeCab

tagger = MeCab.Tagger()
text = "すもももももももものうち"

node = tagger.parseToNode(text)
print(node)
print(type(node))
print(len(node))
[OUT]
<Swig Object of type 'MeCab::Node *' at 0x000001DC80009A30>
<class 'MeCab.Node'>
TypeError: object of type 'MeCab.Node' has no len()

4-2.nodeの詳細情報:surface, feature

 node情報の取得はsurfaceとfeature属性を使用します。

  • surface:表層形

  • feature:その他詳細

[IN]
node = node.next  # 最初のBOSを飛ばす
print(node.surface)
print(node.feature)
[OUT]
すもも
名詞,普通名詞,一般,*,*,*,スモモ,李,すもも,スモモ,すもも,スモモ,和,*,*,*,*,スモモ,スモモ,スモモ,スモモ,*,*,0,C2,*

4-3.次のnode情報取得:node.next

 イテラブルオブジェクトでいう"next(iter())"のように、nodeから次の情報を抽出するには"node.next"を使用します。
 下記にnodeとnode.nextのIDを確認しましたが別物であることが確認でき、node.nextは次の形態素情報を保持しています。

[IN]
print(node)
print(node.next)
[OUT]
<Swig Object of type 'MeCab::Node *' at 0x000001DC8000F6B0>
<Swig Object of type 'MeCab::Node *' at 0x000001DC800111F0>

 注意点としてメソッドのようにinplaceされるわけでないため、情報として残す場合はnodeの上書き(node=node.next)が必要です。

[IN]
import MeCab

tagger = MeCab.Tagger()
text = "すもももももももものうち"

node = tagger.parseToNode(text)
print(f'1つ目の形態素: {node.surface}, 品詞: {node.feature}')
node = node.next
print(f'2つ目の形態素: {node.surface}, 品詞: {node.feature}')
node = node.next
print(f'3つ目の形態素: {node.surface}, 品詞: {node.feature}')
node = node.next
print(f'4つ目の形態素: {node.surface}, 品詞: {node.feature}')
[OUT]
1つ目の形態素: , 品詞: BOS/EOS,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*
2つ目の形態素: すもも, 品詞: 名詞,普通名詞,一般,*,*,*,スモモ,李,すもも,スモモ,すもも,スモモ,和,*,*,*,*,スモモ,スモモ,スモモ,スモモ,*,*,0,C2,*
3つ目の形態素: も, 品詞: 助詞,係助詞,*,*,*,*,モ,も,も,モ,も,モ,和,*,*,*,*,モ,モ,モ,モ,*,*,*,"動詞%F2@-1,形容詞%F4@-2,名詞%F1",*
4つ目の形態素: もも, 品詞: 名詞,普通名詞,一般,*,*,*,モモ,桃,もも,モモ,もも,モモ,和,*,*,*,*,モモ,モモ,モモ,モモ,*,*,0,C3,*

4-4.全情報を一括取得

 node自体はイテラブルオブジェクトではありませんが、形態素が空になった時点でFalseになるためwhile文を用いることで全情報を抽出できます。

[IN]
import MeCab

tagger = MeCab.Tagger()
text = "すもももももももものうち"

node = tagger.parseToNode(text)

while node:
    print(f'{"#"*40}')
    print(f'形態素: {node.surface}, 品詞: {node.feature}')
    node = node.next
    print(node)
[OUT]
########################################
形態素: , 品詞: BOS/EOS,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*
<Swig Object of type 'MeCab::Node *' at 0x000001DC8003C270>
########################################
形態素: すもも, 品詞: 名詞,普通名詞,一般,*,*,*,スモモ,李,すもも,スモモ,すもも,スモモ,和,*,*,*,*,スモモ,スモモ,スモモ,スモモ,*,*,0,C2,*
<Swig Object of type 'MeCab::Node *' at 0x000001DC8003CD70>
########################################
形態素: も, 品詞: 助詞,係助詞,*,*,*,*,モ,も,も,モ,も,モ,和,*,*,*,*,モ,モ,モ,モ,*,*,*,"動詞%F2@-1,形容詞%F4@-2,名詞%F1",*
<Swig Object of type 'MeCab::Node *' at 0x000001DC8003C270>
########################################
形態素: もも, 品詞: 名詞,普通名詞,一般,*,*,*,モモ,桃,もも,モモ,もも,モモ,和,*,*,*,*,モモ,モモ,モモ,モモ,*,*,0,C3,*
<Swig Object of type 'MeCab::Node *' at 0x000001DC8003CD70>
########################################
形態素: も, 品詞: 助詞,係助詞,*,*,*,*,モ,も,も,モ,も,モ,和,*,*,*,*,モ,モ,モ,モ,*,*,*,"動詞%F2@-1,形容詞%F4@-2,名詞%F1",*
<Swig Object of type 'MeCab::Node *' at 0x000001DC8003C270>
########################################
形態素: もも, 品詞: 名詞,普通名詞,一般,*,*,*,モモ,桃,もも,モモ,もも,モモ,和,*,*,*,*,モモ,モモ,モモ,モモ,*,*,0,C3,*
<Swig Object of type 'MeCab::Node *' at 0x000001DC8003CD70>
########################################
形態素: の, 品詞: 助詞,格助詞,*,*,*,*,ノ,の,の,ノ,の,ノ,和,*,*,*,*,ノ,ノ,ノ,ノ,*,*,*,名詞%F1,*
<Swig Object of type 'MeCab::Node *' at 0x000001DC8003C270>
########################################
形態素: うち, 品詞: 名詞,普通名詞,副詞可能,*,*,*,ウチ,内,うち,ウチ,うち,ウチ,和,*,*,*,*,ウチ,ウチ,ウチ,ウチ,*,*,0,C3,*
<Swig Object of type 'MeCab::Node *' at 0x000001DC8003CD70>
########################################
形態素: , 品詞: BOS/EOS,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*
None



参考資料


あとがき

 追って追記(とりあえず先出)


この記事が気に入ったらサポートをしてみませんか?