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


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

## 第8章: 機械学習
>本章では，Bo Pang氏とLillian Lee氏が公開しているMovie Review Dataのsentence polarity dataset v1.0を用い，文を肯定的（ポジティブ）もしくは否定的（ネガティブ）に分類するタスク（極性分析）に取り組む．

###71. ストップワード
>英語のストップワードのリスト（ストップリスト）を適当に作成せよ．さらに，引数に与えられた単語（文字列）がストップリストに含まれている場合は真，それ以外は偽を返す関数を実装せよ．さらに，その関数に対するテストを記述せよ．

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

```python:main.py
# coding: utf-8

# ストップワードのリスト	 http://xpo6.com/list-of-english-stop-words/ のCSV Formatより
stop_words = (
	'a,able,about,across,after,all,almost,also,am,among,an,and,any,are,'
	'as,at,be,because,been,but,by,can,cannot,could,dear,did,do,does,'
	'either,else,ever,every,for,from,get,got,had,has,have,he,her,hers,'
	'him,his,how,however,i,if,in,into,is,it,its,just,least,let,like,'
	'likely,may,me,might,most,must,my,neither,no,nor,not,of,off,often,'
	'on,only,or,other,our,own,rather,said,say,says,she,should,since,so,'
	'some,than,that,the,their,them,then,there,these,they,this,tis,to,too,'
	'twas,us,wants,was,we,were,what,when,where,which,while,who,whom,why,'
	'will,with,would,yet,you,your').lower().split(',')


def is_stopword(str):
	'''文字がストップワードかどうかを返す
	大小文字は同一視する

	戻り値：
	ストップワードならTrue、違う場合はFalse
	'''
	return str.lower() in stop_words


# 正しく検出されることのテスト
assert is_stopword('a')				# リストの先頭
assert is_stopword('your')			# リストの末尾
assert is_stopword('often')			# リストの中間
assert is_stopword('on')			# リストの中間
assert is_stopword('A')				# 大小文字の同一視
assert is_stopword('Your')			# 大小文字の同一視
assert is_stopword('ofteN')			# 大小文字の同一視
assert is_stopword('ON')			# 大小文字の同一視

# 誤検出されないことのテスト
assert not is_stopword('0')			# リストにない
assert not is_stopword('z')			# リストにない
assert not is_stopword('bout')		# 後方一致されない
assert not is_stopword('acros')		# 前方一致されない
assert not is_stopword('fte')		# 中間一致されない
assert not is_stopword(' ')			# 空白
assert not is_stopword('\n')		# 制御コード
assert not is_stopword('')			# 空文字
```

####実行結果：
今回の問題は特に出力がありません。エラーにならなければOKです。

###ストップワード
ストップワードとは、文章から特徴を抽出する際に対象外とする、あまり意味をなさない単語のことです。日本語では「は」や「が」などがそれで、これらを除外することによりデータ量を減らし、より効率よく処理しようというのが目的です。ただし除外してしまうとその情報は使われなくなるため、重要な単語を除外してしまわないよう注意が必要です。

今回は「適当に作成せよ」とのことでしたが、英語のストップワードを自力で作成するのは辛いので、[WikipediaのStop words](https://en.wikipedia.org/wiki/Stop_words)の外部リンクの先頭で紹介されていた[XPO6のList of English Stop Words](http://xpo6.com/list-of-english-stop-words/)のCSV Formatを利用させていただきました。

###アサーションによるテスト
「関数に対するテストを記述せよ」という問題のため、テストの記述についてググってみたところ、Pythonにはテストを行うための仕組みがいろいろあるようです。

今回のようなシンプルな関数単体のテストであれば、モジュール化して関数説明（docstring）内にテストパターンを記述し、[doctest](http://docs.python.jp/3/library/doctest.html#module-doctest)という機能でテストするのがお手軽で良さそうです。ただし、内部ロジックをチェックするためのテストパターンをここに書いてしまうと、本来の関数説明としてのわかりやすさを損なう恐れがありそうです。

そこで今回は、次にお手軽そうなアサーションを使う方法にしてみました。
これは、コードの中に「この部分を通過する時にこの値はこうなっているはず」といったチェックポイントを埋めていくやり方です。チェックに引っかかるとプログラムがそこで終了するため、すぐに間違いに気づくことができます。使い方は簡単で、[`assert`](http://docs.python.jp/3/reference/simple_stmts.html#assert)の後ろに条件を書くだけです。もし条件を満たせなければ、その場でプログラムが終了します。

例えば今回のコードでは、最初のチェックで「a」がストップワードとして検出できていることを確認していますが、これを「hoge」に変えるとチェックに引っかかって次のようなエラーになり、間違いに気づくことができます。

```console:assert文のチェックに引っかかった場合
segavvy@ubuntu:~/ドキュメント/言語処理100本ノック2015/71$ python main.py
Traceback (most recent call last):
  File "main.py", line 27, in <module>
    assert is_stopword('hoge')				# リストの先頭
AssertionError
```

今回のテストでは、ストップワードリストの先頭や末尾のものが適切に処理されているか、誤って中間一致になってしまっていないか、大小文字の差異は吸収できているか、特殊文字の扱いは大丈夫かなどをチェックしてみました。

なお、assert文を書けば書くほど処理が増えるので、このままでは実行速度が落ちてしまいます。そのためアサーションは、デバッグ時のみ実行されるようになっています。実行時にコマンドラインオプションで最適化の指定（`-O`）をつけて実行すると、assert文はすべて無視され、処理速度を落とさずに実行できる仕組みです。

```console:assert文のチェックに引っかる状態で-Oで実行
segavvy@ubuntu:~/ドキュメント/言語処理100本ノック2015/71$ python -O main.py
segavvy@ubuntu:~/ドキュメント/言語処理100本ノック2015/71$ 
```

つまりassert文は、プログラム開発中のチェックにしか使えないことになります。完成したプログラム内でのエラー検出には使えないので注意しましょう。

またassert文の中には、完成時に実行が必要な処理を書いてはいけません。例えば「関数は呼び出すけど、デバッグ時だけ戻り値をチェックしよう」みたいな目的でassert文の中で関数を呼んでしまうと、完成時にassert文が無視されることで関数呼び出しも無視され、assert文のせいでバグが埋まってしまうという笑えない状態になります。この手のバグはデバッグ版で再現しないことになるので、原因を突き止めるのにも苦労しがちです。（実際私は、C/C++プログラマ時代に笑えない状態になりました...^^;）

Pythonのテストのための仕組みは、他にもいろいろあります。「python 単体テスト」などでググってみてください。

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

*実行結果には、[100本ノックで用いるコーパス・データ](http://www.cl.ecei.tohoku.ac.jp/nlp100/#data)で配布されているデータの一部が含まれます。*