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


[言語処理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)からどうぞ。

## 第6章: 英語テキストの処理
>英語のテキスト（nlp.txt）に対して，以下の処理を実行せよ．

###52. ステミング
>51の出力を入力として受け取り，Porterのステミングアルゴリズムを適用し，単語と語幹をタブ区切り形式で出力せよ． Pythonでは，Porterのステミングアルゴリズムの実装として[stemming](https://pypi.python.org/pypi/stemming)モジュールを利用するとよい．

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

```python:main.py
# coding: utf-8
import re
import snowballstemmer

fname = 'nlp.txt'


def nlp_lines():
	'''nlp.txtを1文ずつ読み込むジェネレータ
	nlp.txtを順次読み込んで1文ずつ返す

	戻り値：
	1文の文字列
	'''
	with open(fname) as lines:

		# 文切り出しの正規表現コンパイル
		pattern = re.compile(r'''
			(
				^					# 行頭
				.*?					# 任意のn文字、最少マッチ
				[\.|\;|\:|\?|\!]	# . or ; or : or ? or !
			)
			\s						# 空白文字
			(
				[A-Z].*				# 英大文字以降（＝次の文以降)

			)
		''', re.MULTILINE + re.VERBOSE + re.DOTALL)

		for line in lines:

			line = line.strip()		# 前後の空白文字除去
			while len(line) > 0:

				# 行から1文を取得
				match = pattern.match(line)
				if match:

					# 切り出した文を返す
					yield match.group(1)		# 先頭の文
					line = match.group(2)		# 次の文以降

				else:

					# 区切りがないので、最後までが1文
					yield line
					line = ''


def nlp_words():
	'''nlp.txtを1単語ずつ返すジェネレータ
	文の終わりでは空文字を返す。

	戻り値：
	1単語、ただし文の終わりでは空文字を返す
	'''
	for line in nlp_lines():

		# 単語に分解、終端の区切り文字は除去して返す
		for word in line.split(' '):
			yield word.rstrip('.,;:?!')

		# 文の終わりは空文字
		yield ''


# 読み込み
stemmer = snowballstemmer.stemmer('english')
for word in nlp_words():

	# 元の結果とステミング結果を出力
	print('{}\t{}'.format(word, stemmer.stemWord(word)))
```

####実行結果：

長いので先頭部分の抜粋です。

```console:端末：先頭部分
Natural	Natur
language	languag
processing	process
	
From	From
Wikipedia	Wikipedia
the	the
free	free
encyclopedia	encyclopedia
	
Natural	Natur
language	languag
processing	process
(NLP)	(NLP)
is	is
a	a
field	field
of	of
computer	comput
science	scienc
artificial	artifici
intelligence	intellig
and	and
linguistics	linguist
concerned	concern
with	with
the	the
interactions	interact
between	between
computers	comput
and	and
human	human
(natural)	(natural)
languages	languag
	
As	As
such	such
NLP	NLP
is	is
related	relat
to	to
the	the
area	area
of	of
humani-computer	humani-comput
interaction	interact
	
Many	Mani
challenges	challeng
in	in
NLP	NLP
involve	involv
natural	natur
language	languag
understanding	understand
that	that
is	is
enabling	enabl
computers	comput
to	to
derive	deriv
meaning	mean
from	from
human	human
or	or
natural	natur
language	languag
input	input
and	and
others	other
involve	involv
natural	natur
language	languag
generation	generat
```

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

###ステミングライブラリのインストール
問題で紹介されている[stemming](https://pypi.python.org/pypi/stemming)モジュールは、Python3.5には対応していないらしいので、別のものを探す必要があります。そこで、まずはcondaを「stem」というキーワードで探してみました。

```console:端末：condaで検索
segavvy@ubuntu:~$ conda search stem
Fetching package metadata .......
snowballstemmer              1.2.0                    py26_0  defaults        
                             1.2.0                    py27_0  defaults        
                             1.2.0                    py33_0  defaults        
                             1.2.0                    py34_0  defaults        
                             1.2.0                    py35_0  defaults        
                             1.2.1                    py27_0  defaults        
                             1.2.1                    py34_0  defaults        
                          *  1.2.1                    py35_0  defaults        
system                       5.8                           0  defaults        
                             5.8                           1  defaults        
                             5.8                           2  defaults
```

お、`*`が付いているので、すでに「snowballstemmer」というのが入っていますね。Anacondaで入ったようです。[説明](https://pypi.python.org/pypi/snowballstemmer)を見ると16種類のステミングアルゴリズムがセットになっていて、英語はPorterのアルゴリズムのようです。これなら問題文の条件も満たしますので、これでいきましょう。

###語幹とステミング
語幹とは、語尾が変化する単語の、変化しない前方部分を指すそうです。そして、単語から語幹を抽出する処理がステミングです。今回使っているPorterのアルゴリズは、経験則により語幹を抽出する仕組みとのことでした。詳細はググってみてください。

なお、今回のコードはほぼ[前問](http://qiita.com/segavvy/items/f25fd2ff572cc8ec0534)のままで、最後に語幹の抽出を加えただけです。snowballstemmerの使い方は、オフィシャルサイトの[How to use library](https://github.com/shibukawa/snowball_py#how-to-use-library)を見るとわかります。シンプルでいいですね。

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

*実行結果には、[100本ノックで用いるコーパス・データ](http://www.cl.ecei.tohoku.ac.jp/nlp100/#data)で配布されているデータの一部が含まれます。この第6章で用いているデータのライセンスは[クリエイティブ・コモンズ 表示-継承 3.0 非移植](https://creativecommons.org/licenses/by-sa/3.0/legalcode)（[日本語訳](https://creativecommons.org/licenses/by-sa/3.0/deed.ja)）です。*