*Pythonで自然言語処理 [#h685c13e]
 
 自然言語処理というか、文書を特徴ベクトルに変換するまでの tips などを書いていきます
 
 #contents
 
 *用意した文書を整形 [#i8e9d7d3]
 
 **文書のエンコーディングははっきりしてる? [#se947fa0]
 してる→unicode型に変換しましょう~
  # 例:EUC-JPの場合
  text = text.decode('euc-jp')
 
 してない→文字コードを自動判別しましょう。
 
 日本語の文書であることがはっきりしている場合、nkf、pykf などが便利です。~
 nkf for python: http://city.plala.jp/moin/NkfPython~
 pykf: http://memo.jj-net.jp/jjnet_sandbox/pykf/pykf-0.3.4.tgz(有志によるミラー)~
 SourceForge にも pykf のソースはありますが、そっちはファイルが足りないらしくコンパイルが通りませんでした。
 
 日本語以外の文書が含まれてる場合、chardet が便利です。~
 chardet: http://chardet.feedparser.org/~
 ただし、chardet も完璧ではないので try and error で変換を一つ一つ試した方がいい場合もあります。
 
 **文字列操作をする前に [#ja599b85]
 Python で文字列操作(置換、切り出しなど)をする場合、必ず unicode 型に変換してから行うようにしましょう。扱ってる変数が unicode 型か str 型かはっきりしないときは
  print type(text)
 で確認することができます。
 
 **改行コードを統一させましょう [#a8b530ce]
 string.replace() で文字の置換が可能です。
 
  text = text.replace('\r\n','\n')
  text = text.replace('\r','\n')
  # さらに改行コードを除去する場合
  text = text.replace('\n','')
 
 **全角/半角を統一させましょう [#f48ce261]
 Mac と Macなどのような半角文字と全角文字はそれぞれ別物として扱われます.
 人間から見た場合は同じ意味の単語なので,できるだけ一つの表記に統一させた方がいいです.
 
 *** unicodedata モジュールの normalize 関数を使う [#mf237527]
 normalize 関数は半角カナ→全角カナ、全角英数→半角英数へと正規化します。
  import unicodedata
  text = u'MacMac123123アア'
  text = unicodedata.normalize('NFKC', text)
  print text
 
 *** zenhan.py を使う [#we3fbf91]
 zenhan.py の使い方について: http://straitmouth.jp/blog/setomits/139~
 最新版(2009/11/7時点): http://straitmouth.jp/blog/setomits/877
 
 数字だけ半角にする、など unicodedata に比べて細かい指定が可能です。
 
 **HTMLファイルを扱う場合 [#s9208c77]
 
 ***HTMLから本文部分のみ抽出したい [#af25cbed]
 
 extractcontent という本文抽出モジュールを使うのがお手軽です。
 
 - Ruby版: http://labs.cybozu.co.jp/blog/nakatani/2007/09/web_1.html
 - Perl版: http://search.cpan.org/dist/HTML-ExtractContent/
 - 拙作Python版: http://github.com/yono/python-extractcontent
 
 ***HTMLタグ等を取り除きましょう [#c1c51bdf]
 単純な方法としては、re モジュールを使って
  import re
  htmltag = re.compile(r'<.*?>', re.I | re.S)
  text = htmltag.sub('', html)
 こんな感じで取り除くことができます。
 
 スクレイピングをしたい場合は Beautiful Soup などの HTML パーサーを使うと便利です。~
 http://www.crummy.com/software/BeautifulSoup/
 
 ***HTML特殊文字を通常の文字に変換しましょう [#nc76691b]
 HTML特殊文字とは、&amp;lt; などの文字のことです。
 
 私は下記のページの htmlentity2unicode() を使わせていただいてます。~
 http://www.programming-magic.com/20080820002254/
 
 **不要な記号は取り除きましょう [#f9810985]
 # などの記号は MeCab では「名詞、サ変接続」などと判別されてしまうので形態素解析にかける前に
 取り除いておいた方がいいと思います。
 
 *整形した文書を形態素解析 [#c22ce26a]
 
 **どの形態素解析器を使うか? [#g7896838]
 日本語→MeCab~
 http://mecab.sourceforge.net/~
 Python 用のラッパーも一緒に配布されてます。
 
 英語、フランス語、ドイツ語など→TreeTagger~
 http://www.ims.uni-stuttgart.de/projekte/corplex/TreeTagger/~
 
 Python 用のラッパーは以下から。
 http://www.limsi.fr/Individu/pointal/python/treetaggerwrapper-doc/~
 確認しようとしたところソース本体は403…。(09/11/7)
 
 
 **言語ごとに形態素解析器を使い分けたい場合 [#q57e5616]
 文書の言語判定を行いましょう
 
 ***既存のライブラリ [#n8477037]
 guess-language: http://pypi.python.org/pypi/guess-language/~
 UTF-8のみの対応っぽい
 
 ***私が使ってた手法 [#t2d2747f]
 ngram.py と Lingua::LanguageGuesser の言語モデルを組み合わせる
 
 ngram.py: http://thomas.mangin.me.uk/~
 ページ右側の検索窓で ngram.py で検索するとダウンロードリンクを見つけることができます。~
 もしくは~
 http://thomas.mangin.me.uk/data/source/ngram.py~
 から直接。
 
 Lingua::LanguageGuesser~
 http://gensen.dl.itc.u-tokyo.ac.jp/LanguageGuesser/hajimete_monogatari.html
 
 この二つのモジュールは TextCat という Perl で書かれた言語判定スクリプトの
 - Python 移植版→ngram.py
 - Perl モジュール化&UTF-8への対応を強化→Lingua::LanguageGuesser
 
 という関係性があります。
 
 ngram.py を動かすには言語モデル(言語ごとのプロファイル)を記録したファイルが必要です。配布元では TextCat の言語モデルを使用するように、とされてますがこれに Lingua::LanguageGuesser の言語モデルを使用することで UTF-8 へ対応できるようにします。
 
 ***HTMLの場合 [#t9ef963d]
 HTMLの場合,HTMLタグがついたままだと精度が著しく落ちます.~
 そのため,HTMLタグを除去する必要があるのですが str 型のままだと文字列操作できないので~
 文字コード判定 → Unicode に変換 → HTMLタグ除去 → UTF-8 にエンコード → 言語判定~
 という手順になると思います.~
 HEADタグ中の文字コードや言語コードなどのメタ情報を利用するのも一つの手段です~
 (というか,先にそっちをやるべき)~
 
 **MeCabの辞書について [#m1c425dd]
 
 デフォルト (ipadicなど) だけだと上手く単語が切り出せない場合は他の辞書を併用するなり自分で単語を追加するなりしましょう.
 
 - Ontolopedia.dic: http://www.nal.ie.u-ryukyu.ac.jp/ontolopedia/~
 辞書そのものは公開されてません.研究室サーバ内にあったはず.
 - unidic: http://www.tokuteicorpus.jp/dist/
 
 単語の追加や辞書作成についてはこちらで.~
 http://mecab.sourceforge.net/dic.html
 
 *特徴ベクトルを作成 [#acfd36b9]
 文書における特徴ベクトルは,多くの場合文書中の単語を要素,重みとして出現頻度やTF-IDFによって表現されます.
 
 「文書における特徴ベクトルの要素は文書中の単語です」という文を実際に特徴ベクトルに変換してみます.
 
 ** 文を形態素解析する [#lb1240cd]
  % mecab
  文書における特徴ベクトルの要素は文書中の単語です
  文書    名詞,一般,*,*,*,*,文書,ブンショ,ブンショ
  における        助詞,格助詞,連語,*,*,*,における,ニオケル,ニオケル
  特徴    名詞,一般,*,*,*,*,特徴,トクチョウ,トクチョー
  ベクトル        名詞,固有名詞,一般,*,*,*,*,はてなキーワード
  の      助詞,連体化,*,*,*,*,の,ノ,ノ
  要素    名詞,一般,*,*,*,*,要素,ヨウソ,ヨーソ
  は      助詞,係助詞,*,*,*,*,は,ハ,ワ
  文書    名詞,一般,*,*,*,*,文書,ブンショ,ブンショ
  中      名詞,接尾,副詞可能,*,*,*,中,チュウ,チュー
  の      助詞,連体化,*,*,*,*,の,ノ,ノ
  単語    名詞,一般,*,*,*,*,単語,タンゴ,タンゴ
  です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
 
 ** 解析結果をまとめる [#y51d1056]
 mecab の結果から,文中に出てきた単語と出現頻度を得ることができます.
 
 |単語|出現頻度|
 | 文書 | 2 |
 | における | 1 |
 | 特徴 | 1 |
 | ベクトル | 1 |
 | の | 2 |
 | 要素 | 1 |
 | は | 1 |
 | 中 | 1 |
 | 単語 | 1 |
 | です | 1 |
 
 この表が「文書における特徴ベクトルの要素は文書中の単語です」という文の特徴ベクトルです.
 
 Python で実装してみるとこんな感じです.
 
  #!/usr/bin/env python
  # -*- coding:utf-8 -*-
  """
  feature_vector.py
  
  指定されたファイル中の文章を解析して
  単語とその出現頻度を返す
  
  使い方(コマンドラインから)
  % python feature_vector.py file
  
  使い方 2(Pythonスクリプト中で)
  
  import feature_vector
  
  text = '単語とその出現頻度を返す'
  result = feature_vector.analyse(text)
  """
  import MeCab
  
  def analyse(text):
     mecab = MeCab.Tagger() ## MeCab のインスタンス作成
     feature_vector = {} ## 結果を納める辞書
     node = mecab.parseToNode(text) ## 解析を実行
     while node:
         print node.surface, node.feature ## それぞれ単語とその情報(品詞など)
         surface = node.surface.decode('utf-8')
         feature_vector[surface] = feature_vector.get(surface, 0) + 1 # 辞書に単語を追加
         node = node.next
     
     return feature_vector
  
  if __name__ == '__main__':
     import sys
     filename = sys.argv[1]
     file = open(filename).read()
     feature_vector = analyse(file)
  
     for word,freq in feature_vector.items():
         print "%s\t%d" % (word,freq)
 
 実行結果
  yono@orca% cat test.txt                                              
  文書における特徴ベクトルの要素は文書中の単語です
  
  yono@orca% python feature_vector.py test.txt             
   BOS/EOS,*,*,*,*,*,*,*,*
  文書 名詞,一般,*,*,*,*,文書,モンショ,モンショ
  における 助詞,格助詞,連語,*,*,*,における,ニオケル,ニオケル特徴 名詞,一般,*,*,*,*,特徴,トクチョウ,トクチョー
  ベクトル 名詞,一般,*,*,*,*,ベクトル,ベクトル,ベクトル
  の 助詞,連体化,*,*,*,*,の,ノ,ノ
  要素 名詞,一般,*,*,*,*,要素,ヨウソ,ヨーソ
  は 助詞,係助詞,*,*,*,*,は,ハ,ワ
  文書 名詞,一般,*,*,*,*,文書,モンショ,モンショ
  中 名詞,接尾,副詞可能,*,*,*,中,ナカ,ナカ
  の 助詞,連体化,*,*,*,*,の,ノ,ノ
  単語 名詞,一般,*,*,*,*,単語,タンゴ,タンゴ
  です 助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
   BOS/EOS,*,*,*,*,*,*,*,*
          2
  要素    1
  ベクトル        1
  中      1
  は      1
  の      2
  単語    1
  です    1
  文書    2
  特徴    1
  における        1
 同じものを github にも置いておきますので,参考程度に.~
 http://gist.github.com/231879~
 MeCab と MeCab 用の Python ラッパーをインストールしておく必要があります.~
 (テキストの文字コードをUTF-8に決め打ちしたりなど適当な点があるので,実際に使う際には適宜書き換えてください)
 
 ** 接尾語をまとめる [#b7421627]
 
 MeCab だと「労働者」という単語が「労働」「者」と分かれてしまいます.
  yono@orca% mecab                                                       
  労働者
  労働    名詞,サ変接続,*,*,*,*,労働,ロウドウ,ロードー
  者      名詞,接尾,一般,*,*,*,者,モノ,モノ
  EOS
 「可能性」も同様に「可能」「性」と分かれます.
  可能性
  可能    名詞,形容動詞語幹,*,*,*,*,可能,カノウ,カノー
  性      名詞,接尾,一般,*,*,*,性,セイ,セイ
  EOS
 「第二次」という単語は「第」「二」「次」と分かれます.
  第二次
  第      接頭詞,数接続,*,*,*,*,第,ダイ,ダイ
  二      名詞,数,*,*,*,*,二,ニ,ニ
  次      名詞,接尾,助数詞,*,*,*,次,ジ,ジ
  EOS
 これらはいずれも接頭語,接尾語が分解されるからです.
 場合によりけりですが,労働者や可能性などは一つの単語として用いた方が都合がいいと思われます.
 そこで,これらを一つの単語として扱うためのスクリプトを書いたので github に置いておきました.(ただし,正常に動作しないと思われます.時間があったら後で修正します.)~
 そこで,これらを一つの単語として扱うためのスクリプトを書いたので github に置いておきました.(%%ただし,正常に動作しないと思われます.時間があったら後で修正します.%%)追記: 修正しました.上記の例に関しては全て連結されるようになりました.~
 http://gist.github.com/271862
 
 上記のスクリプトは
 - 接頭語は次に名詞が来たら接続する
 - 接尾辞は一つ前に名詞が来たら接続する
 - 数字の次に助数詞が来たら接続する
 - 接頭詞,数接続 の次に数字が来たら接続する
 
 というルールに基づいてます.
 ただし,このルールだけだと適切でない接続も多く発生すると思われます.