2012年1月29日日曜日

[python] 修正内容をscpで転送するbzrコマンド。

背景

テスト機でプログラムの動作を確認したいとき、
trunkブランチにコミット、deployしてテスト機にソースを送る。
しかしバグがあった場合、再度コミットするため、
余計なリビジョンが増えてしまって気持ち悪い。

そのため、修正したソースをscpとかで送り、テスト機で確認するってことをよくやる。
その時修正したものいちいちbzr statusで探して、scpコマンド打つのとかマジでめんどい。

そこで、追加・修正したファイルを特定のサーバへ転送するためのbzrコマンドを作った。

探したらあった。


探してみると、やりたいこととまったく同じことしてる人がいた。

bzr scp TARGET で作業ファイルを開発サーバに転送するプラグインを作りました

でも作った。


まぁ、とりあえず作ってみる。
再発明の感は否めないけど。

import os
import subprocess
from urlparse import urlparse
from bzrlib.branch import Branch
from bzrlib.workingtree import WorkingTree
from bzrlib.commands import Command,register_command

version_info = (0, 0, 1, 'dev', 1)
__author__ = 'karino'
__date__   = '2012/01/23'

# serve list
server_list = [\
        ["m-tst","karino","karino@kokukuma.net:/home/karino/program/1"],\
        ["x-tst","karino","karino@kokukuma.net:/home/karino/program/2"],\
        ["g-tst","karino","karino@kokukuma.net:/home/karino/program/3"],\
]

class cmd_deploy(Command):
    local_branch_path = ""

    def run(self):

        # get changes
        print
        changes = self.get_changes()
        if changes==[]:
            print "There is no change."
            return
        else:
            print "--- change files ---"
            for filepath in changes:
                print filepath

        # select server
        print
        print "--- select server ---"
        snum = self.select_server()

        # confirm
        print
        print "--- dryrun ---"
        self.scp(changes, snum, True)
        ok = self.user_input("yes/no > ")

        # scp
        if ok:
            print
            print "--- execute ---"
            self.scp(changes, snum, False)
        return


    def scp(self, changes, snum, dryrun):
        for filepath in changes:

            # 
            local_path  = self.local_branch_path + filepath
            server_path = server_list[snum][2]+filepath

            # 
            if dryrun:
                print "scp -Cpr " + local_path + " " + server_path
            else:
                if subprocess.call(["scp","-Cpr", local_path, server_path]):
                    #print "scp -Cpr "+filepath+" "+path+"  ... failed"
                    pass
                else:
                    #print "scp -Cpr "+filepath+" "+path+"  ... success"
                    pass
        return


    def select_server(self):
        for i in range(len(server_list)):
            print i, ":",server_list[i][0]
        snum = raw_input("Which Server > ")
        if snum.isdigit() and int(snum) in range(len(server_list)):
            return int(snum)
        else:
            return 2


    def user_input(self, prompt):
        ok = raw_input(prompt)
        if ok in ['y','yes']: return True
        return False


    def get_changes(self):
        # get workingtree
        wt = WorkingTree.open_containing(os.getcwd())[0]
        br = Branch.open_containing(os.getcwd())[0]
        print urlparse(br.base).path
        self.local_branch_path = urlparse(br.base).path

        # get changes
        changes = wt.changes_from(wt.basis_tree(),want_unversioned=True)
        result = []
        for add in changes.added:
            result.append(add[0].encode('utf-8'))
        for mod in changes.modified:
            result.append(mod[0].encode('utf-8'))
        return result

register_command(cmd_deploy)

使い方と結果

① 例によって、~/.bazaar/plugins/にファイルを置く。

② bzr deployって打つと、追加・修正されたファイル
(bzr status、addとmodifiedが対象)を表示。

③ 送り先サーバを選ぶと、実行されるscpコマンドが表示され、
yesを打つと、そのコマンドが実行される。

[karino@localhost release]$ bzr deploy
"Key 'makuocmd' already registered"
Unable to load plugin 'makuocmd2' from '/home/karino/.bazaar/plugins'

/home/karino/bzr_test/release/
--- change files ---
123.php
k123.php
setting.yml

--- select server ---
0 : m-tst
1 : i-tst
2 : g-tst
Which Server > 0

--- dryrun ---
scp -Cpr /home/karino/bzr_test/release/123.php karino@kokukuma.net:/home/karino/program/1/123.php
scp -Cpr /home/karino/bzr_test/release/k123.php karino@kokukuma.net:/home/karino/program/1/k123.php
scp -Cpr /home/karino/bzr_test/release/setting.yml karino@kokukuma.net:/home/karino/program/1/setting.yml
yes/no > y

--- execute ---
123.php                             100%
k123.php                            100%
setting.yml                         100%
[karino@localhost release]$ 

[python] bazaarのコミット数をランキングするコマンドを作った。

朝会社に来て、bzr logすると、昨日までなかったコミットが
いっぱい増えていることがある。
そんな時ふと思った。この人一体何件コミットしてるんだろうと。

で、ブランチコミット数のランキングを出力するbzrコマンドを作ってみた。


ソース

#!/usr/bin/python
# coding=utf-8

import os
from bzrlib.branch import Branch
from bzrlib.commands import Command,register_command

version_info = (0, 0, 1, 'dev', 1)
__author__ = 'karino'
__date__   = '2012/01/23'

class cmd_rank(Command):

    def run(self):

        # ブランチオープン
        branch = Branch.open_containing(os.getcwd())[0]
        print branch
        history = branch.revision_history()

        # コミッター毎のコミット数を集計
        ranking = {}
        for log in history:
            committer  = log.split('-')
            #print committer
            if committer[0] in ranking:
                ranking[committer[0]] = ranking[committer[0]]+1
            else:
                ranking[committer[0]] = 1

        commitsum = sum(ranking.values())
        print
        print "%25s %10s %10s"  % ('committer', 'commited', 'rate')
        for k,v in reversed(sorted(ranking.items(), key=lambda x:x[1])):
            rate = v * 100/float(commitsum)
            print "%25s %10s %10s %%"  % (k, v, round(rate,2))

        # 
        print
        return

register_command(cmd_rank)

使い方と結果

例によって、~/.bazaar/plugins/にソースファイルを放り込めば使えるようになります。
使い方は、下記のような感じ。

karino@goldfish:~/bazzar/dev$ bzr rank
BzrBranch7('file:///home/karino//bazzar/dev/')

                committer   commited       rate
                   karubo        863      17.39 %
                   takeda        830      16.73 %
                   maniko        791      15.94 %
                  uemitsu        736      14.83 %
                     sato        706      14.23 %
                 morimomo        263        5.3 %
                    inoda        215       4.33 %
                tatsuhira        183       3.69 %
                   karino         72       1.45 %
                   funami         52       1.05 %
                    nitto         49       0.99 %
                    maito         42       0.85 %
                 higurasu         16       0.32 %
                     mori         16       0.32 %
                matsuhasu          9       0.18 %
                    osuga          3       0.06 %
                    kishi          3       0.06 %
                   tokuma          2       0.04 %
                 sakimoto          1       0.02 %
                      oho          1       0.02 %
最大コミット数863!。
自分の10倍以上だw。

参考になったサイト


bzrlib: bazaarをPythonから操作する

Bazaarの中央ブランチへのpushをHudsonのビルドトリガにしたい

2012年1月23日月曜日

[vim] mysql接続先をvimから変更するためのunite source

vimからテーブル操作をするためのプラグインdbextを使っているが、
接続するDBがサーバごとコロコロ変えなきゃいけないのがすごくめんどくさい。
DBSetOptionを変えるのも、sshポートフォワーディングの設定をするのも
かなりめんどくさい。

そこで、それらをuniteから一括して設定するためのsourceを書いた。

機能


① 一覧からDBを選択し、dbextのDB接続先やユーザ名パスワードを変更する。
② ローカルでない場合、sshポートフォワーディングの設定をする。
③ その際、すでにsshポートフォワーディングの設定がある場合は、
そのプロセスを停止して、新しいプロセスを立ち上げる。

ソースとインストールと使い方


☆ ソース
kokukuma / vim-unite-mysource
この中にある、mysql_con.vimってのがそれ。

☆ インストール
① gitから落として、.vim/内に配置
② vimprocを使っているので、それもインストールする。
③ .vimrcにmysqlの設定と、ポートフォワードに必要な情報を、以下のように書く。

let g:mysql_con_list = [
    \["0", "local", "root", "password", "dbkokukuma"],
    \["1", "remote1", "kokukuma", "password", "dbkokukuma_tst"],
    \["1", "remote1", "kokukuma", "password", "dbkokukuma_dev"],
    \["2", "remote2", "kokukuma", "password", "dbkokukuma_stg"],
\]
let g:port_forward = [
    \["kokukuma",  "kokukuma.dev.net"  ,"mysqlserver","/home/karino/.ssh/id_rsa" ],
    \["kokukuma",  "kokukuma.net"  ,"mysqlserver","/home/karino/.ssh/id_rsa" ],
\]

g:mysql_con_listの形式は、
[sshポートフォワード設定番号],[unite表示名],[DB接続ユーザ名],[DB接続パスワード],[接続先DB名]
sshポートフォワード設定番号は、0のときlocalhost、1以降は、g:port_forwardに対応する。

g:port_forwardの形式は、
[ssh接続ユーザ名],[sshサーバ],[DBサーバ],[秘密鍵パス]
DBサーバ名は、sshサーバから見たときの名前にする。


☆ 使い方

① vimを起動して、「:Unite mysql_con」と入力すると、以下のように、
設定した接続先mysql一覧が表示される。

② これを選択すると、dbextの設定、sshポートフォワーディングの設定が切り替わる。




引っかかった点


① sshポートフォワード
以下のように接続する環境。
ローカルホスト → sshサーバ → DBサーバ
sshサーバ :kokukuma.net
DBサーバ :mysqlserver

