2013年2月5日火曜日

[Python] try/except/else/finally, with/asなどについてわからなくなったら

初めてのPython(27章) | kobakoba0723の日記
このお方のブログを見る。

[Python] with文を使った実行時間計測クラス(コンテキストマネージャー)

時間計測にはtimeitとかもありますが、もっとお気楽に測る場合の話です。
Matlab - tic, toc functions analog in Python
ここで紹介されていたやり方です。with文では、withの後ろに与えたコンテキストマネージャクラスがステートメント内に入るときに__enter__が呼ばれ(本来asで受け取る変数を返す)、終わりに__exit__が呼ばれます。これを利用して__enter__で時間計測開始を、__exit__で時間計測結果の出力を行い、with文のステートメントの中のプログラムの実行時間を表示します。with文の詳細については「python with文」でぐぐってください。
# context_timer.py
import time
import sys

class Timer(object):
    def __init__(self, name=None, out=sys.stdout):
        self.name = name
        self.out = out

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        if self.name:
            print >> self.out, '[%s]' % self.name,
        print >> self.out, 'Elapsed: %s' % (time.time() - self.tstart)
これを使って、1から1000までの整数の和を計算する時間を測る場合は次のようにwith文を使います。
from context_timer import Timer
with Timer("Sum"):
    val = 0
    for i in xrange(1, 1001):
        val += i
    print val
出力は次のようになります。
500500
[Sum] Elapsed: 0.00104784965515

2013年2月3日日曜日

Windows 7 64bit でPython環境構築

科学計算をやっている人向けにPythonの環境を簡単に構築できるPython(x,y)は非常に便利ですが、32bit向けのインストーラしか用意されていません。そのため、64-bit版を自分でビルドする必要があります。BLASだATLASだ、色々依存するライブラリをインストールしなくてはならないので結構大変です。そこで、UCIのGohlkeさんという方が公開しているインストーラ群を利用します。

まず、Pythonの64bit版を自分でインストールします。
例えば、Pythonのバージョン2.7でのインストールの場合、下記URLから「Python 2.7.3 Windows X86-64 Installer」のリンクをクリックしてインストーラをダウンロード、インストールしてください。
http://www.python.org/getit/
http://www.python.org/ftp/python/2.7.3/python-2.7.3.amd64.msi(インストーラ直リンク)

ここまでは非常に簡単です。本来、ココからがNumpyやScipyなどをインストールするのは大変な作業ですが、Gohlkeさんのページにあるインストーラを使えば簡単です。感謝です。
Unofficial Windows Binaries for Python Extension Packages
インストールしたいパッケージを探し、利用するPythonのバージョンと「amd64」をファイル名に含むインストーラをダウンロードしてインストールしてください。例えば、Scipyをインストールする場合、「scipy-0.11.0.win-amd64-py2.7.‌exe」を利用します。「Requires Numpy-MKL. 」と書かれているので、「numpy-MKL-1.6.2.win-amd64-py2.7.‌exe」もインストールします。この要領でその他のパッケージもインストールできます。


2013年2月1日金曜日

Numpy.Arrayが整数(Integer)型か浮動少数(Float)型か調べる

数値計算していると、整数型の配列を数値で割ったりすると
>>> a= np.ones(10, dtype=np.uint8)
>>> a /= 10.0
>>> print a
[0 0 0 0 0 0 0 0 0 0]

てな具合に0.1ではない結果になってしまう。

こんな場合は、arrayが整数型か浮動少数型か分けて処理をしたいとか、整数型だったら例外を投げるとかいう処理する必要があります。
例えば、
def normalize(arr):
    arr /= arr.sum()
というメソッドがあったとして、これに整数型を渡すと切り捨てられてしまうので、浮動少数型に限定したい。この場合、np.issubdtypeというメソッドで型が整数か浮動少数か調べます。
def normalize(arr):
    if np.issubdtype(arr.dtype, np.integer):
        raise ValueError("arr must be float.")
    arr /= arr.sum()
みたいな感じ。浮動少数か調べるにはnp.floatを使えばいいはず。
他に良い方法があったら教えて欲しいです。

2013年1月13日日曜日

Scipy.WeaveでPythonにC++埋め込み 3 変数を返す

変数を返す

数値型を返す

def test_ret_int():
    print "---- Return int ----"
    code = """
    return_val = 1;
    """
    ret = weave.inline(code)
    print "ret:", ret

Cの中でint型作成して返すサンプルです。Cのコードから値を返すときには、決してreturn文を使わないでください。weave内では、codeに書かれたコードに到達する前にreturn_valというSCXXのpy::objectが宣言されています。(生成されたcppファイルを見るとわかります。)return_valに代入された変数はweave.inlineメソッドの戻り値として受け取ることができます。APIを見るとわかりますが、py::objectのCの数値型や文字列型を代入すると、自動的にPythonのオブジェクトに変換してくれます。結果は次のようになります。
---- Return int ----
ret: 1

タプルなどを返す


