keisukeのブログ

***乱雑です!自分用のメモです!*** 統計や機械学習の勉強と、読み物を書く練習と、備忘録用のブログ

【Python】 progressbar

進捗を示すためにshell上にプログレスバーを表示したいことがよくあります.

50% (200 of 400) |############            |

こんなやつです.

Pythonでも,重い処理をforループで回すときに欲しくなります. 当然同じことを考える人がいるので,すでにパッケージがあります.

python progressbar」で検索して一番上に出てくるパッケージ(progressbar)はpython3に対応してない上,開発がだいぶ前に止まっているので,今回は別のパッケージ(progressbar2)を使います.

続きを読む

WindowsでanacondaのEnvironment管理をする

参考:How to activate an Anaconda environment - Stack Overflow

何をしたいの?

AnacondaのEnvironment管理に関する公式のドキュメントが、特にWindows環境においてはあまり充実しているとはいえないので、調べた結果を(自分用のメモも兼ねて)和訳して残したいです。 MacOS/Linux系のドキュメントは充分豊富にある上に、Windowsで頻発しがちな問題も起こらないので、Windows環境での設定について書きます。

続きを読む

IPython notebook (Jupyter) と IPython qtconsole を同じIPythonのカーネルで動かす

2015-05-23追記:
IPython Notebook version 3 (Jupyter)からは、どうやらカスタムjavascriptが変わったようですので、%username%\.ipython\profile_default\static\customにある、custom.jsの末尾に挿入するコードは次のようになります:

require([
     'base/js/namespace',
     'base/js/events'
 ], function(IPython, events) {
     events.on('app_initialized.NotebookApp', function(){
         IPython.toolbar.add_buttons_group([
             {
                 'label'   : 'run qtconsole',
                 'icon'    : 'fa-terminal fa', // select your icon from http://fortawesome.github.io/Font-Awesome/icons
                 'callback': function () {
                     IPython.notebook.kernel.execute('qtconsole')
                 }
             }
             // add more button here if needed.
             ]);
     });
     events.on('app_initialized.NotebookApp', function() {
        IPython.CodeCell.options_default.cm_config.autoCloseBrackets = false;
     });
 });

+追記終わり+

2015/03/11追記:
ipython notebookが立ち上がった時に読むjavascriptを編集することで、簡単にqtconsoleを同じカーネルで動かすことが出来ます。 具体的には、
%username%\.ipython\profile_default\static\customにある、custom.jsというファイルの末尾に次を挿入:

$([IPython.events]).on('app_initialized.NotebookApp', function(){
    IPython.toolbar.add_buttons_group([
        {
             'label'   : 'run qtconsole',
             'icon'    : 'icon-terminal', // select your icon from http://fortawesome.github.io/Font-Awesome/icons
             'callback': function () {
                 IPython.notebook.kernel.execute('%qtconsole')
             }
        }
        // add more button here if needed.
        ]);
});

これだけです。 ただし、%username%\.ipython\profile_default\static\customの場所は異なる場合があります。デフォルトでなにもいじっていないなら問題無いと思います。

上記の設定をしたあとにいつも通りipython notebookを起動すれば、画面上部のツールバーに「>_」というアイコンが増えているはずです。 そのボタンを押せば、同じカーネルでqtconsoleが立ち上がり、notebookを汚さずに変数の中身やヘルプのチェックがおこなえます。f:id:kaisk:20150311221620p:plain

ちなみに、設定をいっさいしなくても、notebookに%qtconsoleと打ち込んで実行すれば同じカーネルでqtconsoleが立ち上がります。

+追記終わり+


IPython notebook はとても便利なアプリで,ブラウザ上でインタラクティブPythonのシェルを叩けます. しかし,基本的にはコードのブロックを次々と入力していき,しかも出力や入力のログが残り続けます. 「この変数はなんだっけ?」と思ってprint(x)などとだけ書いたコードを評価すると,そのprint(x)とその出力のログは残り続けるのです(というか,入力と出力のログを残すことがnotebookの役割なので正しいわけですが).

しかし,やっぱり変数の中身だけちょっと見てみたいとか,「これで決定!」なわけではないコードを試してみたいとか,とにかく「ログに残ってほしくない」ようなとき,shellで動くIPythonの便利さが欲しくなります. だからといって新たにshellでIPythonを起動しても,notebook上の変数を調べるためにはnotebookの入力を全部取ってきてshellにコピペしなければなりません.

そこで,同じカーネルでnotebookとqtconsole*1を動かすことを考えます. notebookもqtconsoleも,実はそれ自体はpythonのコードを実行してはいません.裏で動いている「カーネル」と呼ばれるpythonのプロセスに入力を渡して実行してもらい,その出力を受け取って表示しているだけです.よって,その「カーネル」を共有してnotebookとqtconsoleで使えば良いわけです.

