はじめに
大規模行列を扱う人にとっては行列の malloc/realloc は死活問題。メモリを使いまわしたい邪悪な人たちはこの短いチートシートに目を通しておこう。
配列の初期化
ループの先頭などで配列を0埋めしたいときがあるだろう。既に確保した配列をある値で埋めるにはnumpy.ndarray.fill
を使う。
import numpy as np
a = np.ones(5)
for i in range(5):
a.fill(0)
a[i] = 1
print(a)
配列のコピー
配列の中身を別の配列にコピーしたいときはnumpy.copyto
を使う。numpy.copy
は新しい配列を生成してしまうので注意。
import numpy as np
m, n = 2000, 1000
A = np.ones((m, n))
B = np.zeros(A.shape)
# 新しい配列を生成しないコピー
np.copyto(B, A)
大きい配列を確保しておいて小さい配列で先頭から埋める場合にはスライスを使えばよい。余った部分を0埋めしたいときは前述のfill
で処理してしまおう。
import numpy as np
a = np.array([0, 1, 2, 3, 4])
b = np.array([5, 5, 5])
np.copyto(a[0:3], b) # 先頭から小さい配列の値で埋める
a[3:5].fill(0) # 余った部分を0埋め
数学的操作
数学的操作も引数にout
を取れるものは格納先を指定できる。取れない場合も「実は配列自体はいじってない」みたいなことがある。
四則演算/その他
Mathematical functionsでout
のパラメータが指定できる演算はメモリを使いまわせる。broadcasting で対応できる範囲内であれば配列のshape
が異なってもよい。代入先が元の行列に一致するならば+=
などの代入演算子を使えばよい。
import numpy as np
A = np.array([[1, 2, 3],
[4, 5, 6]])
B = np.array([[7, 8, 9],
[8, 7, 6]])
C = np.zeros(A.shape)
# 新しい配列を生成しない四則演算
np.add(A, B, out=C)
np.multiply(A, B, out=C)
np.subtract(A, B, out=C)
np.divide(A, B, out=C)
# 代入演算子を使うパターン
A += B
A -= B
A *= B
A /= B
A = A + B
は新しい行列が生成されてA
に代入されるので注意。
転置
転置はstridesというパラメータをいじってるだけらしく配列自体はいじっていないので高速である。
import numpy as np
A = np.array([[1, 2, 3],
[4, 5, 6]])
# 転置は新しい配列を生成することはない
B = A.T
B[0,1] = 9
print(A)
array([[1, 2, 3],
[9, 5, 6]])
本記事では紹介しないが同様にreshape
も配列の並び順自体は変わっておらず高速である。
スカラー倍
*=
演算子を用いればよい。
import numpy as np
A = np.array([[1, 2, 3],
[4, 5, 6]])
A *= 5
行列積
numpy.dot
もout
が指定できる。
import numpy as np
A = np.array([[1, 2, 3],
[4, 5, 6]])
B = np.array([[7, 8, 9],
[8, 7, 6]])
C = np.zeros((2, 2))
# 新しい配列を生成しない行列積
np.dot(A, B.T, out=C)
完全に余談だが、複数の行列をかけるときはnumpy.linalg.multi_dot
を使うともっとも効率的な順序で行列積を取ってくれる。ただしこちらはout
が指定できない。