ssh -N -L 13306:mysqlserver:3306 karino@kokukuma.net -i /home/karino/.ssh/id_rsa
13306 : ポートフォワードに使用するローカルの任意ポート
mysqlserver : sshサーバから見たDBサーバ名
3306 : DBサーバの接続先ポート


ここらへんを参考にした。
SSH ポートフォワードで MySQL サーバにログインするときのメモ
SSHでポートフォワード


② sshポートフォワードのプロセス取得
いろいろ迷走した挙句、こんな形になるが、要検討。
ps -ax > ~/.tmpfile
cat ~/.tmpfile|grep 'ssh -N -L' && rm ~/.tmpfile
rm ~/.tmpfile

③ vimからbashコマンドを実行し、その戻値を取得

vimprocってプラグインを使わないと難しいらしい。

Shougo / vimproc
コンパイルが必要なので要注意。
cd ~/.vim/bundle/vimproc
make -f make_gcc.mak

2012年1月15日日曜日

[vim] source explorerを入れてみたときのメモ

vimをIDEっぽくするためのプラグイン。
Vimでソースコードを素早く追いかける


インストール


NERDTree.vim, srcexpl,vim, taglist.vimの3つをtrinity.vimが統合して動作する。
vim-scripts / trinity.vim
vim-scripts / Source-Explorer-srcexpl.vim
vim-scripts / taglist.vim

※ NERDTreeはtrinity.vimにも入っているらしい。

$ cd ~/.vim/bundle
$ git clone https://github.com/vim-scripts/trinity.vim
$ git clone https://github.com/vim-scripts/Source-Explorer-srcexpl.vim
$ git clone https://github.com/vim-scripts/taglist.vim


使ってみた感想

面白いけど、遅くなるのと狭くなるのがいただけない。
もうちょっと設定すれば、使い勝手よくなるのかもしれないが、
定着するかな・・・。

[vim] vimからmysqlを操作するプラグイン dbext

最近、mysqlのクエリたたくことが多くなってきた。

周りの人は、mysql workbenchというGUIクライアントを使っている。
MySQL Workbench

リレーション張りまくりのテーブル設計するにはいいかもしれないが、
現状テーブル作るときはsymfonyのコマンド使うし、
ちょっとselect実行するためにGUI立ち上げるのはめんどくさい。


そして何より、周りの人とおんなじもの使うのは負けた気がする。

で、vimからmysqlを実行するためのプラグイン dbextを入れてみた。
vim-scripts / dbext.vim
Vimから任意のデータベースを操作


インストール


pathogenを使っていれば、以下でOK。
$ cd ~/.vim/bundle
$ git clone https://github.com/vim-scripts/dbext.vim
便利だ。
[vim] pathogenを導入

設定

ユーザ名やパスワード、DB名等を.vimrcに書き込んでおく。
let dbext_default_profile=""
let dbext_default_type="MYSQL"
let dbext_default_user="root"
let dbext_default_passwd=""
let dbext_default_dbname="jobeet"
let dbext_default_host="localhost"
let dbext_default_buffer_lines=20
「let dbext_default_buffer_lines=20」は結果を表示するウインドウの行数。

ショートカットキーとかは初期設定でも使えそう。

使い方

① sqlを実行
ノーマルモードもしくはビジュアルモードで、「\ + se」
ノーマルモードでは、カーソルがある行。
ビジュアルモードでは、選択した範囲のSQLが実行される。
実行すると別ウインドウで結果が表示される。


② テーブルのselectを実行
たとえばインサート文が書いてるファイルを開き、
対象のテーブルの中身を見たいとき、
カーソルをテーブル名に合わせ「\ + st」とすればselectした結果が表示される。
いちいちselect文書く必要はない。

③ テーブルのdescを実行
あるテーブルの構造が知りたいときは、
カーソル下にテーブルがある状態で「\ + sdt」



④ show tablesを実行
DBにあるテーブルが知りたいときは、「\ + slt」
ただ、おしにくい。


そのうちやりたいこと

① 接続先を簡単に変更できる設定
接続先は頻繁に変更することになるから、絶対に必要。
unite source作るか。

② DB変更
今はあんまり使わないが、DBの変更も簡単にできるようにしておくか。

③ hook script
ヘルプを読むと、結果を表示した後に結果を適当に編集して表示できるっぽい。
今のとこ、数字と文字列に別の色つけるくらいのアイディアしか浮かばないが、
そのうちもうチョイ面白い設定をしてみたい。

④ tag設定
:DBCompleteTableを実行すれば、
テーブル名補完ができるようになる。
適当に設定して、そのまま放置していた補完回りと一緒にまとめたい。

[vim] 地味に便利。関数を抽出するunite sourceを作った。

前々から書いてみようと思っていたunite のsource。
現在編集しているファイルから関数を定義している行を抽出し、そこにジャンプできるものを書いた。


uniteソースの書き方


unite、vim、source、書き方、
とか調べたら結構出てきた。

あと、unite.vimのデフォルトソースとか参考になった。


使い方


プログラムのファイルを開いた状態で、
vimのコマンドに以下を入力すると。
:Unite get_function


こんな感じで表示される。
Sources: get_function
>
-   13   public function executeIndex(sfWebRequest $request)
-   21   public function executeShow(sfWebRequest $request)
-   27   public function executeNew(sfWebRequest $request)
-   32   public function executeCreate(sfWebRequest $request)
-   45   public function executeEdit(sfWebRequest $request)
-   51   public function executeUpdate(sfWebRequest $request)
-   62   public function executeDelete(sfWebRequest $request)
-   72   protected function processForm(sfWebRequest $request, sfForm $form)

たとえば、「- 32 public function executeCreate(sfWebRequest $request)」を選択してEnterを押すと。
  public function executeCreate(sfWebRequest $request)
  {
    $jobeet_affiliate::func_karino2();
    $this->forward404Unless($request->isMethod(sfRequest::POST));
    $this->form = new jobeet_jobForm();
    $this->processForm($request, $this->form);
    $this->setTemplate('new');
  }
この関数にジャンプする。
普通に便利だ。
これあれば、foldingとかいらないんじゃないかな。

実際使うときは、下の感じでキーバインドを設定した。
noremap  :Unite  get_function -direction=botright  


インストール


以下のソースを
.vim/bundle/unite-function/autoload/unite/sources
以下に配置。

"----------------------------------------------------------+
" get function                                             |
"----------------------------------------------------------+
let s:save_cpo = &cpo
set cpo&vim


" define source
let s:get_function = {
\   'name': 'get_function',
\   'action_table': {},
\   'default_action': {'common':'execute'},
\}


" bzr status
function! s:get_function.gather_candidates(args, context)

  let s:path = expand('%:p')
  let s:ext  = expand('%:e')
  let s:lines = getbufline('%', 1, '$')

  let s:func_list = []
  let s:line_number = 1

  for line in s:lines
    if line =~ 'function ' && s:ext == "php"
      call add(s:func_list, [s:line_number, line])

    elseif line =~ 'function! ' && s:ext == "vim"
      call add(s:func_list, [s:line_number, line])

    elseif line =~ 'def ' && s:ext == "py"
      call add(s:func_list, [s:line_number, line])

    endif
    let s:line_number += 1
  endfor

  return map(copy(s:func_list), '{
  \   "word": v:val[0]." ".v:val[1],
  \   "source": "get_function",
  \   "kind": "jump_list",
  \   "action__path": s:path,
  \   "action__line": v:val[0]
  \ }')

endfunction

"
function! unite#sources#get_function#define()
    return [ s:get_function ]
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

2012年1月7日土曜日

[vim] pathogenを導入

プラグインを入れるたび、だんだん.vimのなかがカオスになってきた。

vimプラグインの管理をpathogen.vimにした

これを入れると、.vim/bundle/以下を、.vim/以下と同じ要に読んでくれるらしい。

① インストール
tpope / vim-pathogen
から落として、.vim/autoloadに配置。

② プラグインをbundleに移動。
移動というか、一回消して、gitでbunldeいかに入れなおした。
入れなおしたのは。

+ NERD_Tree
+ neocomplecache
+ quickrun

③ 使ってみた感想。
すごく良い。
入れなおしたの3つだけなのに、.vimの中がすっきりした。
特に、NERD_Treeとかneocomplecacheとか、.vim/直下にいっぱいディレクトリ作るから、
それなくなっただけでもほんとにきれい。
しかも、プラグインのインストールが楽。
git コマンド1つでインストールが完了する。

次は、vimrcを整理したい。

[python] これは誰のせりふでしょう?→「また負けた・・・働いてないのに また負けちゃった・・・・・・ギャンブル負けちゃった・・・」

分類器とは、入力の特徴を元にクラス分けするプログラムの事。メールスパムフィルタ、テキストのカテゴリ分け、形態素解析の品詞推定などに使われている。今回調べてみた「単純ベイズ分類器」のほかにも、「決定木」、「最大エントロピー分類器」とかがあるみたい。

ここで丁寧に解説されている。ほぼほぼパクリw。
ナイーブベイズを用いたテキスト分類

統計的手法に基づくスパムフィルタの設計と実装
あと、NLTK本の6章。


分類器の動作手順

良くある分類器の処理の流れ。

① 素性抽出
まず、入力から素性を抽出する。ここでの入力は、任意の文章を想定している。素性とは、入力を分類をするための指標となる文字列で、どのように分類したいかによって変わる。たとえば、ブログのラベリングであれば、文章中の頻出語句を素性として使えるだろうし、話し手の性別を判定したいのであれば、文の語尾を素性として使えるかもしれない。ようは、分類に大きな影響を与えるものを素性として選ぶ。

② 機械学習
機械学習では、素性とラベルを対応付ける分類モデルを作成する。分類モデルの種類としては、決定木や単純ベイズ分類器のほかにも、ニューラルネットワーク、サポートベクターマシンとかかなりいろいろあるっぽい。分類モデルを作成するためには、もととなる素性とラベルの組み合わせデータが必要で人の手で作成される事が多いらしい。このデータから分類モデルを作成する事を訓練といい、訓練が必要となる分類モデルを教師あり分類と教師あり~って言われる。

