python_tips_備忘録

はじめに

コードを書いていると、「あ、これ前もググって実装したやつだ」ということがよくある。 しかもどうやったか全く覚えていない。悲しい。 あげく検索ワードすらわからないときが頻繁にある。悲しい。

これ以上、そのような悲しいことを起こさないために、たまに使う細かい処理について書いていく。 間違っている部分や黒魔術的な部分もあるかもしれないので注意。 随時追加するつもり。 番号は参考サイトを示している。サンプルコードについて非常に参考にした。

for文が正しく終了したときに、処理を実行する。(for...else) [1]

正常終了するとelseというのが、なんとなく慣れない。いちいちフラグとかたてなくてもいい。

for i in range(10):
    print i
else:
    print "hoge"
0
1
2
3
4
5
6
7
8
9
hoge
for i in range(10):
    print i
    break
else:
    print "hoge"
0

コマンドライン引数を処理する。(argparser, click) [2]、[3]

コマンドライン引数を処理するモジュールとして、argparserとclickがある。まずはargpaeser。

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-n', '--n_iter', default=1000, type=int, help="num_iteration")
parser.add_argument('-g', '--gpu', action='store_true')
args = parser.parse_args()

print args
print args.n_iter
print args.gpu
$ python temp.py -n 10 --gpu
Namespace(gpu=True, n_iter=10)
10
True

続いてclick。最近知ったので、使い方がまだ良くわかっていないが、こちらのほうがコンパクトに書けて便利な気がする。

import click

@click.command()
@click.argument("name")
@click.option("-n", "--num_iter", type=int, help="num iteration")
def main(name, num_iter):
    for i in range(num_iter):
        msg = "hello %s %d" % (name, i)
        print msg

if __name__ == '__main__':
    main()
$ python temp2.py world -n 10
hello world 0
hello world 1
hello world 2
hello world 3
hello world 4
hello world 5
hello world 6
hello world 7
hello world 8
hello world 9

ファイル名に時間を埋め込む 。(datetime)[4]

結果の出力など、うっかり上書きしないようにするときによく使う。

from datetime import datetime
filename = "results/" + datetime.now().strftime("%Y%m%d_%H%M%S") + ".csv"
print filename
results/20151130_225340.csv

ファイルを開く。(with open... as f)

普通にopenだけで開くと後でcloseしないといけない。忘れるので、withを使う。 自作のクラスもこういうものに対応させておくと後で便利かも。

with open("temp.csv") as f:
    for i in f:
        print i

プログラムの進行状況などをprintするときに改行せず上書きする。 (sys.stdout.write)

ループの中で、print i などと書くと、いちいち改行されて鬱陶しい。 以下のコードでいい感じに進行状況を表示できる。

import sys
import time
for i in range(100):
    sys.stdout.write("\riter=%d" % i)
    sys.stdout.flush()
    time.sleep(0.1)
iter=99

ファイル名で検索してリストを取得する。(glob)

paramsディレクトリ内に存在する全てのパラメータファイルを読んでいって、順番に実行するとか、resultsディレクトリ以下の全てのファイルの平均値をとるとか、やりたいときに使う。

import glob

print glob.glob("./*")
print glob.glob("./*.py")
['./tips.org', './temp.csv', './Untitled.ipynb', './temp1.py', './temp2.py', './temp10.py']
['./temp1.py', './temp2.py', './temp10.py']

文字中に含まれる数字でソート。[5]

ファイル名などに数字が付いている時、普通にsortメソッドを使うと、2の前に10が来たりしてしまう。はじめから%05dとかで書いておけば不要。

import re
files = glob.glob("./*.py")
temp = [(re.search("[0-9]+", x).group(), x) for x in files]
temp.sort(cmp = lambda x, y: cmp(int(x[0]), int(y[0])))
print  [x[1] for x in temp]
['./temp1.py', './temp2.py', './temp10.py']

別のディレクトリのファイルをインポートする。(sys.path.append)

昔作ったコードを再利用したいとき、コピーしてきてもいいけど、これでもOK。

import sys
sys.path.append("../kaggle/mnist/")
import nn
net = nn.Perceptron(10,2)

ある条件を満たす数列を生成する。(リスト内包表記、ジェネレータ)

リスト内包表記をごちゃごちゃすると色々できる。 あまり使いすぎるとわかりにくいので、ほどほどに使う。

x = [i**2 for i in range(10) if i%2==0]
print x
[0, 4, 16, 36, 64]

ジェネレータでもいける。yieldは遅延評価なので、1つ値を生成するコストが大きいかつ、いくつ必要かわからないときとかいいかもしれない。