まずはipython notebookを起動しましょう.ファイル名を指定して実行,またはshell(terminal, cmd.exe, powershellなど)上で

ipython notebook

とします. おなじみの画面がブラウザに表示され,同時にカーネル用のウィンドウ(シェルのウィンドウ)が立ち上がると思います. なにかnotebookを開いて,カーネルを起動させます. notebookを開くと,先ほどのシェルのウィンドウに「Kernel started: ***」のように表示されます.この状態で,今までどおりnotebookは編集できます. さらに同じカーネルを使ったqtconsoleを立ち上げるには,ファイル名を指定して実行,またはshell上で

ipython qtconsole --existing

とします. --existingオプションは,ipythonを最後に起動したカーネルを使って立ち上げるという意味です. もしもすでにカーネル複数立ち上がっていて(=複数のnotebookを編集していて),どのカーネルを使ってqtconsoleをたちあげたいか指定するときには,使いたいカーネルのウィンドウに表示されている「Kernal started:」のあとの文字列と同じものを使って

ipython qtconsole --existing kernel-文字列.json

とします. もしもカーネルの番号がわからなくなった場合,notebookで

%connect_info

を実行すれば教えてくれます."key"がそうです.

*1:shell上のipythonみたいなものです.shellよりちょっと強力な機能がついています.

事前確率,事後確率,尤度,...

確率と統計の基礎(事前確率,事後確率,尤度,ベイズの法則,...)を勉強していると,何度も何度も見たことがある説明がなんとなく理解しづらく,難しく思えることがよくあります.

例えば,

x が与えられたとき y となる確率を p(y\mid x) と書き,事後確率と呼ぶ.」

という文章はあらゆるところで見かけると思います. もちろん確率を勉強した人であれば何度も見た文章なので理解が難しいものではないのですが,それでもこの文章に「尤度」やら「事前確率」やら「カルバックライブラダイバージェンス」やらが混じり始めると,どこかで理解のほつれが出始めることがよくあると思います.

先ほどの文章の何が問題かというと,p(y\mid x) は「yの事後確率」であって「xの事後確率」ではない点です. xに事後確率が存在しないわけではなく,この場合だと p(x\mid y) はxの事後確率です.

丁寧に説明されている場合は,

x が与えられたとき y となる確率を p(y\mid x) と書き,y事後確率と呼ぶ.」

とわざわざ書いてくれますが,基礎向け以外の解説だとよく省略して単に「事後確率」と呼ばれています.

というのも,統計では普通 xy ではなく,観測データ x とパラメータ \theta で話を進めるからです. 統計では,

「パラメータ \theta により決まっているらしい確率分布 p から得られたデータ x をもとに,その背後にある法則(=パラメータ)を予測せよ」

という問題を解くことが基本なので,x は与えられるもの,観測されるものという前提が暗黙にあります. よって,単に「事後確率」と言った場合,「xに関する事後確率 p(x\mid \theta)ではなく\thetaに関する事後確率p(\theta\mid x)*1を指します.

では p(x\mid \theta) がなんと呼ばれているかというと,「(\thetaの)尤度」です. つまり,\thetaが与えられた時のxの事後確率は\thetaの尤度です.

一方,p(\theta)は,「\theta の事前確率」とは呼ばずに,単に「事前確率」と呼びます.

これらの暗黙の了解が頭にしっかりと入っていないと*2,急に出てきた「事後確率」「事前確率」などの言葉に混乱して「どっちの変数が与えられてて,どっちの変数が変数のままなのか」問題を処理できていないまま数式を読み進めることになり理解が追いつかなくなります.

まとめると,
観測データx
パラメータ\thetaと書くとき,
暗黙に
「事後確率」は「データxが与えられた時のパラメータ\thetaの事後確率 p(\theta\mid x)」を指す.
「尤度」は「パラメータ\thetaを決めたときのデータxの事後確率 p(x\mid\theta)」を指す.
「事前確率」は「パラメータ\thetaの事前確率p(\theta)」を指す.

*1:x与えられた時の\thetaの確率

*2:

Windowsバージョン予測

世間はWindows10のニュースで賑わっていますが,なぜWindows8のあとが9ではなく10なのでしょうか. 「Windows9」にすると,これまでのWin9xシリーズであるWindows95Windows98などを検出するためのコードにWindows9も引っかかってしまい,後方互換に問題が出るのでWindows10にしたという噂もどこかで聞きましたがよくは知りません.

ところで,次のグラフはWindowsの製品に含まれる数字を縦軸,発売日を横軸にとったものです:

f:id:kaisk:20150125192806p:plain

