format記法の使い道
pythonでは変数の情報を文字列にする方法がいくつかありますが、その中でも僕はformat記法をよく使っています。
特に最近「dictionaryを文字列にしたいなぁ」という場合に、とてもきれいに書くことができるということを発見(というかドキュメントを読んで判明した)ので「こういう場合に便利ですよ」という意味合いでメモしておきます。
Case: dictionaryからstringに変換したい
例えば、機械学習なんかをやろうと思うと、パラメータをディクショナリとして渡す場面がよくあると思います。仮にそのパラメータをparamsとして以下のように定義します。
params = { 'estimator':'svm', 'gamma':0.1, 'eta':100, }
このパラメータを使って学習した結果を保存しよう、となると「このパラメータ使いましたよ」という名前で保存したくなります。そういうときにdictionary→stringに変換したくなります。
単純に要素をすべて列挙してそれをつなげる、という風にすると以下のようなプログラムが考えられます。
pstr = '' for key,val in params.items(): if pstr != '': pstr += '_' pstr += key + '-' +str(val) print(pstr) # <<< 'estimator-svm_eta-100_gamma-0.1'
これでいいように感じるのですが、一つ問題があります。
dictionaryは入っている要素の順番を保存していないので、実験を繰り返しているとタイトルの順番が変化してしまう場合があるのです。(実際にあってとても困りました)
'estimator-svm_eta-100_gamma-0.1' これが 'eta-100_estimator-svm_gamma-0.1' こんな風になる場合があるってこと
また先のスクリプトでは辞書の値をすべてstr
を用いて文字に変換しています。これも問題があって、例えばfloatをstrを用いて文字にすると桁数の指定ができないのでstr(val)[:2]
のようにして長さを揃えたりしなくてはならず、しかもこれをすると四捨五入等も行ってくれないので、まあ言ったらちょっと不便です。
そこで最近はformat記法を用いて文字列に変換するようにしています。
estimator = 'svm' gamma = 0.166 eta = 100 pstr = 'estimator-{0}_gamma-{1:.2f}_eta-{2}'.format(estimator,gamma,eta) print(pstr) # estimator-svm_gamma-0.17_eta-100
{}の中の番号がformatで与えた引数の順番に対応しています。また同時にそれをどういう形でstrにするかも指定できるので、先の例だとgamma
を下二桁の少数で、という指定をしているので0.17として出力されています。
これでもだいぶ便利なのですが、コードを書いていて「新しい変数hoge
も名前の先頭に追加したいなー」となったとき、先頭にhogeを追加すると、順番がひとつづつずれてしまうので、0,1,2を1,2,3にしなくてはならなくなり、面倒です。
これを回避するためには、最後にhogeを追加して、3番めの引数を文字列の先頭に持ってくる、というformat内部の並びと、文字の対応が取れないコードになってしまいます。ちょっとアホっぽいですね。
hoge = 0.2 estimator = 'svm' gamma = 0.166 eta = 100 pstr = 'hoge-{3:.3f}_estimator-{0}_gamma-{1:.2f}_eta-{2}'.format(estimator,gamma,eta,hoge) print(pstr) # hoge-0.200_estimator-svm_gamma-0.17_eta-100
辞書の展開わたし
これを回避するのが、辞書を展開して渡してあげる、という方法です。(format(**some_dict)
)
展開した辞書の値をほしいときには{key}としてあげます。具体的には一番最初の例だとこんな感じ。
pstr = 'estimator-{estimator}'.format(**params) print(pstr) # estimator-svm
この書き方の良い点は、stringの中にkeyを書くことになるので、ぱっと見たときにこの文字列が何になるのか、がわかりやすいという点です。
短所として長くなると手で書いているのが面倒になるという点がありますが、それでも読みやすいわかりやすいというメリットは大きいと思います。
しかしこの方法は文字にしたいものが最初からdictionaryになっている場合にしか使えません。
locals()
なのですが、これと組み込み関数のlocals()
を用いるとその場合でも楽に変換できます。
localsはその場に定義されている変数を辞書にして返す関数です。なのでこれとformatを組み合わせると、その場にある変数の文字列を先の記法で作ることができます。
val = 100 name = 'some_method' tstr = 'val={val}_name={name}'.format(**locals()) print(tstr) # val=100_name=some_method
0,1,2と指定する場合に比べて、明らかに見やすいですね。
まとめ
localsとか展開渡しとか全然知らずにアホっぽいコード量産していました。ドキュメントはちゃんと読みましょう。
pythonの自作モジュールimportで困ったこと
自作モジュールをimportしたい時
同じ階層にある時はsample.pyであれば
import sample
でやればOKです.
でもあまりファイル数が多くなってくると,一定のまとまりでフォルダに入れて管理したくなります.そういう時は適当なフォルダにスクリプトを入れて,そのフォルダ内に__init__.py
を入れておけば,pythonは再帰的にフォルダ内部の.py
ファイルを読み込んでくれるので,importができるようになります.
今回詰まったこと
フォルダ構造は以下のような感じ
sample.py
import numpy as np def sample(x): x = np.random.normal(size=100) print(x.mean())
begin.py
from sample import sample sample()
これをtest_module
の親ディレクトリにある以下のファイルからimportしようとするとエラーが起こりました.
from test_module import begin begin.sample()
File "/Users/NYer510/python-tutorial/test_module/begin.py", line 2, in <module> from sample import sample ImportError: No module named 'sample'
解決法
結論から言うと,encodingを指定すると直りました.(# -- coding: utf-8 --とかのやつ)
理由はわかりません.謎です.
python の Xgboost を Windows で使いたい!
pythonをwindowsでやるなよ、という意見はごもっともですがでもやりたい時だってあるじゃん?なのでやりましょう。
環境
- windows10
- 64bit
必要なものたち
git cloneする
git bash を起動して、xgboostをgithubから手に入れます。
ここで注意するポイントが2つ
git clone
したあとに submodule を綺麗にして入れ直すこと。- 現在の最新版(2016/06/28現在)のmasterには問題があるようなのでちょっと前の commit に戻すこと。
コマンドとしてはgit checkout 9a48a40
.
Error installing xgboost · Issue #1267 · dmlc/xgboost · GitHub
$ git clone --recursive https://github.com/dmlc/xgboost $ cd xgboost $ git checkout 9a48a40 $ git submodule init $ git submodule update
MinGW-W64のインストール
次にMinGW-W64をインストールします。この時に architecture を x84_64 にセットしてインストールします。加えてデフォルトではC:\Program File
直下にインストールすることになっていますが、空文字を避けるためにC:\mingw64
とか適当にCドライブ直下に新しくフォルダを作り、そこを指定します。
Pathの設定
環境変数設定からPath
を追加します。追加するのはさっきインストールしたMingw64の中にあるmingw32-make.exe
が入っているフォルダです。C:\mingw64\mingw64\bin
とかになるはず。
どうでもいいけどwindows10になって環境変数の編集画面がすごく見やすくなりましたね。個人的には好きです。
コンパイル
make の設定を行ってからコンパイルします。以下のコマンドを先のgit bash 上の、xgboostをコピーした階層で行います
$ cp make/mingw64.mk config.mk $ make -j4
なんかもにゃもにゃ出るので待ちます。
インストール
成功していれば xgboost の階層に xgboost.exe ができているはずです。python パッケージをインストールする場合には python-pakage フォルダで setup.py を実行します。
$ cd python-package $ python setup.py install
結構長いですね。はやくconda install xgboost
でwindowsでも入れられるような幸せな世界になって欲しいです。
参考
pythonによる固有値固有ベクトル計算
pythonで固有値固有ベクトルを計算しようと思うと numpy.linalg (線形代数の便利関数のモジュール)以下にある numpy.linalg.eigs を使うことになります。
numpy.linalg.eig — NumPy v1.11 Manual
この挙動が個人的な感覚とちょっとずれていて、そこでかなり消耗してしまったのでメモとして残しておきます。
nump.linalg.eigs でハマる
numpy にはnumpy.linalg.eigs
という関数が用意されています。
返り値として第一変数に固有値(eigenvalues)、第二変数に正規化された(すなわち \( \|u_i\|=1 \) )固有ベクトル(eigenvector)を返します。
例
X = # 適当な(n,n)のnp.array
eig_vals,eig_vec = np.linalg.eigs(X)
ここで1点注意点があります。
http://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.eig.html
Returns:
w : (..., M) array The eigenvalues, each repeated according to its multiplicity. (以下略
v : (..., M, M) array The normalized (unit “length”) eigenvectors, such that the column v[:,i] is the eigenvector corresponding to the eigenvalue w[i].
重要なのは、index を i とした時に固有値 eig_vals[i]
に対応する固有ベクトルは eig_vec[i]
ではないということ!!
正しくはeig_vec[:,i]
が対応します。
この問題のやっかいな点がデバックすることが困難である点です。
なぜなら、インデックスを入れる次元を間違えて eig_val[i] としても、その配列の長さは正しい場合と同じ M になり、ベクトルの長さでは判定できません。また「固有ベクトル同士は直行する」という条件を使ってデバッグをしようとしても、間違えたベクトルに対しても成り立ってしまいます。すなわちi=jの時だけ
eig_vec[i].dot(eif_vec[j])=1
が成り立ちます。ですから間違えたindexを与えた際にも固有ベクトルの特徴である「お互いが直交する」という性質も満たしてしまっています。
したがって、計算した結果がなんだかおかしい、ということ以外でのデバッグが不可能になります。僕はこれで3時間ぐらい持って行かれました。おとなしく最初にドキュメントを読めばいいのですが。ちょっと慣れてくると適当にやってもそれっぽく出来てしまうのはダメだなと改めて感じました。ドキュメント大事。
までも普通に考えて、i番目の固有ベクトルだからeig_vec[i]
でいいと思うしソッチのほうが自然な定義な気がしてならないんですが、なぜそうなっていないのか……