def test_ret_tuple():
    print "---- Return tuple ----"
    code = """
    if(scxxFlag) {
        // scxx
        py::tuple ret(2);
        py::object tmp(1); ret.set_item(0, tmp);
        tmp = 3.5; ret.set_item(1, tmp);
        return_val = ret;
    } else {
        // Python C API is more reasnable.
        return_val = Py_BuildValue("id", 1, 3.5);
    }
    """
    scxxFlag = 1
    ret = weave.inline(code, ["scxxFlag", ])
    print "ret (scxx):", ret
    scxxFlag = 0
    ret = weave.inline(code, ["scxxFlag", ])
    print "ret (Py/C API):", ret
return_valはpy::object型なので、SCXXのpy::tuple, py::dictなどを代入することも可能ですし、Python/C APIのPyObject*を渡すこともできます。ここでは、if文のscxxFlagが1の場合はSCXXでtupleを、0の場合はPython/C APIでtupleを作成しています。(boolの受け渡しはサポートしていないっぽいので、scxxFlagをbool型にしませんでした。) 出力は以下。
---- Return tuple ----
ret (scxx): (1, 3.5)
ret (Py/C API): (1, 3.5)
両方とも同じ結果になっていますね。 Python/C APIの Py_BuildValue はとても便利なので使った方が良いと思います。フォーマットのiはint、dはdouble, sは文字列(char*), OはPyObject*を意味します。 listもdictも簡単に生成できるので試してみてください。
return_val = Py_BuildValue("[i,d]", 1, 3.5);// List
return_val = Py_BuildValue("{s:i,s:[d,s]}", "abc", 1, "def", 3.5, "Hoge"); // Dict
前回の例とあわせて、これで値の受け渡しができるようになりました。次回は、数値計算を利用している方向けにNumpyのArrayを利用した例を見ていきます。

Scipy.WeaveでPythonにC++埋め込み 2 変数を渡す

変数を渡す

数値型を渡す

以下はInt型をPythonからCに渡して標準出力するメソッド例です。
def test_pass_int():
    print "---- Pass int ----"
    val = 1
    code = """
    std::cout << "val: " << val << std::endl;
    """
    weave.inline(code, ['val', ], headers=['', ])
結果は次のようになります。
---- Pass int ----
val: 1
weave.inlineの第2引数はargs=[]のオプション引数であり、Cのコード内に渡したいPython側の変数名を文字列のリストとして渡します。すると、inlineはPython側でローカル変数またはグローバル変数で使われている変数を探します。そして、見つかった変数を適切な型に変換してCコードの中で使えるようにしてくれます。上の例ではvalはPythonのPyInt型なのでC側ではそのまま使えないはずですが、ちゃんとCのint型に変換してくれます。ここでさり気なく、C++スタイルの標準出力プリントを使いました。cout, endlを使うには<iostream>が必要なので、オプション引数headersにヘッダの文字列リストを与えています。

タプルとかを渡す

タプルやリストや辞書型を渡したときにどのように扱わうか見てみます。
def test_pass_tuple():
    print "---- Pass tuple ----"
    seq = (1, 0.5, "Hoge")
    code = """
    std::cout << "length: " << seq.len() << std::endl;
    std::cout << "seq[0]: " << static_cast<int>(seq[0]) << std::endl;
    std::cout << "seq[1]: " << static_cast<double>(seq[1]) << std::endl;
    std::cout << "seq[2]: " << static_cast<char>(seq[2]) << std::endl;
    """
    weave.inline(code, ['seq', ], headers=['<iostream>', ])
