Edited at

素人の言語処理100本ノック:37

More than 1 year has passed since last update.

言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。


第4章: 形態素解析


夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ.

なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.



37. 頻度上位10語


出現頻度が高い10語とその出現頻度をグラフ(例えば棒グラフなど)で表示せよ.



出来上がったコード:


main.py

# coding: utf-8

import MeCab
from collections import Counter
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties

fname = 'neko.txt'
fname_parsed = 'neko.txt.mecab'

def parse_neko():
'''「吾輩は猫である」を形態素解析
「吾輩は猫である」(neko.txt)を形態素解析してneko.txt.mecabに保存する
'''

with open(fname) as data_file, \
open(fname_parsed, mode='w') as out_file:

mecab = MeCab.Tagger()
out_file.write(mecab.parse(data_file.read()))

def neco_lines():
'''「吾輩は猫である」の形態素解析結果のジェネレータ
「吾輩は猫である」の形態素解析結果を順次読み込んで、各形態素を
・表層形(surface)
・基本形(base)
・品詞(pos)
・品詞細分類1(pos1)
の4つをキーとする辞書に格納し、1文ずつ、この辞書のリストとして返す

戻り値:
1文の各形態素を辞書化したリスト
'''
with open(fname_parsed) as file_parsed:

morphemes = []
for line in file_parsed:

# 表層形はtab区切り、それ以外は','区切りでバラす
cols = line.split('\t')
if(len(cols) < 2):
raise StopIteration # 区切りがなければ終了
res_cols = cols[1].split(',')

# 辞書作成、リストに追加
morpheme = {
'surface': cols[0],
'base': res_cols[6],
'pos': res_cols[0],
'pos1': res_cols[1]
}
morphemes.append(morpheme)

# 品詞細分類1が'句点'なら文の終わりと判定
if res_cols[1] == '句点':
yield morphemes
morphemes = []

# 形態素解析
parse_neko()

# Counterオブジェクトに単語をセット
word_counter = Counter()
for line in neco_lines():
word_counter.update([morpheme['surface'] for morpheme in line])

# 頻度上位10語の取得
size = 10
list_word = word_counter.most_common(size)
print(list_word)

# 単語(x軸用)と出現数(y軸用)のリストに分解
list_zipped = list(zip(*list_word))
words = list_zipped[0]
counts = list_zipped[1]

# グラフで使うフォント情報(デフォルトのままでは日本語が表示できない)
fp = FontProperties(
fname='/usr/share/fonts/truetype/takao-gothic/TakaoGothic.ttf'
)

# 棒グラフのデータ指定
plt.bar(
range(0, size), # x軸の値(0,1,2...9)
counts, # それに対応するy軸の値
align='center' # x軸における棒グラフの表示位置
)

# x軸のラベルの指定
plt.xticks(
range(0, size), # x軸の値(0,1,2...9)
words, # それに対応するラベル
fontproperties=fp # 使うフォント情報
)

# x軸の値の範囲の調整
plt.xlim(
xmin=-1, xmax=size # -1〜10(左右に1の余裕を持たせて見栄え良く)
)

# グラフのタイトル、ラベル指定
plt.title(
'37. 頻度上位10語', # タイトル
fontproperties=fp # 使うフォント情報
)
plt.xlabel(
'出現頻度が高い10語',# x軸ラベル
fontproperties=fp # 使うフォント情報
)
plt.ylabel(
'出現頻度', # y軸ラベル
fontproperties=fp # 使うフォント情報
)

# グリッドを表示
plt.grid(axis='y')

# 表示
plt.show()



実行結果:


端末

[('の', 9198), ('。', 7486), ('て', 6798), ('、', 6772), ('は', 6418), ('に', 6177), ('を', 6047), ('と', 5486), ('が', 5339), ('た', 3968)]


Kobito.F4PjId.png


matplotlibのインストール

問題で奨められているmatplotlibはすでに入っていました。問題02で入れたAnacondaに含まれていたようです。良かった^^


matplotlibの使い方

parse_neko()neco_lines()、およびword_counterを求める所までは前問と同じです。

matplotlibそのものについては、ネットに解説がたくさんありますので詳細は割愛します。オフィシャルサイトはこちらです。

以下、いくつかポイントです。コードにも多めにコメントを入れてみました。


初めて実行した時のwarning

matplotlibを初めてimportすると、次のような警告が表示されます。


端末

segavvy@ubuntu:~/ドキュメント/言語処理100本ノック2015/37$ python main.py 

/home/segavvy/anaconda3/lib/python3.5/site-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')

これは、フォントキャッシュをビルドする作業だそうで、初回だけ表示されます。次回以降は出てこないので、特に気にする必要はありません。


棒グラフ

棒グラフはpyplot.bar()を使います。パラメータで必須なのは、x軸の値のリストと、それに対応するy軸の値のリストだけです。align='center'は表示を中央にしたかったために指定しています。指定しないと左に寄りますので試してみてください。


グラフ用に結果を変換

word_counter.most_common(10)の取得結果は、


list_word

[('の', 9198), ('。', 7486), ('て', 6798), ('、', 6772), ('は', 6418), ('に', 6177), ('を', 6047), ('と', 5486), ('が', 5339), ('た', 3968)]


という形ですが、グラフにするためには、x軸(単語)とy軸(その出現頻度)のリストに分ける必要があります。これは問題02で出てきたzip()を使うと簡単です。


list(zip(*list_word))

[('の', '。', 'て', '、', 'は', 'に', 'を', 'と', 'が', 'た'), (9198, 7486, 6798, 6772, 6418, 6177, 6047, 5486, 5339, 3968)]



グラフで日本語を使う場合の注意

デフォルトのフォントには日本語の文字のデータがないため、日本語を表示しようとすると四角になってしまいます(豆腐とか呼ばれています)。そのため、FontPropertiesクラスで指定する必要があります。今回はUbuntu 16.04 LTSに入っていたTakaoGothic.ttfを指定しています。

なお、デフォルトのフォントを切り替えてしまう方法もあるようですが、今回は試していません。


x軸の値の範囲

グラフの軸の範囲はmatplotlibが適当に決めてくれるのですが、今回の場合、x軸が-2〜10になってしまい、そのままだと左に余白ができてしまいます。

Kobito.gvp7cv.png

そのため、plt.xlim()で範囲を-1〜10に設定しています。


グリッドの表示

グリッドがあった方が見やすいので、plt.grid()でy軸だけ表示してみました。

 

38本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第4章で用いているデータは青空文庫で公開されている夏目漱石の長編小説『吾輩は猫である』が元になっています。