③ 分類
入力から素性を抽出して、機械学習で作成した分類モデルを使って入力を分類する。


単純ベイズ分類器の仕組み


単純ベイズ分類器では、「文章Xが与えられたとき、そのラベルAである条件付き確率」を計算し、最も確率が高いラベルを文章に付与する。この条件付き確率 P(label|doc)を計算する。

① ベイズの定理より。

※ P(doc)はどのカテゴリでも共通なので無視。

② 文章を素性の集合と考え、それぞれの素性は独立に(ベイズ仮定)


③ P(label), P(word_i|label)を計算する。
P(label)は、「labelに属する文書 / 総文書数」で求められる。
P(word_i|label)は、「対象文書に含まれるword_iの数 / labelに属する文書の総単語数」で求められる。



※ ゼロ頻度問題に対応するため、P(wordi|label)の計算の際、出現回数に1を加えるラプラススムージングを行う。

④ アンダーフロー回避のため、logをとる。

※ logをとっても,P(label|doc)の大小関係は変化しないため、判定に影響はない。

⑤ ④に③を代入し、P(label|doc)を求める。


単純ベイズ分類器の実装


せっかくベイズ分類ができるので、
カイジに出てきたキャラクターの台詞を学習させ、
他のせりふが誰のものか判別できるかどうかためしてみた。

ここの台詞を学習させた。
心に刻め!!カイジの名言集

ソースコードは結構長いので最後に貼り付ける。

また、似たような事は潜在的意味解析でもできるが、
潜在的意味解析で判定できるのは、「与えた台詞が、誰のどの台詞に近いか」であって、
分類器でできる「与えた台詞が、誰の台詞に近いか」とは異なる。



まずは、カイジ君
「また負けた・・・働いてないのに また負けちゃった・・・・・・ギャンブル負けちゃった・・・」

結果
[karino@localhost simple_bayesian_classifier]$ ./simple_bayesian.py 

また負けた・・・働いてないのに また負けちゃった・・・・・・ギャンブル負けちゃった・・・

カイジ: -48.1215877591
利根川: -51.8378671316
会長  : -52.5295179405
遠藤  : -52.0766103197
大槻  : -52.5066907554

label :  カイジ
お。ちゃんとカイジと判定された!

次は、あえての大槻班長。
「大槻班長:食べ終わったら、奴はとりあえず満足してこう考えるだろう。明日からがんばろう。明日から節制だと。」

[karino@localhost simple_bayesian_classifier]$ ./simple_bayesian.py 

食べ終わったら、奴はとりあえず満足してこう考えるだろう。明日からがんばろう。明日から節制だと。

カイジ: -57.5804609164
利根川: -56.3075993467
会長  : -57.4567716257
遠藤  : -56.8765245825
大槻  : -55.7732979831

label :  大槻

おぉ。ちゃんと班長と判定されたw。かなり恣意的な気もするが。

最後を飾るのはもちろん利根川先生のこの台詞。
「ファックユー ぶち殺すぞ ゴミめら!」

[karino@localhost simple_bayesian_classifier]$ ./simple_bayesian.py 

ファックユー ぶち殺すぞ ゴミら!

カイジ: -24.2264367345
利根川: -23.9961446128
会長  : -24.4275136119
遠藤  : -23.9181559224
大槻  : -25.1875317303

label :  遠藤勇次
あぁ、遠藤さんになっちゃった。近いといえば近いが。

まぁ、こんな様な事ができるってデモでした。


#!/usr/bin/python
# coding=utf-8

import MeCab
import math
import sys
import codecs
from decimal import Decimal
from collections import defaultdict
reload(sys)
sys.setdefaultencoding('utf-8')
sys.stdout = codecs.getwriter('utf-8') (sys.stdout)

kaiji =[
        ["カイジ","おまえは100%成功しないタイプ…!"],
        ["利根川幸雄","一生迷ってろ…!そして失い続けるんだ…貴重な機会(チャンス)をっ!"],
        ["大槻","明日からがんばるんじゃない…今日…今日だけがんばるんだっ…!今日をがんばった者…今日をがんばり始めた者にのみ…明日が来るんだよ…!"],
        ["カイジ","奇跡なんて望むな!「勝つ」ってことは…そんな神頼みなんかじゃなく…具体的な勝算の彼方にある…現実だ…!勝つべくして勝つ…!"],
        ["カイジ","疑ってるうちはまだしもそれを口にしたら…戦争だろうがっ!"],
        ["利根川幸雄"," 金は命より重い…!そこの認識をごまかす輩は生涯地を這う…!"],
        ["兵藤和尊","大詰めで弱い人間は信用できぬ…!つまりそれは管理はできても勝負のできぬ男…平常時の仕事は無難にこなしても緊急時にはクソの役にも立たぬということだ!]",],
        ["兵藤和尊","剥げたな…お前の化けの皮…二流だ…しょせんお前は指示待ち人間…!",],
        ["カイジ","胸を張れっ!手痛く負けた時こそ…胸をっ…!",],
        ["利根川幸雄"," 大人は質問に答えたりしない それが基本だ",],
        ["カイジ","堂々といけっ…!やばい時ほど堂々と…",],
        ["カイジ","ピンチだけど…チャンスッ…",],
        ["遠藤勇次","おまえの毎日って今……ゴミって感じだろ?無気力で自堕落で非生産的",],
        ["岡林","友情や口約束でもらえるものは旅先からの絵ハガキや土産…あるいは思い出の品というガラクタ…そんな程度のものだ…",],
        ["利根川幸雄","なめてなどいない…熟知しているだけだ…人間の無力について…!",],
        ["カイジ","変でいい、変でなきゃダメだ…狂ってなきゃ、逸脱してなきゃ悪魔は殺せない…!常軌を逸してこそ開かれる、勝ちへの道が…!",],
        ["兵藤和尊","借金における誠意なんて、これはもう誰が考えたって一つしかないのだ…内臓を売ろうと、強盗をしでかそうと…何をしてもいいから、要するに…期限までに金を返すことだっ…!",],
        ["カイジ","敗者は失うっ…!それをねじ曲げたら…なにがなにやらわからない…受け入れるべきだっ…!負けを受け入れることが…敗者の誇り…オレは…負けをぼかさないっ…!",],
        ["カイジ","運・勘・人に頼る勝負はやめだ…そういうノータリンな振る舞いはもうやめ…自分の頭で考え…勝つべくして勝つ…",],
        ["利根川幸雄","よく戦ったからじゃない…彼らは勝った、ゆえに今、そのすべて…人格まで肯定されている…!",],
        ["カイジ","いっちゃ悪いが、奴ら正真正銘のクズ…負けたからクズってことじゃなくて可能性を追わないからクズ…",],
        ["カイジ"," 目先を追うな!いい加減気がつけ!耐えることなくして勝利はないんだっ!",],
        ["カイジ","他人なんか関係ねえんだよ…!オレだっ…!オレだっ…!オレなんだっ…!肝心なのはいつも…!オレがやると決めてやる…ただそれだけだっ…!",],
        ["遠藤勇次","一体「何」を「いつまで」待つつもりだ…?こんな薄汚いアパートで…貧しいバイトをして…半ば眠ったような意識で鬱々と「待つ」…?そういうのを無為っていうんだっ…!",],
        ["カイジ"," 落とさなきゃ落とされる…この仕組みは…この世の姿そのもの…基本も基本…大原則だっ…!",],
        ["カイジ","できるかどうかじゃない!やるんだっ!勝つために生きなくてどうするっ…!",],
        ["兵藤和尊","命はもっと粗末に扱うべきなのだ…!命は、生命は…丁寧に扱いすぎると澱み腐る",],
        ["ナレーション","いたずらに時間だけが流れていく…!まるで命そのもののような…血の時間が…",],
       ]

def get_kaiji_morpheme(sentences):
    data = []
    for title, sentence in sentences:
        data.append([title] + get_morpheme(sentence))
    return data

def get_morpheme(sentence):
    """Use Mecab"""
    t = MeCab.Tagger (" ".join(sys.argv))
    m = t.parseToNode(sentence)
    data = defaultdict(list)
    data = []

    while m:
        parse= m.feature.split(',')[0]
        if parse == "名詞" or parse == "動詞":
            #print m.surface
            data.append(m.surface)
        m = m.next
    return data

class NaiveBayes:
    """NaiveBayes"""

    def __init__(self):
        self.categories = set()
        self.vocabularies = set()
        self.wordcount = {}
        self.catcount = {}
        self.denominator = {}

    def train(self, data):
        """ナイーブベイズ分類器の訓練"""

        # 文章集合からカテゴリを抽出して辞書を初期化
        for d in data:
            cat = d[0]
            self.categories.add(cat)
        for cat in self.categories:
            #print cat
            self.wordcount[cat] = defaultdict(int)
            self.catcount[cat] = 0

        # 文章集合からカテゴリと単語をカウント
        for d in data:
            cat, doc = d[0], d[1:]
            self.catcount[cat] += 1
            for word in doc:
                self.vocabularies.add(word)
                self.wordcount[cat][word] += 1

        # 単語の条件付確率分布の分母をあらかじめ一括計算しておく
        for cat in self.categories:
            self.denominator[cat] = sum(self.wordcount[cat].values()) + len(self.vocabularies)

    def classify(self, doc):
        """事後確率の対数log(P(cat|doc))がもっとも大きなカテゴリを返す"""
        best = None
        max = -sys.maxint
        for cat in self.catcount.keys():
            p = self.score(doc, cat)
            if p > max:
                max = p
                best = cat
        return best


    def score(self, doc, cat):
        """文書が与えられたときのカテゴリの事後確率の対数"""
        total = sum(self.catcount.values())
        score = math.log(Decimal(self.catcount[cat]) / Decimal(total))
        for word in doc:
            score += math.log(self.wordprod(word, cat))

        return score

    def keywords(self):
        """単語の条件付き確率P(word|cat)を求める"""
        for cat in self.catcount.keys():
            print cat

    def wordprod(self, word, cat):
        """単語の条件付き確率P(word|cat)を求める"""
        # ラプラススムージングを適用
        return Decimal(self.wordcount[cat][word] + 1) / Decimal(self.denominator[cat])

    def __str__(self):
        total = sum(self.catcount.values())
        return "documents: %d, vocabularies: %d, categories: %d" % (total, len(self.vocabularies), len(self.categories))


