『入門 ソーシャルデータ』で日本語を扱うために

追記(2012/09/17):macab-pythonをMac OSX 10.7 Lion, Python2.7にインストールする。

入門 ソーシャルデータ ―データマイニング、分析、可視化のテクニック

入門 ソーシャルデータ ―データマイニング、分析、可視化のテクニック

オライリーの『入門 ソーシャルデータ』を購入したが、p.8で躓いた。
本書の内容だけでは、「英語は文章が単語ごとにスペースで区切られているが、日本語はそうではない」という特性を処理できないのです。

まずぶちあたった問題はMac 0S X 10.6のコンソールでのPython対話型インタプリタで日本語が入力できない問題でした。
これは、macpythonインタラクティブシェルで日本語入力できないよ(解決) - kakatofu’s note http://d.hatena.ne.jp/kakatofu/20100825/python を参考に

$ easy_install readline

を実行するだけで解決しました。
これでp.6でtwitter_search.search(q="SNL"の部分に日本語を入力できるようになる。今回は、q="消費税"で試した。
この結果のうち、ツイートのテキスト部分のみをtweetsに格納するコードがp.8にあります。

>>> tweets = [ r['text'] for result in search_results for r in result['results']]

ここで、print tweets とやると、\u3060\u3002\u8a02...といったエンコードされていない文字列が延々と表示されて訳がわからない。しかし、エンコードを指定しようとすると、

>>>print tweets.encode('utf-8')
Traceback (most recent call last):
    File "<stdin>", line 1, in <module> 
Attribute Error: 'list' object has no attribute 'encode'

と怒られてしまう。配列にはエンコードって属性ないようです。
しょうがないから、配列からひとつひとつ要素を取り出してエンコードするように書き換える。

>>>for t in tweets:
...            print t.encode('utf-8')

これでやっと読める形で結果が表示されました。

次に、この結果を単語レベルに分けなければいけない(トークン化っていうの?)のだが、これが難しかったです。
参考にしたのは、Python による日本語自然言語処理 http://nltk.googlecode.com/svn/trunk/doc/book-jp/ch12.html です。これはオライリー『入門 自然言語処理』の訳者の方が書かれているものです。

まず、この検索結果をUTF-8形式のテキストファイルに保存します。
保存先は、説明が前後してしまいますが、NLTKのコーパスファイルのあるディレクトリ(私の場合、'nltk_data/corpora/')です。

>>>import codecs
>>>f = codecs.open('nltk_data/corpora/tweets.txt', 'w', 'utf-8')
>>>for t in tweets:
...           f.write(t)
>>>f.close()

ここまでできたら、一度Pythonを閉じて、$easy_install nltk を実行後Pythonを起動します。
そして、 import nltk とした後、 nltk.download() で表示される画面で選択し、 "all" というコレクション中の "jeita" という識別子を持つコーパスをインストールする。ホームディレクトリにnltk_dataというディレクトリが生成され、そのなかにあるzipファイルを解凍後、ファイルをnltk_data/corpora/下に移動します。

で、ここからが日本語のトークン化処理に入ります。

>>> import nltk
>>> from nltk.corpus.reader import *
>>> from nltk.corpus.reader.util import *
>>> from nltk.text import Text

をまず実行する。
これ以降は Python による日本語自然言語処理 http://nltk.googlecode.com/svn/trunk/doc/book-jp/ch12.html を参考に、

各行を各パラグラフとして読み込むためには、 PlaintextCorpusReader オブジェクト生成時にコンストラクタの para_block_reader 引数に read_line_block を渡せばよい。また、「。」、「!」、「?」で終わる一連の文字列を文として認識し分割するために、以下の RegexpTokenizer を定義しよう。

>>> jp_sent_tokenizer = nltk.RegexpTokenizer(u'[^ 「」!?。]*[!?。]')

また、文字種はそれぞれ Unicode の範囲(ひらがなについては [ぁ-ん] 、カタカナについては [ァ-ン] 、漢字については U+4E00 〜 U+9FFF )を指定することによって検出できる。これらを RegexpTokenizer に渡す正規表現として以下のように書くことができる。

>>> jp_chartype_tokenizer = nltk.RegexpTokenizer(u'([ぁ-んー]+|[ァ-ンー]+|[\u4e00-\u9FFF]+|[^ぁ-んァ-ンー\u4e00-\u9FFF]+)')

以上の各パラメータを使って、平文コーパスを読み込む PlaintextCorpusReader は以下のように宣言できる( /path/to/dir には、実際にコーパスファイルが置かれているディレクトリを指定する必要がある)。


を実行した後、

>>>word  = PlaintextCorpusReader("nltk_data/corpora/", r'tweets.txt', encoding='utf-8', para_block_reader=read_line_block, sent_tokenizer=jp_sent_tokenizer, word_tokenizer=jp_chartype_tokenizer)

を実行します。
そのあと、print word.raw()で、ちゃんと文字列が日本語で表示されるか確認してみてください。
そしたら、print '/'.join(word.words())で、品詞で区切られているか確かめる。
この結果が気に入らない場合は、上記サイトを読み込んで他の方法を試してみてください(私は挫折しました…)。
そしてついに、『入門 ソーシャルデータ』p.8の1.2.2に進みます。

>>>tweets_t = '/'.join(word.words())
>>>words =[]
>>>words = tweets_t.split('/')

これが、例1-7の最初の4行の代わりとなります。wordsの中身を確かめるには、

>>>for t in words:
...           print t.encode(''utf-8')

を実行します。
繰り返しますが、配列には.encode('utf-8')の属性を指定できません。for文で配列の中身を1つずつtに移して、そこにエンコードを指定するというやり方をとらないといけません。
そして、

>>> len(words)
23232
>>> len(set(words))
2948

といった結果がやっと得られました。

最初の一歩が重かった…。
私はPython自然言語処理も初心者で、この本で初めて取り組みました。
至らない所などたくさんあると思いますので、補足等、コメントいただけたら幸いです。