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


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

###33. サ変名詞
>サ変接続の名詞をすべて抽出せよ．

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

```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文ずつ辞書のリストを取得しサ変名詞を抽出
nouns = set()
nouns_test = []		# 確認用の出現順リスト
lines = neco_lines()
for line in lines:
	for morpheme in line:
		if morpheme['pos'] == '名詞' and morpheme['pos1'] == 'サ変接続':
			nouns.add(morpheme['surface'])
			nouns_test.append(morpheme['surface'])		# 確認用の出現順リストにも追加

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

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

```console:端末（先頭部分）
['見当', '記憶', '話', '装飾', '突起', '運転', '分別', '決心', '我慢', '餓死', '訪問', '始末', '猶予', '遭遇', '返報', '勉強', '昼寝', '珍重', '経験', '供', '——', '同居', '観察', '断言', '同衾', '迫害', '尊敬', '生活', '剿滅', '議論', '所有', '憤慨', '観念', '御馳走', '掠奪', '代言', '失敗', '投書', '鑑定', '述懐', '処', '想像', '写生', '感心', '失笑', '揶揄', '欠伸', '自白', '造作', '彩色', '判然', '心中', '感服', '小便', '失敬', '予定', '失望', '漫罵', '増長', '報道', '退屈', '加減', '一睡', '運動', '嘆賞', '前後', '佇立', '身動き', '挨拶', '鼓動', '軽蔑', '肥満', '乱暴', '教育', '交際',
```

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


###サ変接続の名詞とは
サ変接続というのは「〜する」の形が取れるものです。そのため品詞が`名詞`で、品詞細分類1が`サ変接続`のものを探せば良いようです。

ただしMeCabの結果には、品詞が`名詞`で品詞細分類1が`接尾`で品詞細分類2が`サ変接続`のものもあります。これは名詞に続く名詞で「〜する」の形が取れる、例えば「巨大『化』する」とか「世間『話』する」とかを指すようです。本来はこれらも抽出対象だと思いますが、[問題30](http://qiita.com/segavvy/items/1f517e06aa3bc5fc2316)で品詞細分類2を取り込んでいないので判定ができません。これは仕様ということで。

基本的には[前問](http://qiita.com/segavvy/items/64920712cc4b12e8ce51)で見ていた辞書のkeyと取り出す値のkeyを変えただけです。

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

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

# 1文ずつ辞書のリストを取得しサ変名詞を抽出
list_nouns = [morpheme['surface']
		for line in neco_lines()
		for morpheme in line
		if morpheme['pos'] == '名詞' and morpheme['pos1'] == 'サ変接続']
nouns = set(list_nouns)		# 重複除去

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

[前問](http://qiita.com/segavvy/items/64920712cc4b12e8ce51)でshiracamusさんにコメントいただいた内容ほぼまんまです^^;

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

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