# main
def main():
    #
    data = get_kaiji_morpheme(kaiji)

    # ナイーブベイズ分類器の訓練
    nb = NaiveBayes()
    nb.train(data)

    #print nb
    #nb.keywords()


    # テストデータのカテゴリを予測
    string = "また負けた・・・働いてないのに また負けちゃった・・・・・・ギャンブル負けちゃった・・・"
    string = "食べ終わったら、奴はとりあえず満足してこう考えるだろう。明日からがんばろう。明日から節制だと。"
    string = "ファックユー ぶち殺すぞ ゴミら!"

    test = get_morpheme(string)
    print
    print string
    print
    print "カイジ:", nb.score(test, "カイジ")
    print "利根川:", nb.score(test, "利根川幸雄")
    print "会長  :", nb.score(test, "兵藤和尊")
    print "遠藤  :", nb.score(test, "遠藤勇次")
    print "大槻  :", nb.score(test, "大槻")
    print
    print "label : ",nb.classify(test)


if __name__ == '__main__':
    main()

2012年1月5日木曜日

[python] mecab-pythonのインストールメモ。

mecab-pythonのインストールメモ。

① ダウンロード
mecab-python

② インストール
$ sudo python setup.py build
$ sudo python setup.py install

※ Mecab本体は別途インストールする。