2000系列が明らかに外れ値となっています.おそらくMicrosoftの方が命名を間違えたのでしょう.98の次が2000になるのは小学生が見ても誤りですね.98年の二年後が2000年なんて馬鹿なことはありません.98年の二年後は100年です.そこで,Microsoftの方が命名を間違えた製品を除いたグラフは:

f:id:kaisk:20150125193114p:plain

このようになります.まだ95や98が外れ値になっていますが,2000と比べればエラーが小さくなったのでそこまで気にしなくてもいいでしょう.98→2000に比べれば,3.1→98は大した間違いではありませんね.

さて,では本当にWindows10という命名は妥当だったのでしょうか?これまでのWindowsの製品の命名から,2015年に発表されるべきWindowsの製品名を予測しましょう. 線形回帰します.

f:id:kaisk:20150125193911p:plain

Ridgeの線が見えませんが,Lassoのものと重なっています.これまでのWindowsの製品名から予測した結果,2015年に発売されるべきWindowsは,OLSによると「Windows 17.346」,Ridge・Lassoによると「Windows 14.913」のようです.実際に発売されるであろうWindowsが「10」であることを考えると,あまり良い予測ではありあません.そこで,やはりMicrosoftの方が製品名を付け間違えたであろう「Windows95」「windows98」をデータから除いて,同じことをしました:

f:id:kaisk:20150125194318p:plain

今度は3つの線がすべて重なっています.予測された2015年のWindowsの製品名は「Windows 7.678」です.8より小さくなってしまいました.しかし,データを見ると,1995年のあたりで明らかに傾向が変わっています.これは,その頃にMicrosoftの方が命名の規則を忘れたのでしょう.そこで,WindowsNT系列のデータを切り出します:

f:id:kaisk:20150125194619p:plain

だいぶいい感じになってきました.予測された製品名は「Windows 8.638」,四捨五入すれば「Windows 9」です!やはりwindows10ではなく9が正統な製品名なのでしょう.参考までに,最後のデータによって予測された2020年の製品名は「Windows 9.90」,つまりWindows10は5年くらい時代を先取りしているようです.ちなみにWindows98が発売されるのは西暦2400年ごろ,Windows2000が発売されるのは西暦10000年ごろのようです.

なお,Windowsの製品名ではなくちゃんと内部バージョン名を見てみるともっとちゃんとできそうです(やりませんけど). 現在主流のNT系の内部バージョン名のグラフを見ると,どうやら線形ではなく頭打ちのlog曲線を描いているように見えますね(やりませんけど). 今回はネタなので製品名にはいっている数字を予測しましたが,ちゃんと内部バージョン名を予測しようとしたらけっこうよい精度が出そうです.

f:id:kaisk:20150125195425p:plain

f:id:kaisk:20150125195501p:plain

f:id:kaisk:20150125195504p:plain

f:id:kaisk:20150125195507p:plain

【Python】sliceの速度

listのsliceの速度がどれくらいかと思って実験してみました.

問題

長さlのlistがあるとします.その連続したr要素の和の最大値を求めます:

x = [1,2,3,10,11,2]  # l = 6: 長さ6のlist
# sum([1,2,3])  # r = 3: 連続した3要素の和
# sum([2,3,10])
# sum([3,10,11])
# sum([10,11,2])
# 一番大きな値はどれ?

方法1 (sliceと呼ぶことにします)

まず,r要素のsliceをとってsumを取る方法:

def slice_sum_max(x, r):
    max_val = -np.inf
    for i in range(len(x)-r+1):
        sum_val = sum(x[i:i+r])
        if sum_val > max_val:
            max_val = sum_val 
    return max_val

方法2 (squeezeと呼ぶことにします)

先頭を押し出して,新しい要素を足していく方法:

def squeeze_sum_max(x, r):
    max_val = sum(x[:r])
    sum_val = max_val
    last_ele = x[0]
    for i in range(len(x)-r+1):
        sum_val -= last_ele
        last_ele = x[i+r]
        sum_val += last_ele
        if sum_val > max_val:
            max_val = sum_val
    return max_val

結果

長さ10000の場合:

f:id:kaisk:20150125021320p:plain

長さ100000の場合:

f:id:kaisk:20150125021328p:plain

どっちも変わらない結果になりました. なお,listのまま関数に渡すか,np.ndarrayに変換してから関数に渡すかを変えていますが,やはりndarrayに要素アクセスすると遅くなるようです.

xargsメモ

xargsで実行したいコマンドに2回引数を渡したいときは,
例えば画像をpngからjpgへ変換したいときは,

$ find *.png | xargs -I{} convert {} {}_.jpg

などとするとよい.
この例では,convert前の画像の名前と,後の画像の名前を指定しなければならないので,-I{}によって{}という変数に一旦格納している.