9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-11-15

言語処理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章で用いているデータは青空文庫で公開されている夏目漱石の長編小説『吾輩は猫である』が元になっています。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?