③ コマンドラインから実行
[karino@localhost mecab-python-0.98]$ python
Python 2.6.6 (r266:84292, Dec  7 2011, 20:48:22) 
[GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import MeCab
>>> m=MeCab.Tagger("-Ochasen")
>>> print m.parse("命はもっと粗末に扱うべきなのだ…!命は、生命は…丁寧に扱いすぎると澱み腐る")
命      イノチ  命      名詞-一般               
は      ハ      は      助詞-係助詞             
もっと  モット  もっと  副詞-一般               
粗末    ソマツ  粗末    名詞-形容動詞語幹               
に      ニ      に      助詞-副詞化             
扱う    アツカウ        扱う    動詞-自立       五段・ワ行促音便        基本形
べき    ベキ    べし    助動詞  文語・ベシ      体言接続
な      ナ      だ      助動詞  特殊・ダ        体言接続
の      ノ      の      名詞-非自立-一般                
だ      ダ      だ      助動詞  特殊・ダ        基本形
…      …      …      記号-一般               
!      !      !      記号-一般               
命      イノチ  命      名詞-一般               
は      ハ      は      助詞-係助詞             
、      、      、      記号-読点               
生命    セイメイ        生命    名詞-一般               
は      ハ      は      助詞-係助詞             
…      …      …      記号-一般               
丁寧    テイネイ        丁寧    名詞-形容動詞語幹               
に      ニ      に      助詞-副詞化             
扱い    アツカイ        扱う    動詞-自立       五段・ワ行促音便        連用形
すぎる  スギル  すぎる  動詞-非自立     一段    基本形
と      ト      と      助詞-格助詞-引用                
澱み    ヨドミ  澱む    動詞-自立       五段・マ行      連用形
腐る    クサル  腐る    動詞-自立       五段・ラ行      基本形
EOS

>>> 

[python] バージョン管理ツールbazaarのフックスクリプトを書いてみた

今日、文末に「;」がついてないphpソースをコミットしてしまった。

2回も。

bazaarのフックスクリプトを書いた。

内容は、コミットするときに、対象となるphpファイルに対して、
「php -l」を実行し、エラーがあったら、コミットしないというもの。
pythonで簡単にフックスクリプトを書けるのはありがたい。
参考文献が少なくて結構手こずったけども。

ソース


"""this is a plugin/hook for bazaar. just add this file to ~/.bazaar/plugins/"""


import os
import subprocess
import commands
from itertools import chain
from bzrlib import branch
from bzrlib import errors

version_info = (0, 0, 1, 'dev', 1)


def get_php_files(tree_delta):
    """get php files"""
    php_files = []

    for delta_file in chain(tree_delta.added, tree_delta.modified):

        # not php file
        bzr_path, file_id, type_ = delta_file[:3]
        root, ext = os.path.splitext(bzr_path)

        # not php file
        if ext == ".php":
            php_files.append(bzr_path)
            continue

    return php_files


def pre_commit_hook(local, master, old_revno, old_revid, future_revno, future_revid, tree_delta, future_tree):
    """This hook will execute precommit script from root path of the bazaar
        branch. Commit will be canceled if precommit fails."""

    # fix flg
    errflg = 0

    # get root directory path
    root_path = commands.getoutput("bzr root")

    # check file
    for bzr_path in  get_php_files(tree_delta):
        # exe php -l
        absolute_path = root_path + "/" + bzr_path
        if subprocess.call(["php", "-l", absolute_path]):
            errflg = 1
        print ""

    # commit
    if errflg == 1:
        raise errors.BzrError("You hsve several bugs in your php files.")
    else:
        print "Congratulations! There are no errors."

branch.Branch.hooks.install_named_hook('pre_commit', pre_commit_hook, 'Check pre_commit hook')

「~/.bazaar/plugins/」以下におけば、自動で読み込まれるはず。

rootのパスを取るために「bzr root」を実行しているところがすごくいけてない・・・。
絶対、引数から持ってこれるとは思うけど、調べてもわからなかったから、
取り急ぎこれで。


結果


ダメなphpファイルをコミットしてみる。


コミット結果
karino@goldfish:~/bzr_test/bran/karino$ bzr commit -m 'karino' 
Committing to: /home/karino/bzr_test/karino/                                                                                                                          
modified karino.php
PHP Parse error:  syntax error, unexpected $end, expecting T_VARIABLE or T_DOLLAR_OPEN_CURLY_BRACES or T_CURLY_OPEN in /home/karino/bzr_test/bran/karino/karino.php on line 4
Errors parsing /home/karino/bzr_test/bran/karino/karino.php

bzr: ERROR: You hsve several bugs in your php files. 
このようにコミット拒否される。

phpファイルを修正してコミットしてみると。


karino@goldfish:~/bzr_test/bran/karino$ bzr commit -m 'karino' 
Committing to: /home/karino/bzr_test/karino/                                                                                                                          
modified karino.php
No syntax errors detected in /home/karino/bzr_test/bran/karino/karino.php                                                                                             

Congratulations! There are no errors.
Committed revision 4650.


参考になったページ


bazaarのプラグインを作る(1)

フックを利用する

how to execute tests on a bazaar pre-commit hook

jk0 / bzr-pre-commit-hook

2012年1月3日火曜日

[python] 辞書型形態素解析器の作成

NLTK本12章に形態素解析のアルゴリズムが紹介されていたので、実装してみた。
12.2 日本語形態素解析

Web+DB vol.64にも、日本語かな漢字変換器の実装として、同様の手法が紹介されているが、
これだけ読んで、実装するのはちょっと骨にひびはいるかも。
ざっくりと概略つかむにはいい。
WEB+DB PRESS vol.64に日本語入力の記事を書いたよ


辞書型形態素解析の流れ


① 辞書に基づいた形態素分解
入力した文字列に対して可能な形態素分割をすべて求める。具体的には、入力文字列の頭から抽出できる文字列で辞書を引き、ヒットしたものを列挙する。次に、抽出開始位置を1文字進めて再度文字列を抽出し、辞書を引き、ヒットしたものを列挙する。たとえば、「くるま」なら、「く(区)」「く (九)」「くる(来る)」「くるま(車)」などの辞書エントリを列挙する。これは「共通接頭辞探索」とよばれ、このアルゴリズムに適した辞書のデータ構造として「トライ構造」が使われている。


② ラティス構造の作成
共通接頭辞探索を先頭から順に行う際、得られた形態素とその位置で終わる形態素との組み合わせを考え、接続したグラフ構造を作成する。この構造は「ラティス構造」と呼ばれる。このラティス構造を作成する際、つまり、形態素同士を接続する際、日本語の文法に正しいかどうかを確認し、間違っている場合は接続しない。このように連接可能な形態素同士が接続したラティス構造が作成される。


③ もっとも適切な解析結果の取得
ラティス構造を作成した段階で、日本語の文法的に正しい解析結果を得ることができる。より日本語らしい解析結果を上位に持ってくるための手法の一つとして、「コスト最小法」がある。この方法は、「形態素と形態素の結びつきやすさ/にくさ」(連接コスト)、「形態素自体の出現のしやすさ/しにくさ」(生起コスト)を各形態素ごとに割り当て、文全体で見たときのコストの総和を計算することで、日本語らしい解析結果を得る。

※ コストそのものの導出は、機械学習で作成したり、
統計的な手法(隠れマルコフモデルや条件付き確率場)を使って作成、推定するらしい。


辞書型形態素解析の実装


とりあえず雰囲気は出ている。
#!/usr/bin/python
# coding=utf-8

import sys
from collections import defaultdict
reload(sys)
sys.setdefaultencoding('utf-8')

# 辞書
dict_entries = [
    [u"かれ",     {'pos':'N',   'lemma':u"彼",    'cost':  0}],
    [u"の",       {'pos':'J-K', 'lemma':u"の",    'cost':  0}],
    [u"く",       {'pos':'N',   'lemma':u"区",    'cost':  0}],
    [u"くる",     {'pos':'V-S', 'lemma':u"来る",  'cost':  0}],
    [u"くる",     {'pos':'V-T', 'lemma':u"来る",  'cost':  0}],
    [u"くるま",   {'pos':'N',   'lemma':u"車",    'cost':  0}],
    [u"ま",       {'pos':'N',   'lemma':u"間",    'cost':  0}],
    [u"まで",     {'pos':'J-F', 'lemma':u"まで",  'cost':  0}],
    [u"で",       {'pos':'J-K', 'lemma':u"で",    'cost':  0}],
    [u"でま",     {'pos':'N',   'lemma':u"デマ",  'cost':  0}],
    [u"まつ",     {'pos':'N',   'lemma':u"松",    'cost':  0}],
    [u"まつ",     {'pos':'V-S', 'lemma':u"待つ",  'cost':  0}],
    [u"まつ",     {'pos':'V-T', 'lemma':u"待つ",  'cost':  0}],
    [u"つ",       {'pos':'N',   'lemma':u"津",    'cost':  0}],
    [u"こ",       {'pos':'SF',  'lemma':u"個",    'cost': 10}],
    [u"この",     {'pos':'T',   'lemma':u"この",  'cost': 10}],
    [u"ひ",       {'pos':'N',   'lemma':u"日",    'cost': 40}],
    [u"ひと",     {'pos':'N',   'lemma':u"人",    'cost': 40}],
    [u"ひとこと", {'pos':'N',   'lemma':u"一言",  'cost': 40}],
    [u"と",       {'pos':'J',   'lemma':u"と",    'cost': 10}],
    [u"こと",     {'pos':'N',   'lemma':u"事",    'cost': 10}],
    [u"で",       {'pos':'V-Z', 'lemma':u"出",    'cost': 40}],
    [u"で",       {'pos':'V-Y', 'lemma':u"出",    'cost': 40}],
    [u"で",       {'pos':'J',   'lemma':u"で",    'cost': 10}],
    [u"げんき",   {'pos':'N',   'lemma':u"元気",  'cost': 40}],
    [u"に",       {'pos':'J',   'lemma':u"に",    'cost': 10}],
    [u"になっ",   {'pos':'V-Y', 'lemma':u"担っ",  'cost': 40}],
    [u"なっ",     {'pos':'V-Y', 'lemma':u"なっ",  'cost': 40}],
    [u"た",       {'pos':'A',   'lemma':u"た",    'cost': 10}]
]

# Dic class
class Dic:
    """辞書クラス"""

    # トライの作成関数
    @staticmethod
    def insert(trie, key, value):
        """trie"""
        if key:
            first, rest = key[0], key[1:]
            if first not in trie:
                trie[first] = {}
            Dic.insert(trie[first], rest, value)
        else:
            if not 'value' in trie:
                trie['value'] = []
            trie['value'].append(value)

    # トライの作成
    @staticmethod
    def mktrie(dict_entries):
        matrie = {}
        for entry in dict_entries:
            entry[1]['length'] = len(entry[0])
            Dic.insert(matrie, entry[0].encode('utf-8'), entry[1])

        return matrie

# Node class
class Node:
    """ラティス構造を構築するためのノード"""
    def __init__(self, lemma, pos, node_cost, begin):
        self.lemma = lemma
        self.pos   = pos
        self.node_cost = node_cost
        self.length = len(lemma)
        self.next = []
        self.cost = 0
        self.begin = begin

# 形態素解析
def analyze(trie, sent, connect_func=lambda x,y: True, cost_func=lambda x,y: 0):

    # 共通接頭辞探索
    def common_prefix_search(trie, key):
        ret = []
        if 'value' in trie:
            ret += trie['value']
        if key:
            first, rest = key[0], key[1:]
            if first in trie:
                ret += common_prefix_search(trie[first], rest)
        return ret

    # 結果表示用
    def enum_solutions(node):
        results = []
        if node.lemma == u'EOS':
            return [[u'EOS']]
        for nnode in node.next:
            for solution in enum_solutions(nnode):
                results.append([node.lemma]+solution)
        return results


    # BOSノードの定義
    bos_node = Node('BOS', 'BOS', 0, -1)
    end_node_list = defaultdict(list)
    end_node_list[0].append(bos_node)


    # 文頭から
    for i in range(0, len(sent)+1):

        # 共通接頭辞探索
        if i < len(sent):
            cps_results = common_prefix_search(trie, sent[i:].encode('utf-8'))
        else:
            # EOS
            cps_results = [{'lemma':u'EOS','length':3,'pos':'EOS','cost':0}]

        # 各形態素の連接可能性、コストを計算
        for centry in cps_results:
            cnode = Node(centry['lemma'], centry['pos'], centry['cost'], i)
            min_cost = -1
            min_bnodes = []

            # 連接可能性を確認
            for bnode in end_node_list[i]:

                # 連接可能性を確認
                if connect_func(bnode, cnode):

                    # 生起コスト+連接コスト
                    cost = bnode.cost + cost_func(bnode, cnode)

                    # コストが最小のものを選択
                    if min_cost < 0 or cost < min_cost:
                        min_cost = cost
                        min_bnodes = [bnode]
                    elif cost == min_cost:
                        min_bnodes.append(bnode)

            if len(min_bnodes) > 0:
                for bnode in min_bnodes:
                    cnode.cost = min_cost
                    bnode.next.append(cnode)

                end_nodes = end_node_list[i + centry['length']]
                if not cnode in end_nodes: end_nodes.append(cnode)

    return enum_solutions(bos_node)

# main
def main():
    """main"""

    # 連接可能判定関数
    def is_connectable(bnode, cnode):
        """is_connectable"""
        ctable = set([
            ('BOS', 'N'),('BOS', 'V'),('BOS','T'),
            ('T', 'N'),('N', 'J'),('J','N'),('J','V'),
            ('V-T', 'N'),('V-T', 'J-F'),('V-Y', 'A'),
            ('V-S', 'EOS'),('A', 'EOS'),
        ])

        bpos = bnode.pos
        bpos_s = bpos.split('-')[0]
        cpos = cnode.pos
        cpos_s = cpos.split('-')[0]

        #return True
        return ((bpos, cpos) in ctable) or ((bpos_s, cpos) in ctable) \
            or ((bpos, cpos_s) in ctable) or ((bpos_s, cpos_s) in ctable)

    # 連接コスト計算関数
    def cost_minimum(bnode, cnode):
        ctable = {
            ('BOS', 'T'): 10,
            ('T', 'N'):   10,
            ('N', 'J'):   10,
            ('J', 'N'):   10,
            ('N', 'N'):   10,
            ('N', 'V-Z'): 40,
            ('N', 'V-Y'): 40,
            ('V-Z', 'N'): 40,
            ('V-Y', 'N'): 40,
            ('J', 'V-Z'): 10,
            ('J', 'V-Y'): 10,
            ('V-Y', 'A'): 10,
            ('A', 'EOS'): 10
        }
        pos_2gram = (bnode.pos, cnode.pos)
        return cnode.cost + (ctable[pos_2gram] if pos_2gram in ctable else 100)


    # 辞書のtrieを作成
    matrie = Dic.mktrie(dict_entries)

    # 解析
    res = analyze(matrie, u"かれのくるまでまつ", is_connectable, cost_minimum)
    print "解析1"
    print '\n'.join('/'.join(sent) for sent in res)
    print

    # 解析
    res = analyze(matrie, u"このひとことでげんきになった", is_connectable, cost_minimum)
    print "解析2"
    print '\n'.join('/'.join(sent) for sent in res)
    print

if __name__ == '__main__':
    main()

実行結果
[karino@localhost jisyo]$ python analyze_fix.py 
解析1
BOS/彼/の/車/で/待つ/EOS

解析2
BOS/この/一言/で/元気/に/なっ/た/EOS



コストをすべてゼロにした場合の実行結果。
考えられるすべての組み合わせが出力される。
解析1
BOS/彼/の/来る/間/で/間/津/EOS
BOS/彼/の/来る/間/で/松/EOS
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/間/出/間/津/EOS
BOS/彼/の/来る/間/出/松/EOS
BOS/彼/の/来る/間/出/待つ/EOS
BOS/彼/の/来る/間/出/待つ/EOS
BOS/彼/の/来る/間/出/間/津/EOS
BOS/彼/の/来る/間/出/松/EOS
BOS/彼/の/来る/間/出/待つ/EOS
BOS/彼/の/来る/間/出/待つ/EOS
BOS/彼/の/来る/間/で/間/津/EOS
BOS/彼/の/来る/間/で/松/EOS
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/間/デマ/津/EOS
BOS/彼/の/来る/まで/間/津/EOS
BOS/彼/の/来る/まで/松/EOS
BOS/彼/の/来る/まで/待つ/EOS
BOS/彼/の/来る/まで/待つ/EOS
BOS/彼/の/来る/間/で/間/津/EOS
BOS/彼/の/来る/間/で/松/EOS
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/間/出/間/津/EOS
BOS/彼/の/来る/間/出/松/EOS
BOS/彼/の/来る/間/出/待つ/EOS
BOS/彼/の/来る/間/出/待つ/EOS
BOS/彼/の/来る/間/出/間/津/EOS
BOS/彼/の/来る/間/出/松/EOS
BOS/彼/の/来る/間/出/待つ/EOS
BOS/彼/の/来る/間/出/待つ/EOS
BOS/彼/の/来る/間/で/間/津/EOS
BOS/彼/の/来る/間/で/松/EOS
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/間/デマ/津/EOS
BOS/彼/の/来る/まで/間/津/EOS
BOS/彼/の/来る/まで/松/EOS
BOS/彼/の/来る/まで/待つ/EOS
BOS/彼/の/来る/まで/待つ/EOS
BOS/彼/の/車/で/間/津/EOS
BOS/彼/の/車/で/松/EOS
BOS/彼/の/車/で/待つ/EOS
BOS/彼/の/車/で/待つ/EOS
BOS/彼/の/車/出/間/津/EOS
BOS/彼/の/車/出/松/EOS
BOS/彼/の/車/出/待つ/EOS
BOS/彼/の/車/出/待つ/EOS
BOS/彼/の/車/出/間/津/EOS
BOS/彼/の/車/出/松/EOS
BOS/彼/の/車/出/待つ/EOS
BOS/彼/の/車/出/待つ/EOS
BOS/彼/の/車/で/間/津/EOS
BOS/彼/の/車/で/松/EOS
BOS/彼/の/車/で/待つ/EOS
BOS/彼/の/車/で/待つ/EOS
BOS/彼/の/車/デマ/津/EOS

解析2
BOS/個/の/日/と/個/と/で/元気/に/なっ/た/EOS
BOS/個/の/日/と/個/と/で/元気/担っ/た/EOS
BOS/個/の/日/と/個/と/出/元気/に/なっ/た/EOS
BOS/個/の/日/と/個/と/出/元気/担っ/た/EOS
BOS/個/の/日/と/個/と/出/元気/に/なっ/た/EOS
BOS/個/の/日/と/個/と/出/元気/担っ/た/EOS
BOS/個/の/日/と/個/と/で/元気/に/なっ/た/EOS
BOS/個/の/日/と/個/と/で/元気/担っ/た/EOS
BOS/個/の/日/と/事/で/元気/に/なっ/た/EOS
BOS/個/の/日/と/事/で/元気/担っ/た/EOS
BOS/個/の/日/と/事/出/元気/に/なっ/た/EOS
BOS/個/の/日/と/事/出/元気/担っ/た/EOS
BOS/個/の/日/と/事/出/元気/に/なっ/た/EOS
BOS/個/の/日/と/事/出/元気/担っ/た/EOS
BOS/個/の/日/と/事/で/元気/に/なっ/た/EOS
BOS/個/の/日/と/事/で/元気/担っ/た/EOS
BOS/個/の/人/個/と/で/元気/に/なっ/た/EOS
BOS/個/の/人/個/と/で/元気/担っ/た/EOS
BOS/個/の/人/個/と/出/元気/に/なっ/た/EOS
BOS/個/の/人/個/と/出/元気/担っ/た/EOS
BOS/個/の/人/個/と/出/元気/に/なっ/た/EOS
BOS/個/の/人/個/と/出/元気/担っ/た/EOS
BOS/個/の/人/個/と/で/元気/に/なっ/た/EOS
BOS/個/の/人/個/と/で/元気/担っ/た/EOS
BOS/個/の/人/事/で/元気/に/なっ/た/EOS
BOS/個/の/人/事/で/元気/担っ/た/EOS
BOS/個/の/人/事/出/元気/に/なっ/た/EOS
BOS/個/の/人/事/出/元気/担っ/た/EOS
BOS/個/の/人/事/出/元気/に/なっ/た/EOS
BOS/個/の/人/事/出/元気/担っ/た/EOS
BOS/個/の/人/事/で/元気/に/なっ/た/EOS
BOS/個/の/人/事/で/元気/担っ/た/EOS
BOS/個/の/一言/で/元気/に/なっ/た/EOS
BOS/個/の/一言/で/元気/担っ/た/EOS
BOS/個/の/一言/出/元気/に/なっ/た/EOS
BOS/個/の/一言/出/元気/担っ/た/EOS
BOS/個/の/一言/出/元気/に/なっ/た/EOS
BOS/個/の/一言/出/元気/担っ/た/EOS
BOS/個/の/一言/で/元気/に/なっ/た/EOS
BOS/個/の/一言/で/元気/担っ/た/EOS
BOS/この/日/と/個/と/で/元気/に/なっ/た/EOS
BOS/この/日/と/個/と/で/元気/担っ/た/EOS
BOS/この/日/と/個/と/出/元気/に/なっ/た/EOS
BOS/この/日/と/個/と/出/元気/担っ/た/EOS
BOS/この/日/と/個/と/出/元気/に/なっ/た/EOS
BOS/この/日/と/個/と/出/元気/担っ/た/EOS
BOS/この/日/と/個/と/で/元気/に/なっ/た/EOS
BOS/この/日/と/個/と/で/元気/担っ/た/EOS
BOS/この/日/と/事/で/元気/に/なっ/た/EOS
BOS/この/日/と/事/で/元気/担っ/た/EOS
BOS/この/日/と/事/出/元気/に/なっ/た/EOS
BOS/この/日/と/事/出/元気/担っ/た/EOS
BOS/この/日/と/事/出/元気/に/なっ/た/EOS
BOS/この/日/と/事/出/元気/担っ/た/EOS
BOS/この/日/と/事/で/元気/に/なっ/た/EOS
BOS/この/日/と/事/で/元気/担っ/た/EOS
BOS/この/人/個/と/で/元気/に/なっ/た/EOS
BOS/この/人/個/と/で/元気/担っ/た/EOS
BOS/この/人/個/と/出/元気/に/なっ/た/EOS
BOS/この/人/個/と/出/元気/担っ/た/EOS
BOS/この/人/個/と/出/元気/に/なっ/た/EOS
BOS/この/人/個/と/出/元気/担っ/た/EOS
BOS/この/人/個/と/で/元気/に/なっ/た/EOS
BOS/この/人/個/と/で/元気/担っ/た/EOS
BOS/この/人/事/で/元気/に/なっ/た/EOS
BOS/この/人/事/で/元気/担っ/た/EOS
BOS/この/人/事/出/元気/に/なっ/た/EOS
BOS/この/人/事/出/元気/担っ/た/EOS
BOS/この/人/事/出/元気/に/なっ/た/EOS
BOS/この/人/事/出/元気/担っ/た/EOS
BOS/この/人/事/で/元気/に/なっ/た/EOS
BOS/この/人/事/で/元気/担っ/た/EOS
BOS/この/一言/で/元気/に/なっ/た/EOS
BOS/この/一言/で/元気/担っ/た/EOS
BOS/この/一言/出/元気/に/なっ/た/EOS
BOS/この/一言/出/元気/担っ/た/EOS
BOS/この/一言/出/元気/に/なっ/た/EOS
BOS/この/一言/出/元気/担っ/た/EOS
BOS/この/一言/で/元気/に/なっ/た/EOS
BOS/この/一言/で/元気/担っ/た/EOS

[python] 日本語構文解析器CaboChaのおいしい食べ方

日本語構文解析器CaboChaをインストール。
ついでに、係り受け解析についてチョット調べた。

このCaboChaを使うと、日本語の文を文節に区切り、
その文節間の修飾関係(係り受け)を出力する事ができる。

ここのまんま。ありがとうございます。
YUMで一発、cabocha で係り受け解析

オライリーの12章にも参考になるところがあった。
Python による日本語自然言語処理、CaboChaを使う


まずはcabochaを頂戴してくる


sudo rpm -Uvh http://rtilabs.net/files/repos/yum/rh/6/x86_64/rtilabs-release-1-0.noarch.rpm
sudo yum install --enablerepo=rtilabs cabocha 
sudo yum install --enablerepo=rtilabs cabocha-python
ついでにpythonからcabochaを操作する奴も一緒に入れる。


pythonで、cabochaを食べてみる


① ソース
#!/usr/bin/python
# coding=utf-8

import CaboCha

def main():
    """main"""

    c = CaboCha.Parser('--charset=UTF8')
    sentence = u"日本語構文解析器CaboChaをインストールするのはyumを使うと激しく簡単だった".encode('utf-8')

    print c.parseToString(sentence)

    tree = c.parse(sentence)
    print tree.toString(CaboCha.FORMAT_TREE)
    print tree.toString(CaboCha.FORMAT_LATTICE)
    print tree.toString(CaboCha.FORMAT_XML)

if __name__ == '__main__':
    main()

② 実行結果(tree)
日本語構文解析器CaboChaを-D        
       インストールするのは-------D
                        yumを-D   |
                         使うと---D
                           激しく-D
                         簡単だった
EOS

③ 実行結果(XML)

 
  日本語
  構文解析器
  CaboCha
  
 
 
  インストール
  する
  
  
 
 
  yum
  
 
 
  使う
  
 
 
  激しく
 
 
  簡単
  だっ
  
 


chunkが文節、tokが形態素、linkが係っている先。
つまり、

「日本語構文解析木CaboChaを」→「インストールするのは」→「簡単だった」
「yumを」→「使うと」→「簡単だった」
「激しく」→「簡単だった」

という感じで係っていることがわかる。


cabochaを味わう


調べてみると、係り受けを出力するまでの流れとしては、以下の感じになるっぽい。
① 形態素解析:文章を形態素に分割
② 文節チャンキング:文節毎に形態素をグループ分け(チャンクを作成)
③ 係り受け解析:チャンク間の修飾関係を出力

どの段階にも、そのやり方にいろいろな種類があり、
1つ1つが深そう。

ルールベースの係り受け解析アルゴリズムが載ってたので試してみる。
Python による日本語自然言語処理 係り受け解析

ここで紹介されているのは、ルールベースの係り受け解析。
いくつかのルールを設定しておき、そのルールに従って、
修飾関係を決定する。

プログラムの内容は以下。

① 準備
・「cabocha2depgraph」で、CaboChaの出力データ(FORMAT_LATTICE)から、
nltkのDependencyGraphに形式変換
・「reset_deps」で、係り受け情報を削除

② 係り受け解析
・「set_head_form」で、各チャンクの主辞、語形を抽出。
・「get_dep_type」で、係り受けのルールを設定
・「analyze_dependency」で、係り受け解析を実施

ちょこちょこインデントが間違ってるので、
そのままコピペしても動かない。
チョット長いけど、ソースも張っておく。

#!/usr/bin/python
# coding=utf-8

import CaboCha
import re
from nltk.parse import DependencyGraph
import sys
reload(sys)
sys.setdefaultencoding('utf-8')


# CaboChaを使った係り受け解析(tree)
def cabocha_tree(sentence):
    """main"""

    c = CaboCha.Parser('--charset=UTF8')

    tree = c.parse(sentence)

    #print tree.toString(CaboCha.FORMAT_LATTICE)
    #print tree.toString(CaboCha.FORMAT_TREE)
    #print tree.toString(CaboCha.FORMAT_XML)

    return tree.toString(CaboCha.FORMAT_TREE)

# CaboChaを使った係り受け解析(lattice)
def cabocha_lattice(sentence):
    """main"""

    c = CaboCha.Parser('--charset=UTF8')

    tree = c.parse(sentence)

    #print tree.toString(CaboCha.FORMAT_LATTICE)
    #print tree.toString(CaboCha.FORMAT_TREE)
    #print tree.toString(CaboCha.FORMAT_XML)

    return tree.toString(CaboCha.FORMAT_LATTICE)

# CaboChaを使った係り受け解析結果をDependencyGraphの形式に変換
def cabocha2depgraph(t):
    """cabocha -> depgraph"""
    dg = DependencyGraph()
    i = 0
    for line in t.splitlines():
        if line.startswith("*"):
            # start of bunsetsu

            cells = line.strip().split(" ",3)
            m = re.match(r"([\-0-9]*)([ADIP])", cells[2])

            node = dg.nodelist[i]
            node.update(
                {'address': i,
                'rel': m.group(2), # dep_type
                'word': [],
                'tag': []
                })
            dep_parent = int(m.group(1))

            while len(dg.nodelist) < i+1 or len(dg.nodelist) < dep_parent+1:
                dg.nodelist.append({'word':[], 'deps':[], 'tag': []})

            if dep_parent == -1:
                dg.root = node
            else:
                dg.nodelist[dep_parent]['deps'].append(i)

            i += 1

        elif not line.startswith("EOS"):
            # normal morph

            cells = line.strip().split("\t")

            morph = (cells[0], tuple(cells[1].split(',')))
            dg.nodelist[i-1]['word'].append(morph[0])
            dg.nodelist[i-1]['tag'].append(morph[1])

    return dg

# CaboChaを使った係り受け解析結果のみを削除
def reset_deps(dg):
    for node in dg.nodelist:
        node['deps'] = []
    dg.root = dg.nodelist[-1]

# 主辞と語形の抽出
def set_head_form(dg):
    """set head form"""
    for node in dg.nodelist:
        tags = node['tag']
        num_morphs = len(tags)

        # extract bhead(主辞) and bform(語形)
        bhead = -1
        bform = -1
        for i in xrange(num_morphs-1, -1, -1):
            if tags[i][0] == u"記号":
                continue
            else:
                if bform == -1: bform = i
                if not (tags[i][0] == u"助詞"
                    or (tags[i][0] == u"動詞" and tags[i][1] == u"非自立")
                    or  tags[i][0] == u"助動詞"):
                    if bhead == -1: bhead = i

        node['bhead'] = bhead
        node['bform'] = bform

NEXT_NODE = 1
NEXT_VERB_NODE = 2
NEXT_NOUN_NODE = 3

# 係り受けのルール
def get_dep_type(node):
    """get_dep_type"""
    bform_tag = node['tag'][node['bform']]
    if bform_tag[0] == u"助詞" and bform_tag[1] == u"格助詞":
        return NEXT_VERB_NODE
    elif bform_tag[0] == u"助動詞" and bform_tag[-1] == u"タ":
        return NEXT_NOUN_NODE
    else:
        return NEXT_NODE

# ルールベースの係り受け解析
def analyze_dependency(dg):
    """analyze Dependency"""
    num_nodes = len(dg.nodelist)

    for i in xrange(0, num_nodes):
        dg.nodelist[i]['closed'] = False

    for i in xrange(num_nodes-1, 0, -1):
        node = dg.nodelist[i]

        if i == num_nodes - 1:
            to_node = 0
        elif i == num_nodes - 2:
            to_node = num_nodes -1
        else:
            dep_type = get_dep_type(node)
            if dep_type == NEXT_NODE:
                to_node = i + 1
            elif (dep_type == NEXT_VERB_NODE or
                  dep_type == NEXT_NOUN_NODE):
                for j in xrange(i+1, num_nodes):
                    node_j = dg.nodelist[j]
                    node_j_headtag = node_j['tag'][node_j['bhead']]
                    if (node_j['closed'] == False and
                        (dep_type == NEXT_VERB_NODE and node_j_headtag[0] == u"動詞") or
                        (dep_type == NEXT_NOUN_NODE and node_j_headtag[0] == u"名詞" and
                        node_j_headtag[1] != u"形容動詞語幹".encode('utf-8'))):
                        to_node = j
                        break

        node['head'] = to_node
        dg.nodelist[to_node]['deps'].append(i)
        for j in xrange(i+1, to_node):
            dg.nodelist[j]['closed'] = True

# main 
def main():

    def _node_map(node):
        """_node_map"""
        #node['word'] = '/'.join(node['word']).encode('utf-8')
        node['word'] = '/'.join(node['word'])
        return node

    # sentence定義
    sentence = u"日本語構文解析器CaboChaをインストールするのはyumを使うと激しく簡単だった".encode('utf-8')

    # CaboChaの係り受け解析結果を表示
    lattice = cabocha_lattice(sentence)
    dg = cabocha2depgraph(lattice)
    dg.nodelist = [_node_map(n) for n in dg.nodelist]
    print str(dg.tree())
    print "--------------"


    # CaboChaの結果から文節チャンクのみを抽出
    lattice = cabocha_lattice(sentence)
    dg = cabocha2depgraph(lattice)
    reset_deps(dg)


    # 主辞と語形の抽出
    set_head_form(dg)

    # 各節点のwordをunicodeからUTF8に変換
    dg.nodelist = [_node_map(n) for n in dg.nodelist]

    # 係り受け解析
    analyze_dependency(dg)

    # output
    print str(dg.tree())

if __name__ == '__main__':
    main()

結果は、こんな感じ。
(簡単/だっ/た                                                                                            
  (インストール/する/の/は                                                                               
    日本語/構文解析器/CaboCha/を)                                                                        
  (使う/と yum/を)                                                                                       
  激しく)                                                                                                
--------------                                                                                           
(簡単/だっ/た                                                                                            
  (激しく                                                                                                
    (使う/と (yum/を インストール/する/の/は))))
上がCaboChaでやった結果、下がルールベースの実装した結果。
結果はかなり違うけど、それは設定したルールがしょぼいってことかな。

[python] 遺伝的アルゴリズムって結構難しいのね

pythonの数値計算ライブラリについて調べてみた。
ぱっと見つかったのは、numpy, scipy, pyevolve。

scipy


この辺を参考にした。
Pythonによる数値計算
Python Scientific Lecture Notes

① インストール
バージョン古いけどめんどくさいからyumでいいや。
# yum install scipy
numpyも一緒に入る。

② 使い方
ぱっとみ、簡単そうに見える。
eigen3とかと変わらなそう。
使うときがきたら調べよう。
これインストールしとけば、潜在意味解析とかもいけそう。
SciPyを用いて潜在的意味解析(LSA)

PHPで潜在意味解析したときは、
わざわざeigen3のphp extensionとか作ってたのに。

PHPで潜在意味解析
kokukuma / php-lsa



pyevolveでナップサック問題を解く。

また、pythonで遺伝アルゴリズムを扱うライブラリ、
pyevolveが面白そうだったから入れておいた。

ここに書いてある事をまるっとぱくってやってみた。
pyevolveによる遺伝的アルゴリズム

[karino@localhost numerical_calculation]$ ./pyevolve_test.py 
Gen. 1 (0.20%): Max/Min/Avg Fitness(Raw) [4101.01(4101.00)/0.00(0.29)/3994.99(3994.99)]
Gen. 50 (10.00%): Max/Min/Avg Fitness(Raw) [4892.40(4101.00)/0.00(2901.00)/4794.55(4077.00)]
Gen. 100 (20.00%): Max/Min/Avg Fitness(Raw) [4101.02(4101.00)/0.00(0.29)/3890.97(3890.97)]
Gen. 150 (30.00%): Max/Min/Avg Fitness(Raw) [4101.02(4101.00)/0.00(0.29)/3878.97(3878.97)]
Gen. 200 (40.00%): Max/Min/Avg Fitness(Raw) [4101.03(4101.00)/0.00(0.29)/3724.94(3724.94)]
Gen. 250 (50.00%): Max/Min/Avg Fitness(Raw) [4101.03(4101.00)/0.00(0.29)/3770.96(3770.96)]
Gen. 300 (60.00%): Max/Min/Avg Fitness(Raw) [4101.03(4101.00)/0.00(0.29)/3688.96(3688.96)]
Gen. 350 (70.00%): Max/Min/Avg Fitness(Raw) [4101.02(4101.00)/0.00(0.29)/3806.96(3806.96)]
Gen. 400 (80.00%): Max/Min/Avg Fitness(Raw) [4101.02(4101.00)/0.00(0.29)/3864.97(3864.97)]
Gen. 450 (90.00%): Max/Min/Avg Fitness(Raw) [4101.01(4101.00)/0.00(0.29)/3994.99(3994.99)]
Gen. 500 (100.00%): Max/Min/Avg Fitness(Raw) [4834.80(4101.00)/0.00(3001.00)/4477.56(4029.00)]
Total time elapsed: 5.989 seconds.
[0, 0, 1, 1, 1]
商品C price: 1100 size: 5
商品D price: 1200 size: 7
商品E price: 1800 size: 8
total price: 4100 total size: 20

おぉ、ちゃんと結果出た。


遺伝的アルゴリズムを使って、条件を満たす数列を作成する。

おまけにもう一問やってみる。

以下の条件を満たす数列Aを計算する。
① Aのある位置から順に数字を取り出すと、最短X回ですべての種類の要素を取得できる。
② Aのどの位置から取り出しても、最大Y回ですべての要素を取得できる
③ 数字を取り出す際、末尾まできたら、先頭に戻る。

こんな数列を遺伝アルゴリズムを使って作成する。

#!/usr/bin/python
# coding=utf-8

from pyevolve import G1DBinaryString
from pyevolve import G1DList
from pyevolve import GSimpleGA
from pyevolve import Mutators

#GA setting
GENERATION = 500
POPULATION_SIZE = 50

# data
#BASE_ARRAY = [1,2,3,4,5,6,7]
BASE_ARRAY = [1,2,3,4,5,6]
SEQUENCE_LENGTH = 12
MAX_NUMBER = 11
MIN_NUMBER = 7

#
def calc_comp(start, chromosome):
    """要素コンプリートまでの抽出回数を計測"""
    """function documentation"""
    tmp = []
    for x in xrange(len(chromosome)):

        idx = start + x
        if idx >= len(chromosome): idx -= len(chromosome)

        if chromosome[idx] in BASE_ARRAY and chromosome[idx] not in tmp:
            tmp.append(chromosome[idx])
            if len(list(set(tmp))) == len(BASE_ARRAY):
                return x+1

def calc_max_min(chromosome):
    """要素コンプリートまでの最大・最小抽出回数を計測"""
    max = 0
    min = int(SEQUENCE_LENGTH)
    for x in xrange(len(chromosome)):
        num = calc_comp(x, chromosome)
        if num > max: max = num
        if num < min: min = num
    return (max, min)


# 評価関数
def eval_func(chromosome):
    """要素コンプリートまでの最大・最小抽出回数を計測"""

    #score = 30 
    score = 0

    # 異なる値が多いほうが評価
    tmp = []
    for value in list(set(chromosome)):
        if value in BASE_ARRAY and value not in tmp:
            tmp.append(value)
            score += len(tmp)
        else:
            score += 0

    # 要素コンプリートまでの最大・最小抽出回数を計測
    maxn, minn = calc_max_min(chromosome)

    # 要素コンプリートまでの最大・最小抽出回数を計測
    if maxn == 0:
        score = score
    elif maxn == MAX_NUMBER and minn == MIN_NUMBER:
        score = score * 2 + 30
    else:
        score = score * 2 - abs(maxn - MAX_NUMBER) - abs(MIN_NUMBER - minn)


    # 最低でも0を返す
    if score < 0: score = 0
    return score

# main
def main():
    """遺伝アルゴリズムの計算"""
    genome = G1DList.G1DList(SEQUENCE_LENGTH)
    genome.evaluator.set(eval_func)
    genome.setParams(rangemin=1,rangemax=max(BASE_ARRAY))

    # シンプルGAの作成
    ga = GSimpleGA.GSimpleGA(genome)
    ga.setGenerations(GENERATION)
    ga.setPopulationSize(POPULATION_SIZE)

    # GAの開始 (途中経過を非表示にしたい場合はfreq_stats=0にする)
    ga.evolve(freq_stats=50)
    best = list(ga.bestIndividual())
    print best
    maxn, minn = calc_max_min(best)
    print "max : ",
    print maxn
    print "min : ",
    print minn


if __name__ == '__main__':
    main()
    #print calc_comp(0, [3,1,2,4,5,6,1,2,3])
出力結果
[karino@localhost numerical_calculation]$ ./pyevolve_sequence.py 
Gen. 1 (0.20%): Max/Min/Avg Fitness(Raw) [40.34(72.00)/30.36(15.00)/33.62(33.62)]
Gen. 50 (10.00%): Max/Min/Avg Fitness(Raw) [71.35(72.00)/40.06(39.00)/59.46(59.46)]
Gen. 100 (20.00%): Max/Min/Avg Fitness(Raw) [72.17(72.00)/38.70(39.00)/60.14(60.14)]
Gen. 150 (30.00%): Max/Min/Avg Fitness(Raw) [69.34(72.00)/23.01(15.00)/57.78(57.78)]
Gen. 200 (40.00%): Max/Min/Avg Fitness(Raw) [76.68(72.00)/26.19(40.00)/63.90(63.90)]
Gen. 250 (50.00%): Max/Min/Avg Fitness(Raw) [74.30(72.00)/34.99(40.00)/61.92(61.92)]
Gen. 300 (60.00%): Max/Min/Avg Fitness(Raw) [80.33(72.00)/0.00(39.00)/67.48(66.94)]
Gen. 350 (70.00%): Max/Min/Avg Fitness(Raw) [77.47(72.00)/23.67(41.00)/64.56(64.56)]
Gen. 400 (80.00%): Max/Min/Avg Fitness(Raw) [77.42(72.00)/22.22(40.00)/64.52(64.52)]
Gen. 450 (90.00%): Max/Min/Avg Fitness(Raw) [75.17(72.00)/32.34(40.00)/62.64(62.64)]
Gen. 500 (100.00%): Max/Min/Avg Fitness(Raw) [73.73(72.00)/7.40(15.00)/61.44(61.44)]
Total time elapsed: 52.364 seconds.
[3, 3, 6, 5, 2, 5, 4, 1, 4, 2, 6, 1]
max :  11
min :  7

一応それっぽくなった。
でも、元の要素増やすとすぐできなくなる。

問題はまぁ、評価関数の作り方でしょうな。
ほしい結果に適した評価関数を作る基本的な考え方ってあるのかな。

[python] 自然言語処理ライブラリ NLTKをインストール

NLTK触ったときのメモ。

NLTKのインストール


Pythonの自然言語処理用パッケージNLTKをインストール
$ wget http://nltk.googlecode.com/files/nltk-2.0.1rc1.tar.gz
$ tar -xvzf nltk-2.0.1rc1.tar.gz
$ cd nltk-2.0.1rc1
$ sudo python setup.py install
$ python

>>> import nltk
>>> nltk.download()
NLTK Downloader
---------------------------------------------------------------------------
    d) Download      l) List      c) Config      h) Help      q) Quit
