行列・テンソル(NumPy その2)¶
このノートブックではnumpy
モジュールのエイリアス(短縮表記)としてnp
を用いる.
import numpy as np
行列のオブジェクト¶
行列もnp.arrayで作成できる.以下のコードは\(3 \times 4\)の行列 \( X = \left(\begin{array}{c} 1 & 2 & 3 & 4 \\ 2 & 3 & 4 & 5 \\ 3 & 4 & 5 & 6 \end{array}\right) \)を定義している.
x = np.array([
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6]
])
x
array([[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6]])
作成されたオブジェクトの型はnumpy.ndarray.
type(x)
numpy.ndarray
行列の次元数(軸の数)は2(x
は2次元の行列).
x.ndim
2
各次元の要素数を表すタプル.x
の1次元目には\(3\)個,2次元目には\(4\)個の要素が格納されている.すなわち,\(3 \times 4\)の行列であることを表している.
x.shape
(3, 4)
オブジェクトに含まれる要素数.x
は\(3 \times 4\)の行列なので,全要素数は\(12\).
x.size
12
x
の各要素は整数である.
x.dtype
dtype('int64')
行列の要素¶
x = np.array([
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6]
])
行列x
の要素\(x_{2,1}\)
x[2][1]
4
要素\(x_{2,1}\)に以下のようにアクセスすることも可能.
x[2,1]
4
行列x
の1次元目(行)で1番目の行を取り出す(1番目の行ベクトル).
x[1]
array([2, 3, 4, 5])
行列x
の2次元目(列)で1番目の列を取り出す(1番目の列ベクトル).
x[:,1]
array([2, 3, 4])
行列x
の1行目および1列目までを取り出す(つまり,左上から\(2 \times 2\)の部分を取り出す).
x[:2,:2]
array([[1, 2],
[2, 3]])
行列x
のスライスにリストを渡すこともできる.
x[[0,1,2],[0,1,2]]
array([1, 3, 5])
行列x
の要素\(x_{0,0} \leftarrow 0\)
x[0][0] = 0
x
array([[0, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6]])
行列x
の0行目の行ベクトル\(x_{0,*} \leftarrow \boldsymbol{0}\)
x[0] = 0
x
array([[0, 0, 0, 0],
[2, 3, 4, 5],
[3, 4, 5, 6]])
行列x
の0列の列ベクトル\(x_{*,0} \leftarrow \boldsymbol{0}\)
x[:,0] = 0
x
array([[0, 0, 0, 0],
[0, 3, 4, 5],
[0, 4, 5, 6]])
\(x_{0,*} \leftarrow \left(\begin{array}{c} 0 & 1 & 2 & 3 \end{array}\right)\)
x[0] = np.arange(4)
x
array([[0, 1, 2, 3],
[0, 3, 4, 5],
[0, 4, 5, 6]])
行列の演算¶
\(X = \left(\begin{array}{c} 1 & 2 & 3 \\ 2 & 3 & 4 \end{array}\right), Y = \left(\begin{array}{c} 1 & 3 & 5 \\ 2 & 4 & 6 \\ \end{array}\right) \) に対して,様々な演算を見ていく.
x = np.array([
[1, 2, 3],
[2, 3, 4],
], dtype='float')
y = np.array([
[1, 3, 5],
[2, 4, 6],
], dtype='float')
行列の和: \(X + Y\)
x + y
array([[ 2., 5., 8.],
[ 4., 7., 10.]])
行列の差: \(X - Y\)
x - y
array([[ 0., -1., -2.],
[ 0., -1., -2.]])
行列のスカラー倍: \(2X\)
2 * x
array([[2., 4., 6.],
[4., 6., 8.]])
スカラーと行列の和: \(1 + X\)
1 + x
array([[2., 3., 4.],
[3., 4., 5.]])
スカラーと行列の差: \(1 - X\)
1 - x
array([[ 0., -1., -2.],
[-1., -2., -3.]])
行列と行列のアダマール積: \(X \odot Y = \left(\begin{array}{c} x_{0,0} y_{0,0} & \dots & x_{0,n-1} y_{0,n-1} \\ \dots & \dots & \dots \\ x_{m-1,0} y_{m-1,0} & \dots & x_{m-1,n-1} y_{m-1,n-1} \\ \end{array}\right)\)
x * y
array([[ 1., 6., 15.],
[ 4., 12., 24.]])
転置行列\(Z = Y^\top\)
z = y.T
z
array([[1., 2.],
[3., 4.],
[5., 6.]])
行列積\(XZ\)
np.dot(x, z)
array([[22., 28.],
[31., 40.]])
行列積\(XZ\)は@
演算子で書くこともできる.
x @ z
array([[22., 28.],
[31., 40.]])
行列の累乗: \(X^2\)
x ** 2
array([[ 1., 4., 9.],
[ 4., 9., 16.]])
行列の要素数が合わないなどで,演算が実行できないときはエラーとなる.例えば行列積\(XY\)を計算するには,\(X\)の列の数と\(Y\)の行の数が一致する必要がある.
x @ y
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-31-e6c6f87ceb97> in <module>
----> 1 x @ y
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)
様々な行列の作成法¶
x = np.array([
[0, 1],
[1, 2],
[2, 3],
])
零行列.
np.zeros((3, 4))
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
\(X\)と同じ形状の零行列.
np.zeros_like(x)
array([[0, 0],
[0, 0],
[0, 0]])
要素がすべて\(1\)の行列.
np.ones((3, 4))
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
\(X\)と同じ形状で要素がすべて\(1\)の行列.
np.ones_like(x)
array([[1, 1],
[1, 1],
[1, 1]])
任意の値で初期化した行列.
np.full((3, 4), -2.)
array([[-2., -2., -2., -2.],
[-2., -2., -2., -2.],
[-2., -2., -2., -2.]])
\(X\)と同じ形状で任意の値で初期化した行列.
np.full_like(x, -2.)
array([[-2, -2],
[-2, -2],
[-2, -2]])
単位行列(必ず正方行列となる).
np.identity(4)
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
対角要素が\(1\),それ以外の要素が\(0\)である行列(正方行列でなくても作成できる).
np.eye(3, 4)
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.]])
np.eyeでも単位行列を作成できる.
np.eye(4)
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
ベクトルから形状を変更して行列にする例.
np.arange(12).reshape(3, 4)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
reshapeメソッドにおいて,いずれかの次元の要素数を-1とすると,他の次元の要素数に基づいてその次元の要素数が推定される.
np.arange(12).reshape(2, -1)
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]])
\([0, 1)\)の範囲の一様分布からランダムに要素をサンプルして作った行列.
np.random.rand(3, 4)
array([[0.41138273, 0.56472476, 0.74019377, 0.21415386],
[0.72671417, 0.00608295, 0.73117914, 0.97736548],
[0.27531559, 0.69205105, 0.1033887 , 0.94401989]])
標準正規分布(平均\(0\),分散\(1\)の正規分布)からランダムに要素をサンプルして作った行列.
np.random.randn(3, 4)
array([[-0.10454056, -0.14676112, -0.12848028, 0.62593397],
[-0.82940938, -0.45294596, -0.3850291 , -1.75512478],
[ 0.28350437, 0.54926243, -0.66035861, -0.50153151]])
正規分布(以下の例では平均\(0.5\),分散\(2\)の正規分布)からランダムに要素をサンプルして作った行列.
np.random.normal(0.5, 2, (3, 4))
array([[ 0.05489919, 0.4101351 , 2.20940252, -1.12757369],
[ 0.18863236, 0.20804204, -0.58426747, 1.76459683],
[-2.4958973 , 4.89609212, -3.17816385, 1.27884234]])
数学関数¶
ベクトルのときに紹介した数学関数は行列でも使える.ただし,必要に応じて演算を行う単位を軸(axis)として指定する.
x = np.array([
[ 1, 12, 9, 4],
[ 8, 2, 3, 7],
[ 11, 5, 6, 10],
])
行列の要素の和.
np.sum(x)
78
行列の最初の次元のベクトル(行ベクトル)の和.
np.sum(x, axis=0)
array([20, 19, 18, 21])
行列の2番目の次元のベクトル(列ベクトル)の和.
np.sum(x, axis=1)
array([26, 20, 32])
行列の要素の積.
np.prod(x)
479001600
行列の最初の次元のベクトル(行ベクトル)の積.
np.prod(x, axis=0)
array([ 88, 120, 162, 280])
行列の2番目の次元のベクトル(列ベクトル)の積.
np.prod(x, axis=1)
array([ 432, 336, 3300])
行列の要素の累積和.
np.cumsum(x)
array([ 1, 13, 22, 26, 34, 36, 39, 46, 57, 62, 68, 78])
行列の最初の次元のベクトル(行ベクトル)の累積和.
np.cumsum(x, axis=0)
array([[ 1, 12, 9, 4],
[ 9, 14, 12, 11],
[20, 19, 18, 21]])
行列の2番目の次元のベクトル(列ベクトル)の累積和.
np.cumsum(x, axis=1)
array([[ 1, 13, 22, 26],
[ 8, 10, 13, 20],
[11, 16, 22, 32]])
行列の要素の累積積.
np.cumprod(x)
array([ 1, 12, 108, 432, 3456, 6912,
20736, 145152, 1596672, 7983360, 47900160, 479001600])
行列の最初の次元のベクトル(行ベクトル)の累積積.
np.cumprod(x, axis=0)
array([[ 1, 12, 9, 4],
[ 8, 24, 27, 28],
[ 88, 120, 162, 280]])
行列の2番目の次元のベクトル(列ベクトル)の累積積.
np.cumprod(x, axis=1)
array([[ 1, 12, 108, 432],
[ 8, 16, 48, 336],
[ 11, 55, 330, 3300]])
行列の要素の平均.
np.mean(x)
6.5
行列の最初の次元のベクトル(行ベクトル)の平均.
np.mean(x, axis=0)
array([6.66666667, 6.33333333, 6. , 7. ])
行列の2番目の次元のベクトル(列ベクトル)の平均.
np.mean(x, axis=1)
array([6.5, 5. , 8. ])
行列の要素の分散.
np.var(x)
11.916666666666666
行列の最初の次元のベクトル(行ベクトル)の分散.
np.var(x, axis=0)
array([17.55555556, 17.55555556, 6. , 6. ])
行列の2番目の次元のベクトル(列ベクトル)の分散.
np.var(x, axis=1)
array([18.25, 6.5 , 6.5 ])
行列の要素の標準偏差.
np.std(x)
3.452052529534663
行列の最初の次元のベクトル(行ベクトル)の標準偏差.
np.std(x, axis=0)
array([4.18993503, 4.18993503, 2.44948974, 2.44948974])
行列の2番目の次元のベクトル(列ベクトル)の標準偏差.
np.std(x, axis=1)
array([4.27200187, 2.54950976, 2.54950976])
行列の要素の最小値.
np.min(x)
1
行列の最初の次元のベクトル(行ベクトル)を取り出し,各要素の最小値を取り出したベクトル.
np.min(x, axis=0)
array([1, 2, 3, 4])
行列の2番目の次元のベクトル(列ベクトル)を取り出し,各要素の最小値を取り出したベクトル.
np.min(x, axis=1)
array([1, 2, 5])
行列の要素の最大値.
np.max(x)
12
行列の最初の次元のベクトル(行ベクトル)を取り出し,各要素の最大値を取り出したベクトル.
np.max(x, axis=0)
array([11, 12, 9, 10])
行列の2番目の次元のベクトル(列ベクトル)を取り出し,各要素の最大値を取り出したベクトル.
np.max(x, axis=1)
array([12, 8, 11])
行列の最小値に対応するインデックス番号.
np.unravel_index(x.argmin(), x.shape)
(0, 0)
行列の最大値に対応するインデックス番号.
np.unravel_index(x.argmax(), x.shape)
(0, 1)
ユニバーサル関数¶
ベクトルの場合と同様に,行列に対してユニバーサル関数を適用すると,要素ごとに数学的な計算を行う.
x = np.array([
[ 0, 1],
[-1, 2],
])
theta = np.array([
[ 0, 90],
[-90, 180],
])
累乗(**
と同じ).
y = np.power(x, 4)
y
array([[ 0, 1],
[ 1, 16]])
平方根.
np.sqrt(y)
array([[0., 1.],
[1., 4.]])
\(e\)に対する指数
y = np.exp(x)
y
array([[1. , 2.71828183],
[0.36787944, 7.3890561 ]])
自然対数
np.log(y)
array([[ 0., 1.],
[-1., 2.]])
三角関数
np.sin(theta / 360 * 2 * np.pi)
array([[ 0.0000000e+00, 1.0000000e+00],
[-1.0000000e+00, 1.2246468e-16]])
np.cos(theta / 360 * 2 * np.pi)
array([[ 1.000000e+00, 6.123234e-17],
[ 6.123234e-17, -1.000000e+00]])
np.tan(theta / 360 * 2 * np.pi)
array([[ 0.00000000e+00, 1.63312394e+16],
[-1.63312394e+16, -1.22464680e-16]])
その他,要素の切り捨て,切り上げ,四捨五入などもベクトルの場合と同様なので省略する.
行列の連結¶
x = np.array([
[1, 2],
[2, 3],
[3, 4],
])
x
array([[1, 2],
[2, 3],
[3, 4]])
y = np.ones((3, 1))
y
array([[1.],
[1.],
[1.]])
z = np.array([0, 1])
z
array([0, 1])
行列・ベクトルを横に(水平に)連結.
np.hstack((x, y))
array([[1., 2., 1.],
[2., 3., 1.],
[3., 4., 1.]])
行列・ベクトルを縦に(垂直に)連結.
np.vstack((z, x))
array([[0, 1],
[1, 2],
[2, 3],
[3, 4]])
ビュー¶
x = np.array([
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6]
])
元の行列・ベクトル(np.ndarray)からスライスで抽出した部分はビュー,すなわち元のオブジェクトへの参照となり,元のオブジェクトとメモリ領域を共有している.以下の例では行列x
から行ベクトルy
をビューとして抽出している.
y = x[2]
y
array([3, 4, 5, 6])
抽出されたビューy
の要素を変更してみる.
y[0] = 0
y
array([0, 4, 5, 6])
元の行列x
の該当する箇所x[2,0]
も変更されている.
x
array([[1, 2, 3, 4],
[2, 3, 4, 5],
[0, 4, 5, 6]])
ビューy
の全ての要素を変更する.
y[:] = 0
y
array([0, 0, 0, 0])
元の行列x
の行ベクトルx[2]
も変更されている.
x
array([[1, 2, 3, 4],
[2, 3, 4, 5],
[0, 0, 0, 0]])
なお,ビューy
の全体を変更しようとして以下のコードを実行すると,y
の要素が変更されるのではなく,y
に1
が代入されるだけなので混同しないこと.
y = 1
y
1
当然,元の行列x
の要素にも変化がない.
x
array([[1, 2, 3, 4],
[2, 3, 4, 5],
[0, 0, 0, 0]])
x
の部分行列をスライスで取り出した場合も挙動は同じ.
y = x[:2,:2]
y
array([[1, 2],
[2, 3]])
y[:] = 0
y
array([[0, 0],
[0, 0]])
x
全体のビューはviewメソッドで作成することもできる.
y = x.view()
y
array([[0, 0, 3, 4],
[0, 0, 4, 5],
[0, 0, 0, 0]])
y[:] = 1
y
array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]])
x
array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]])
テンソル¶
スカラー(次元数\(0\))やベクトル(次元数\(1\)),行列(次元数\(2\))を一般化したものをテンソルと呼ぶ.以下のコードは3次元のテンソルを定義している.
x = np.array([
[
[1, 2, 3],
[2, 3, 4]
],
[
[3, 4, 5],
[5, 6, 7]
],
])
x.ndim
3
x.shape
(2, 2, 3)
x[1,:,:]
array([[3, 4, 5],
[5, 6, 7]])
x[1,...]
array([[3, 4, 5],
[5, 6, 7]])
x[:,:,1]
array([[2, 3],
[4, 6]])
x[...,1]
array([[2, 3],
[4, 6]])
x = np.array([
[1, 2],
[3, 4],
])
x
array([[1, 2],
[3, 4]])
np.expand_dims(x, 0)
array([[[1, 2],
[3, 4]]])
np.expand_dims(x, 1)
array([[[1, 2]],
[[3, 4]]])