Python高速化・小技メモ
本記事について
Pythonでの記述に関して、小技をメモしていく。自分用が主たる目的なので、記述に関して詳説はしない。目的と、その手法の2段ツリー構造でまとめていく。
高速化
配列の、指定したindexの要素で構成された新しい配列の作成
何がしたいって、こういうこと。配列data
があって、それに対応する各行ごとのindexをまとめた配列idxs
がある。これらを組み合わせて、新たにidxs
に対応するdata
の要素で構成された配列expected_out
を作成したい。ってこと。
>>> from platform import python_version >>> print(python_version()) 3.7.4 >>> import numpy as np >>> np.__version__ '1,17,2'
data = np.array( [[1, 3, 2, 4, 8], [8, 9, 3, 4, 2], [7, 8, 1, 2, 3]]) indexes = np.array( [[0, 1, 3, 4, 2], [1, 1, 2, 3, 4], [3, 2, 3, 4, 3]]) expected_out = np.array( [[1, 3, 4, 8, 2], [9, 9, 3, 4, 2], [2, 1, 2, 3, 2]])
np.take_along_axis
を使用
[doc] こんな便利な関数があったのね、やるじゃん、numpy。
>>> np.take_along_axis(arr=data, indices=indexes, axis=1) == expected_out array([[ True, True, True, True, True], [ True, True, True, True, True], [ True, True, True, True, True]])
idx + W*y を使用
なんのこっちゃねーんってね。
これは、index用の配列indexes
の各行にそれぞれそれまでの行に含まれる要素数を足す(今回の例だと、5x5の配列なので、0行目には0を、1行目には5を、2行目には10をそれぞれ足す)。それを、対象となるdata
を1次元に変換した時の配列data.ravel()
のindexとして使用し、元の配列の形状にその後戻す、というやり方。強引だけどパッとこんなやり方思いついたらかっこいいよね。。
>>> idx_ = indexes + (np.arange(0, indexes.shape[0]) * indexes.shape[1])[:, None] >>> out = data.ravel()[idx_].reshape(data.shape) >>> out == expected_out array([[ True, True, True, True, True], [ True, True, True, True, True], [ True, True, True, True, True]])
実行時間の比較
timeit
モジュールを使用して実行速度の比較。numpyのメソッドであるnp.take_along_axis
に軍配。一行で記述できることからも、こっちが実用上は正解の選択肢になりそう。それでも、アイデアとしては後者の方がかっこいいけどなぁ(内部で同じようなことしていたりして)。
>>> from timeit import timeit >>> LOOP = 100000 >>> result = timeit("np.take_along_axis(arr=data, indices=indexes, axis=1)", globals=globals(), number=LOOP) >>> print(result / LOOP) 7.96317385000293e-06 >>> def func(org, idx, ans): >>> idx_ = idx + (np.arange(0, idx.shape[0]) * idx.shape[1])[:, None] >>> ret = org.ravel()[idx_].reshape(data.shape) >>> return None >>> >>> result = timeit("func(data, indexes, expected_out)", globals=globals(), number=LOOP) >>> print(result / LOOP) 9.105247100014822e-06
小技
010101...な配列を生成したい
>>> from platform import python_version >>> print(python_version()) 3.7.4 >>> import numpy as np >>> np.__version__ '1,17,2'
正攻法だと、
>>> N = 10 >>> a = np.ones(N) >>> a[::2] = 0 >>> a array([0., 1., 0., 1., 0., 1., 0., 1., 0., 1.]) >>> b = np.ones(N) >>> b[1::2] = 0 >>> b array([1., 0., 1., 0., 1., 0., 1., 0., 1., 0.]) >>> c = np.ones((N, N)) >>> c[::2, ::2] = 0 >>> c[1::2, 1::2] = 0 >>> c array([[0., 1., 0., 1., 0., 1., 0., 1., 0., 1.], [1., 0., 1., 0., 1., 0., 1., 0., 1., 0.], [0., 1., 0., 1., 0., 1., 0., 1., 0., 1.], [1., 0., 1., 0., 1., 0., 1., 0., 1., 0.], [0., 1., 0., 1., 0., 1., 0., 1., 0., 1.], [1., 0., 1., 0., 1., 0., 1., 0., 1., 0.], [0., 1., 0., 1., 0., 1., 0., 1., 0., 1.], [1., 0., 1., 0., 1., 0., 1., 0., 1., 0.], [0., 1., 0., 1., 0., 1., 0., 1., 0., 1.], [1., 0., 1., 0., 1., 0., 1., 0., 1., 0.]])
ちょっと洒落たことやる。np.indices
を使用する。
>>> d = np.indices((N,N)) >>> d.shape (2, 10, 10) >>> d array([[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [3, 3, 3, 3, 3, 3, 3, 3, 3, 3], [4, 4, 4, 4, 4, 4, 4, 4, 4, 4], [5, 5, 5, 5, 5, 5, 5, 5, 5, 5], [6, 6, 6, 6, 6, 6, 6, 6, 6, 6], [7, 7, 7, 7, 7, 7, 7, 7, 7, 7], [8, 8, 8, 8, 8, 8, 8, 8, 8, 8], [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]], [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]]) >>> np.sum(d, axis=0) % 2 array([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]) # 1 dimensional >>> (np.sum(np.indices((N,1)), axis=0) % 2).ravel() array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1]) >>> (np.sum(np.indices((N,1)), axis=0) % 2).ravel() ^ 1 array([1, 0, 1, 0, 1, 0, 1, 0, 1, 0])