def generator():
    counter = 0
    while(True):
        yield counter**2
        counter += 2
for i in generator():
    print i
    if i>=64:
        break
0
4
16
36
64

学習データからランダムなミニバッチを作る。( np.random.permutation(N) ) [6]、[7]

SGDとかやるときに使う。

    def train(self, inputs, labels, tests):
        N = len(inputs)
        inputs = np.array(inputs).astype(np.float32)
        labels = np.array(labels).astype(np.int32)
        tests = np.array(tests).astype(np.float32)

        for epoch in range(self.num_epoch):
            print "epoch: %d" % epoch
            perm = np.random.permutation(N)
            for i in range(0, N, self.batchsize):
                x_batch = cuda.to_gpu(inputs[perm[i:i + self.batchsize]])
                y_batch = cuda.to_gpu(labels[perm[i:i + self.batchsize]])

KFold Cross Validation(sklearn.cross_validation.KFold) [8]

一瞬自分でつくろうかと思ったけど、探してみたらやっぱりあったscikit_learn。 自分で書くコードは最小限にする方針からこれを使う。

kf = cross_validation.KFold(len(all_vec), n_folds=10, shuffle=True)
for train_index, test_index in kf:
    test_vec = all_vec[test_index]
    test_label = labels[test_index]
    train_vec = all_vec[train_index]
    train_label = labels[train_index]

特徴ベクトル(の一部)を正規化する。(sklearn.preprocessing.MinMaxScaler) [9]

sklearnに正規化の関数があるので、それを使う。1ofKの変数とかは正規化しなくてもいいと思うので、一部だけやる例。

min_max_scaler = sklearn.preprocessing.MinMaxScaler()
no_normalize_index = 8
min_max_scaler.fit(train_vec[:, no_normalize_index:])
test_vec[:, no_normalize_index:] = min_max_scaler.transform(test_vec[:, no_normalize_index:])
train_vec[:, no_normalize_index:] = min_max_scaler.transform(train_vec[:, no_normalize_index:])

bool型の掛け算順

以前、一度バグを産んだ原因コード。演算子の優先度は、(符号としての) - > * > (引き算としての)- なので、こういうことをすると、一見引き算の順序を変えただけなのに、結果がかわる。これからはおとなしくastypeでキャストしようと心に誓った。

import numpy as np
bool_list = np.array([True, False])

print [0,0] - bool_list * 0.5
print -bool_list * (0.5) + [0,0]

print - bool_list.astype(int) * 0.5 + [0,0]
[-0.5  0. ]
[ 0.   0.5]
[-0.5  0. ]

クラスタリングなどの結果をscatterでプロットする(plt.scatter)

だいたい特徴量的にx, yを管理しているので、特徴ベクトル行列のrow, colを入れ替えてプロットする。 colorの変数に適当に突っ込むとよしなにやってくれる。 ついでにPCAもする。

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pcaed_data = pca.fit_transform(data[:,1:]/255.0)
pcaed_data = zip(*pcaed_data)
plt.scatter(pcaed_data[0], pcaed_data[1], marker=".",c=data[:,0], linewidths=0, alpha=0.7)

csvファイルからデータを読み込む。(pandas)

普通にopenでファイルを開いてもいいけど、pandasでやると欠損値とかの扱いがかなり楽。

import pandas as pd
data = pd.read_csv("train.csv")

data = data[~data.Cabin.isnull()] #"Cabin"がNanでないものだけを抽出する。
print data
     PassengerId  Survived  Pclass  \
1              2         1       1
3              4         1       1
6              7         0       1
10            11         1       3
..           ...       ...     ...
879          880         1       1
887          888         1       1
889          890         1       1

                                                  Name     Sex   Age  SibSp  \
1    Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1
3         Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1
6                              McCarthy, Mr. Timothy J    male  54.0      0
10                     Sandstrom, Miss. Marguerite Rut  female   4.0      1
..                                                 ...     ...   ...    ...
879      Potter, Mrs. Thomas Jr (Lily Alexenia Wilson)  female  56.0      0
887                       Graham, Miss. Margaret Edith  female  19.0      0
889                              Behr, Mr. Karl Howell    male  26.0      0

     Parch       Ticket      Fare        Cabin Embarked
1        0     PC 17599   71.2833          C85        C
3        0       113803   53.1000         C123        S
6        0        17463   51.8625          E46        S
10       1      PP 9549   16.7000           G6        S
..     ...          ...       ...          ...      ...
879      1        11767   83.1583          C50        C
887      0       112053   30.0000          B42        S
889      0       111369   30.0000         C148        C

