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

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

###35. 名詞の連接
>名詞の連接（連続して出現する名詞）を最長一致で抽出せよ．

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

```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文ずつ辞書のリストを取得し抽出
list_series_noun = []		# 出現順リスト、重複あり
for line in neco_lines():
	nouns = []		# 見つけた名詞のリスト
	for morpheme in line:

		# 名詞ならnounsに追加
		if morpheme['pos'] == '名詞':
			nouns.append(morpheme['surface'])

		# 名詞以外なら、それまでの連続する名詞をlist_series_nounに追加
		else:
			if len(nouns) > 1:
				list_series_noun.append("".join(nouns))
			nouns = []

	# 名詞で終わる行があった場合は、最後の連続する名詞をlist_series_nounに追加
	if len(nouns) > 1:
		list_series_noun.append("".join(nouns))

# 重複除去
series_noun = set(list_series_noun)

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

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

```console:端末（先頭部分）
['人間中', '一番獰悪', '時妙', '一毛', 'その後猫', 'こんな片輪', '一度', 'ぷうぷうと煙', '邸内', '三毛', '暖かそう', '書生以外', '再びおさん', '同じ事', '四五遍', 'この間おさん', '三馬', '御台所', 'ら内', 'まま奥', '住家', 'ぎりほとんど', '勉強家', '勤勉家', '二三ページ', '主人以外', '限り吾輩', '朝主人', 'その後いろいろ経験', '一番心持', '二人', '一つ床', '一人', '最後大変', '——こと', '——猫', '神経胃弱性', '尻ぺたをひどく', '言語同断', '家内総がかり', '筋向', '白君', '度毎', '先日玉', '四疋', '三日目', '猫族', '完くし', '家族的生活',
```

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


###ロジックについて
`parse_neko()`と`neco_lines()`は[前問](http://qiita.com/segavvy/items/92f1177b6f095eb686a8)と同じです。

今回はコメントを少し多めにいれてみました。名詞が続く間は`nouns`に追加していって、名詞以外がきた時にそれまでにためた`nouns`を連結して`list_series_noun`に追加する流れです。
なお、これだと最後が名詞で終わった場合に`list_series_noun`に追加されないので、1行分のループが終わった後にも同じ処理を入れています。ただし今回の『吾輩は猫である』は必ず行が句点で終わるので、ここで追加されることはありません。

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

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