数値型や文字列型以外のタプルやリストなどのオブジェクトはSCXXというPython C APIのC++ラッパーライブラリクラスに変換されます。しかし、そのまま使っているわけではなく、weave向けに独自に名前とか書き換えているっぽいです。tuple型はpy::tupleに変換されます。APIは直接weave.__file__の下のscxxフォルダにある*.hファイルを読めばすぐわかります。一応ここ( http://projects.scipy.org/scipy/browser/trunk/Lib/weave/scxx)でも見れます。py::list::operator[]でタプルの中の要素をpy::object(正確にはindexed_ref型)として取り出すことができます。py::objectには、数値型、str型、complex型の変換演算子が定義されているので、代入やキャストによって中身をCの型として取り出すことができます。
出力は以下。
---- Pass tuple ----
length: 3
seq[0]: 1
seq[1]: 0.5
seq[2]: Hoge
リストの場合もSCXXのpy::listに変換され、APIはほぼ同じです。
def test_pass_list():
    print "---- Pass list ----"
    seq = [1, 0.5, "Hoge"]
    code = """
    std::cout << "length: " << seq.len() << std::endl;
    std::cout << "seq[0]: " << static_cast<int>(seq[0]) << std::endl;
    std::cout << "seq[1]: " << static_cast<double>(seq[1]) << std::endl;
    std::cout << "seq[2]: " << static_cast<char>(seq[2]) << std::endl;
    """
    weave.inline(code, ['seq', ], headers=['<iostream>', ])
出力は以下
---- Pass list ----
length: 3
seq[0]: 1
seq[1]: 0.5
seq[2]: Hoge
辞書型はSCXXのpy::dict型に変換されます。APIも非常に簡単です。
def test_pass_dict():
    print "---- Pass dict ----"
    dct = {"PyInt":1, "PyFloat":0.5, "PyString":"Hoge"}
    code = r"""
    std::cout << "length: " << dct.len() << std::endl;
    std::cout << "dct[\"PyInt\"]: " << static_cast<int>(dct["PyInt"]) << std::endl;
    std::cout << "dct.has_key(\"PyInt\"): " << dct.has_key("PyInt") << std::endl;
    std::cout << "dct.has_key(\"PyHoge\"): " << dct.has_key("PyHoge") << std::endl;
    py::list keys = dct.keys();
    std::cout << "keys[0] : " << static_cast<char>(keys[0]) << std::endl;
    py::object tmp = py::object(634);
    dct.set_item("SkyTree", tmp);
    std::cout << "dct[\"SkyTree\"] : " << static_cast<int>(dct["SkyTree"]) << std::endl;
    """
    weave.inline(code, ['dct', ], headers=['<iostream>', ])
出力は以下。
---- Pass dict ----
length: 3
dct["PyInt"]: 1
dct.has_key("PyInt"): 1
dct.has_key("PyHoge"): 0
keys[0] : PyInt
dct["SkyTree"] : 634
例にはないですが、変数は参照として渡されているのでinlineの中で変更したdict型は呼び出し側でも変更されます。

今回は変数を渡す例をお見せしました。次回は変数を返す例を見てみます。

2013年1月11日金曜日

Ubuntu10.04LTSにPython2.7.3をインストール numpy,scipy,matplotlib,opencv,pygtk

ソースからインストールします。全然難しくありません。

sqliteパッケージ使うのに必要だったはず
sudo apt-get install sqlite3

インストール先を指定(ルートインストールしない場合)
IPX=$HOME/python27
export PATH=$IPX/bin:$PATH
export LD_LIBRARY_PATH=$IPX/lib:$LD_LIBRARY_PATH
rootでインストールしてしまう場合は
sudo su -でルートになる。
ルートインストールする場合は--prefixオプションはいらないです。

Python-2.7.3をダウンロードして解凍
./configure --prefix=$IPX --enable-shared && make install

EasyInstallをインストール
サイトから setuptools-0.6c11-py2.7.eggをダウンロード
sh setuptools-0.6c11-py2.7.egg --prefix=$IPX

Pipも
$IPX/bin/easy_install pip

ついでにNumpyやら必要なものをインストール

$IPX/bin/pip install numpy
sudo apt-get install liblapack-dev gfortran g++
$IPX/bin/pip install scipy
$IPX/bin/pip install ipython
$IPX/bin/pip install virtualenv virtualenvwrapper
$IPX/bin/pip install matplotlib
再ログインなどするとパスが通っていないので、
IPX=$HOME/python27
export PATH=$IPX/bin:$PATH
export LD_LIBRARY_PATH=$IPX/lib:$LD_LIBRARY_PATH
を.bashrcなどに追加して常にパスが通っているようにしておく。(rootインストールの場合は必要なし)

ついでにOpenCVもインストールする。

sudo apt-get install cmake-curses-gui
wget http://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.3/OpenCV-2.4.3.tar.bz2
ダウンロードして解凍。 cmakeに必要なオプションを渡してインストール。
ccmake .. -DCMAKE_INSTALL_PREFIX=${IPX} -DPYTHON_EXECUTABLE=${IPX}/bin/python -DPYTHON_INCLUDE_DIR=${IPX}/include/python2.7 -DPYTHON_LIBRARY=${IPX}/lib/libpython2.7.so
make install 
OK。
$ ipython
>>> import cv2
で使えます。

PyGTKも

依存関係を考慮してシステムのバージョンに合わせる
pygtk 2.17.0
pycairo 1.8.10
pygobject 2.21.1

py2cairoをインストール
wget http://cairographics.org/releases/py2cairo-1.8.10.tar.gz
tar xvjf py2cairo-1.8.10.tar.gz
cd py2cairo-1.8.10
./waf configure --prefix=$IPX
./waf build
./waf installl
pygobjectインストール
sudo apt-get install libffi-dev
http://ftp.gnome.org/pub/GNOME/sources/pygobject/2.21/pygobject-2.21.1.tar.bz2
tar xvjf pygobject-2.21.1.tar.bz2
cd pygobject-2.21.1
./configure --prefix=$IPX
make install
pygtkインストール
sudo apt-get install libgtk2.0-dev
export PKG_CONFIG_PATH=$IPX/lib/pkgconfig:$PKG_CONFIG_PATH
wget http://ftp.gnome.org/pub/GNOME/sources/pygtk/2.17/pygtk-2.17.0.tar.bz2 
あとはPyGObjectと同じ