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


[言語処理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を用いるとよい．

###32. 動詞の原形
>動詞の原形をすべて抽出せよ．

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

```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['base'])
			verbs_test.append(morpheme['base'])		# 確認用の出現順リストにも追加

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

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

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

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


###原形とは
おそらく原形とは基本形のことですよね。そのため、[前問](http://qiita.com/segavvy/items/e63d90c675dad7d8bb42)で結果に格納していた`surface`を`base`に変えただけです。
実行後の状態の`print(verb_test)`の結果も載せておきます。こちらは重複が含まれます。

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

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

###内包表記
ループでリストを作っていますので、この部分の内包表記に挑戦してみました。2重ループになっているので、まずは内側のループだけを内包表記にしています。

```python:main2.pyの後半部分
# 形態素解析
parse_neko()

# 1文ずつ辞書のリストを取得し動詞を抽出
list_verbs = []		# 出現順リスト、重複あり
for line in neco_lines():
	list_verbs.extend(
		morpheme['base'] for morpheme in line if morpheme['pos'] == '動詞'
	)
verbs = set(list_verbs)

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

すでに内側を内包表記にしただけで混乱し始めてますが、同じ感じで外のループも内包表記にできそうですね。次回も似たような問題が続くので挑戦してみようと思います。

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

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