---------------------------------------------------------------------------
Downloader> d
Download which package (l=list; x=cancel)?
Download which package (l=list; x=cancel)?
  Identifier> book
    Downloading collection 'book'
       | 
       | Downloading package 'brown' to /home/karino/nltk_data...
       |   Unzipping corpora/brown.zip.
       | Downloading package 'chat80' to /home/karino/nltk_data...
       |   Unzipping corpora/chat80.zip.
       | Downloading package 'cmudict' to /home/karino/nltk_data...
       |   Unzipping corpora/cmudict.zip.
       | Downloading package 'conll2000' to /home/karino/nltk_data...
       |   Unzipping corpora/conll2000.zip.
       | Downloading package 'conll2002' to /home/karino/nltk_data...
       |   Unzipping corpora/conll2002.zip.
       | Downloading package 'dependency_treebank' to
       |     /home/karino/nltk_data...
       |   Unzipping corpora/dependency_treebank.zip.
       | Downloading package 'genesis' to /home/karino/nltk_data...
       |   Unzipping corpora/genesis.zip.
       | Downloading package 'gutenberg' to /home/karino/nltk_data...
       |   Unzipping corpora/gutenberg.zip.
       | Downloading package 'ieer' to /home/karino/nltk_data...
       |   Unzipping corpora/ieer.zip.
       | Downloading package 'inaugural' to /home/karino/nltk_data...
       |   Unzipping corpora/inaugural.zip.
       | Downloading package 'movie_reviews' to
       |     /home/karino/nltk_data...
       |   Unzipping corpora/movie_reviews.zip.
       | Error with downloaded zip file
