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


[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)の挑戦記録です。環境はUbuntu 16.04 LTS ＋ Python 3.5.2 \:\: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧は[こちら](http://qiita.com/segavvy/items/fb50ba8097d59475f760)からどうぞ。

## 第4章: 形態素解析
>夏目漱石の小説『吾輩は猫である』の文章（neko.txt）をMeCabを使って形態素解析し，その結果をneko.txt.mecabというファイルに保存せよ．このファイルを用いて，以下の問に対応するプログラムを実装せよ．
>
>なお，問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい．

###31. 動詞
>動詞の表層形をすべて抽出せよ．

####出来上がったコード：

```python:main.py
# coding: utf-8
import MeCab
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()

# 1文ずつ辞書のリストを取得し動詞を抽出
verbs = set()
verbs_test = []		# 確認用の出現順リスト
lines = neco_lines()
for line in lines:
	for morpheme in line:
		if morpheme['pos'] == '動詞':
			verbs.add(morpheme['surface'])
			verbs_test.append(morpheme['surface'])		# 確認用の出現順リストにも追加

# 確認しやすいようverbs_testを使って出現順にソートして表示
print(sorted(verbs, key=verbs_test.index))
```

####実行結果：
長いので先頭部分のみです。

```console:端末（先頭部分）
['生れ', 'つか', 'し', '泣い', 'い', 'いる', '始め', '見', '聞く', '捕え', '煮', '食う', '思わ', '載せ', 'られ', '持ち上げ', 'あっ', '落ちつい', 'いう', '思っ', '残っ', 'さ', 'れ', '逢っ', '出会わ', 'なら', '吹く', '咽せ', '弱っ', '飲む', '知っ', '坐っ', 'おっ', 'する', '動く', '分ら', '廻る', 'なる', '助から', 'さり', '出', '考え出そ', '気が付い', '見る', '見え', '隠し', 'しまっ', '違っ', '明い', '這い出し', '棄て', '這い出す', 'ある', '考え', '迎', '来', 'くれる', '考え付い', 'やっ', '渡っ', '暮れ', 'かかる', '減っ', '泣き', 'あるこ', 'そろ', '廻り', '這っ', '行く', '這入っ', '崩れ', 'もぐり込ん', '破れ', '知れん', '云っ', '至る', 'なっ', '忍び込ん', '減る', '降っ', '来る', '出来', 'あるい', '考える', 'つかん', '抛り出し', 'ねぶっ', '任せ', '這い', '上っ', '投げ出さ', '上り', '繰り返し', 'おさ', '云う', 'してやっ', '下り', 'つまみ出さ', 'れよ', 'いい', 'ぶら下げ', '向け', '出し', '困り', '撚り', '眺め', '置い', 'やれ', '聞か', '極める', '合せる', '帰る', '見せ', '覗い', '読みかけ', 'たらし', '帯び', 'あらわし', '食っ', '飲ん', 'ひろげる', '読む', '垂らす', '繰り返す', '限る', '寝', '勤まる', '云わ', 'せる', '鳴らし', '住み込ん', '行っ', '跳ね', '付け', 'くれ', 'つけ', '分る', '得る', '入れ', 'つとめ', '乗る', '構い', 'やむを得ん', '寝る', '入っ', 'ねる', '見出し', '割り込む', '醒ます', 
```

結果全体は[GitHub](https://github.com/segavvy/nlp100_Python/tree/master/31)にアップしています。


###表示時に出現順でソート
`parse_neko()`と`neco_lines()`は[前問](http://qiita.com/segavvy/items/1f517e06aa3bc5fc2316)と同じです。
抽出結果には重複がかなりあります。今回は`set`に格納して重複を除いていますが、出現順ではなくなってしまって正しく抽出できているか良く分からないので、追加時に`verbs_test`というリストも別で作ってそちらには出現順に格納し、表示時にこれを使って並び替えています。

参考までに、実行後の状態の`print(verb_test)`の結果も載せておきます。こちらは重複が含まれます。

```console:print(verb_test)の結果（先頭部分）
['生れ', 'つか', 'し', '泣い', 'い', 'し', 'いる', '始め', '見', '聞く', '捕え', '煮', '食う', '思わ', '載せ', 'られ', '持ち上げ', 'られ', 'し', 'あっ', '落ちつい', '見', 'いう', '見', '思っ', '残っ', 'いる', 'さ', 'れ', 'し', '逢っ', '出会わ', 'し', 'なら', 'し', 'いる', '吹く', '咽せ', '弱っ', '飲む', '知っ', '坐っ', 'おっ', 'する', 'し', '始め', '動く', '動く', '分ら', '廻る', 'なる', '助から', '思っ', 'いる', 'さり', 'し', '出', 'し', 'いる', '考え出そ', '分ら', '気が付い', '見る', 'い', 'おっ', '見え', '隠し', 'しまっ', '違っ', '明い', 'い', 'られ', '這い出し', '見る', '棄て', 'られ', '這い出す', 'ある', '坐っ', 'し', '考え', '見', '出', 'し', '泣い', '迎', '来', 'くれる', '考え付い', 'やっ', '見', '来', '渡っ', '暮れ', 'かかる', '減っ', '来', '泣き', '出', 'ある', 'あるこ', 'し', 'そろ', '廻り', '始め', 'し', '這っ', '行く', '出', '這入っ', 'なる', '思っ', '崩れ', 'もぐり込ん', '破れ', 'い', 'し', '知れん', '云っ', '至る', 'する', 'なっ', 'いる', '忍び込ん', '分ら', 'なる', '減る', '降っ', '来る', '出来', 'なっ', 'あるい', '行く', '考える', '這入っ', 'おっ', '見る', 'し', '逢っ', '見る', 'つかん', '抛り出し', '思っ', 'ねぶっ', '任せ', 'い', '出来', '見', '這い', '上っ', '投げ出さ', 'れ', '投げ出さ', 'れ', '這い', '上り', '這い', '上っ', '投げ出さ', 'れ', '繰り返し', 'し', 'いる', 'おさ', '云う', 'なっ', 'してやっ', '下り', 'つまみ出さ', 'れよ', 'し', 'いい', '出', '来', 'ぶら下げ', '向け', '出し', '出し', '上っ', '来', '困り', 'いう', '撚り', '眺め', 'おっ', '置い', 'やれ', '這入っ', 'しまっ', '聞か', '見え', '抛り出し', '極める', 'し', '合せる', '帰る', '這入っ', '出', '来る', '思っ', 'いる', '見せ', 'いる', 'いう', '覗い', '見る', 'し', 'いる', 'ある', '読みかけ', 'ある', 'たらし', 'いる', '帯び', 'あらわし', 'いる', '食う', '食っ', '飲む', '飲ん', 'ひろげる', '読む', 'なる', '垂らす', '繰り返す', '考える', 'ある', '生れ', 'なる', '限る', '寝', 'い', '勤まる', '出来', '云わ', 'せる', '来る', '鳴らし', 'いる', '住み込ん', '行っ', '跳ね', '付け', 'られ', 'し', 'くれ', 'さ', 'れ', '至る', 'つけ', 'くれ', '分る', '出来', '得る', '入れ', 'くれ', 'いる', 'つとめ', '読む', '乗る', 'する', '乗る', '構い', 'やむを得ん', '寝る', 'し', '入っ', 'もぐり込ん', 'ねる', 'なる', '入っ', '寝る', '見出し', '割り込む', '醒ます'
```

こちらも結果全体は[GitHub](https://github.com/segavvy/nlp100_Python/tree/master/31)にアップしています。

###改良予告？
今回、集合と出現順にするためのリストを同時に作成していますが、先にリストを作って一気に集合に変換した方が良さそうですね。また、ループでのリスト作成は内包表記で書いた方がすっきりしそうです。その辺に挑戦しようかと思ったのですが、ちょっと今回は時間がないので次回挑戦してみます。

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

*実行結果には、[100本ノックで用いるコーパス・データ](http://www.cl.ecei.tohoku.ac.jp/nlp100/#data)で配布されているデータの一部が含まれます。この第4章で用いているデータは[青空文庫](http://www.aozora.gr.jp)で公開されている夏目漱石の長編小説『吾輩は猫である』が元になっています。*