[204 rows x 12 columns]

リストで重複した要素を削除する。(pandas)

例えばIDが重複していて、どちらかの要素がいらないとき、これを見つけて削除する。 オプションで上にあるものを残すか、下にあるものを残すかを選べる。

print data[~data.Cabin.duplicated()]
     PassengerId  Survived  Pclass  \
1              2         1       1
3              4         1       1
6              7         0       1
10            11         1       3
..           ...       ...     ...
879          880         1       1
887          888         1       1
889          890         1       1

                                                  Name     Sex   Age  SibSp  \
1    Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1
3         Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1
6                              McCarthy, Mr. Timothy J    male  54.0      0
10                     Sandstrom, Miss. Marguerite Rut  female   4.0      1
..                                                 ...     ...   ...    ...
879      Potter, Mrs. Thomas Jr (Lily Alexenia Wilson)  female  56.0      0
887                       Graham, Miss. Margaret Edith  female  19.0      0
889                              Behr, Mr. Karl Howell    male  26.0      0

     Parch       Ticket      Fare        Cabin Embarked
1        0     PC 17599   71.2833          C85        C
3        0       113803   53.1000         C123        S
6        0        17463   51.8625          E46        S
10       1      PP 9549   16.7000           G6        S
..     ...          ...       ...          ...      ...
879      1        11767   83.1583          C50        C
887      0       112053   30.0000          B42        S
889      0       111369   30.0000         C148        C

[147 rows x 12 columns]

特定の文字を含むものを取り出す(pandas)

数字は簡単だけど、文字の一部に含まれているというのがよくわからなかった。これもpandasを使えば簡単。

print data[data.Cabin.str.contains("A")]
     PassengerId  Survived  Pclass  \
23            24         1       1
96            97         0       1
174          175         0       1
185          186         0       1
209          210         1       1
284          285         0       1
445          446         1       1
475          476         0       1
556          557         1       1
583          584         0       1
599          600         1       1
630          631         1       1
647          648         1       1
806          807         0       1
867          868         0       1

                                                  Name     Sex  Age  SibSp  \
23                        Sloper, Mr. William Thompson    male   28      0
96                           Goldschmidt, Mr. George B    male   71      0
174                            Smith, Mr. James Clinch    male   56      0
185                              Rood, Mr. Hugh Roscoe    male  NaN      0
209                                   Blank, Mr. Henry    male   40      0
284                         Smith, Mr. Richard William    male  NaN      0
445                          Dodge, Master. Washington    male    4      0
475                        Clifford, Mr. George Quincy    male  NaN      0
556  Duff Gordon, Lady. (Lucille Christiana Sutherl...  female   48      1
583                                Ross, Mr. John Hugo    male   36      0
599       Duff Gordon, Sir. Cosmo Edmund ("Mr Morgan")    male   49      1
630               Barkworth, Mr. Algernon Henry Wilson    male   80      0
647                Simonius-Blumer, Col. Oberst Alfons    male   56      0
806                             Andrews, Mr. Thomas Jr    male   39      0
867               Roebling, Mr. Washington Augustus II    male   31      0

     Parch    Ticket     Fare Cabin Embarked
23       0    113788  35.5000    A6        S
96       0  PC 17754  34.6542    A5        C
174      0     17764  30.6958    A7        C
185      0    113767  50.0000   A32        S
209      0    112277  31.0000   A31        C
284      0    113056  26.0000   A19        S
445      2     33638  81.8583   A34        S
475      0    110465  52.0000   A14        S
556      0     11755  39.6000   A16        C
583      0     13049  40.1250   A10        C
599      0  PC 17485  56.9292   A20        C
630      0     27042  30.0000   A23        S
647      0     13213  35.5000   A26        C
806      0    112050   0.0000   A36        S
867      0  PC 17590  50.4958   A24        S

おわりに

書いたら覚えるだろう、そう思っていた。

参考

  1. for文の終了時の処理(for...else) - 繰り返し - Python入門
  2. argparseを使ってみた - そこはかとなく書くよ。
  3. Python: コマンドラインパーサの Click が便利すぎた - CUBE SUGAR CONTAINER
  4. 現在時刻を取得・ファイル名用に整形 - 週末京都
  5. スーパーIT戦士になりたい!: Python: ファイル名の番号でソートする
  6. Chainerによる多層パーセプトロンの実装 - 人工知能に関する断創録
  7. numpy.random.permutation — NumPy v1.10 Manual
  8. sklearn.cross_validation.KFold — scikit-learn 0.17 documentation
  9. API Reference — scikit-learn 0.17 documentation