---------------------------------------------------------------------------
    d) Download      l) List      c) Config      h) Help      q) Quit
---------------------------------------------------------------------------
Downloader> q
True
>>> from nltk.book import *
*** Introductory Examples for the NLTK Book ***
Loading text1, ..., text9 and sent1, ..., sent9
Type the name of the text or sentence to view it.
Type: 'texts()' or 'sents()' to list the materials.
text1: Moby Dick by Herman Melville 1851
text2: Sense and Sensibility by Jane Austen 1811
text3: The Book of Genesis
text4: Inaugural Address Corpus
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python2.6/site-packages/nltk/book.py", line 33, in 
    text5 = Text(nps_chat.words(), name="Chat Corpus")
  File "/usr/lib/python2.6/site-packages/nltk/corpus/util.py", line 68, in __getattr__
    self.__load()
  File "/usr/lib/python2.6/site-packages/nltk/corpus/util.py", line 56, in __load
    except LookupError: raise e
LookupError: 
**********************************************************************
  Resource 'corpora/nps_chat' not found.  Please use the NLTK
  Downloader to obtain the resource: >>> nltk.download().
  Searched in:
    - '/home/karino/nltk_data'
    - '/usr/share/nltk_data'
    - '/usr/local/share/nltk_data'
    - '/usr/lib/nltk_data'
    - '/usr/local/lib/nltk_data'
**********************************************************************


nltk.download()で「book」を選択すれば、使いそうなものを全部インストールしてくれる。
[-] book................ Everything used in the NLTK Book

>>> from nltk.book import * で、ダウンロードしたデータを読み込むはずだが、
corpora/nps_cahtが見つからないと。
もう一回、nltk.download()でall-corporaをダウンロードした。
合計630M。

>>> from nltk.book import *
*** Introductory Examples for the NLTK Book ***
Loading text1, ..., text9 and sent1, ..., sent9
Type the name of the text or sentence to view it.
Type: 'texts()' or 'sents()' to list the materials.
text1: Moby Dick by Herman Melville 1851
text2: Sense and Sensibility by Jane Austen 1811
text3: The Book of Genesis
text4: Inaugural Address Corpus
text5: Chat Corpus
text6: Monty Python and the Holy Grail
text7: Wall Street Journal
text8: Personals Corpus
text9: The Man Who Was Thursday by G . K . Chesterton 1908


簡単な使い方

>>> text1.concordance("monstrous")
>>> text1.similar("monstrous")
>>> text2.similar("monstrous")
>>> text4.common_contexts(["monstrous","very"])
>>> text3.generate()
>>> len(text3)
>>> from __future__ import division
>>> text4.count('a') / len(text4)
テキストを検索、その単語と同じ文脈で使われる単語、
2つの単語が共通して使われている文脈
文中の単語数、出現する単語のカウントとか、いろいろできるらしい。