拙著「Pythonでデータ分析」の第4章「行列演算とPythonスクリプト」の補足説明
配列の行・列の並べ替えと選択
配列の行および列の並べ替えと選択、それらの固有分解、特異値分解への応用についてスクリプト例を示す。
Numpyの配列(ndarray型)の要素は、i番目の要素をj番目に置くという指定によって並べ替えることができる。例えば、ndarray型の配列
a = numpy.array([1,2,3,4,5])
において、
[a[2],a[3],a[4],a[1],a[0]]
という形に並べ替えるときは、並べ替えのリストを
idx = [2,3,4,1,0]
と用意して、a[idx]とすればよい。
すなわち、次のスクリプト
import numpy as np
a = np.array([1,2,3,4,5])
print('a =',
a)
idx = [2,3,4,1,0]
print('idx =', idx)
b = a[idx]
print('b =',
b)
を実行すると、出力は
a = [1 2 3 4 5]
idx = [2, 3, 4, 1, 0]
b = [3 4 5 2 1]
となる。
行単位で並べ替えるときは、次のスクリプトのようになる。
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
print('A =', A)
idx1 = [2,0,1]
print('idx1 =',idx1)
B = A[idx1]
print('B =', B)
実行結果の出力は以下のようになる。
A = [[1 2 3]
[4 5 6]
[7 8 9]]
idx1 = [2, 0, 1]
B = [[7 8 9]
[1 2 3]
[4 5 6]]
列単位で並べ換えるときのスクリプトは次のようになる。
C = A[:,idx1]
print('C =',C)
実行結果の出力は、以下のようである。
C = [[3 1 2]
[6 4 5]
[9 7 8]]
配列において、要素の大きさの順を得る関数としてnumpy.argsortがある。
次のスクリプトでは、リストaの要素の大きさの順が、それぞれの大きさが小さいものから何番目であるのかを示す配列idxとして得ることができる。
a = [5,3,1,2,4]
print('a =', a)
idx = np.argsort(a)
print('idx
=', idx)
実行結果の出力は以下のようになる。
a = [5, 3, 1, 2, 4]
idx = [2 3 1 4 0]
すなわち、1番小さい要素はa[2]、次がa[3]、次がa[1]、次がa[4]、一番大きい要素がa[0]である。
したがって、aの要素を小さいものから順に並べ替えるスクリプトは以下のようになる。
a1 = np.array(a)[idx]
print('a1 =', a1)
aの要素の並べ替えにおいて、リスト型のaをnp.array(a)とndarray型に変換していることに注意。
実行結果の出力は以下の通りである。
a1 = [1 2 3 4
5]
要素を大きいもから小さいものの順に並べ替えるときは、以下のようにすればよい。-np.array(a)とマイナス記号-を付けてargsortしていることに注意。
idx = np.argsort(-np.array(a))
print('idx
=', idx)
a2 = np.array(a)[idx]
print('a2 =', a2)
実行結果の出力は以下のようになる。
idx = [0 4 1 3 2]
a2 = [5 4 3 2 1]
ndarray型の配列は、インデックスに論理値を指定することで選択できる。Trueで選択、Falseで非選択である。
例えば、次のスクリプト
import numpy
as np
a = np.array([1,2,3,4,5])
print('a =', a)
a1 = a[[True, False, False, True,
True]]
print('a1 =', a1)
を実行すると、以下の出力を得る。
a = [1 2 3 4 5]
a1 = [1 4 5]
したがって、配列から偶数の要素を選ぶスクリプト例は以下のようになる。
a = np.array([1,2,3,4,5])
ck = a % 2 ==0
print('ck
=', ck)
a1 = a[ck]
print('a =', a)
print('a1 =', a1)
実行結果は、以下のような出力となる。
ck = [False True False True False]
a = [1 2 3 4 5]
a1 = [2 4]
2より大きい要素を選ぶスクリプト例は以下のようである。
ck1 = a > 2
print('ck1 =', ck1)
a2 = a[ck1]
print('a2 =', a2)
実行結果は、以下のような出力である。
ck1 = [False False True True True]
a2 = [3 4 5]
2次元の配列の場合は、行単位、列単位で選択できる。サンプルスクリプトを以下に示す。
A = np.array([[1,2,3],[4,5,6],
[7,8,9]])
print('A =\n', A)
ck = [True, False,True]
A1 = A[ck]
# 行の選択
print('ck
=', ck)
print('A1 =\n', A1)
A2 = A[:,ck]
# 列の選択
print('A2 =\n', A2)
A3 = A[ck][:,ck]
# 行と列の選択
print('A3 =\n', A3)
実行結果は、以下の通りである。
A =
[[1 2 3]
[4 5 6]
[7 8 9]]
ck = [True, False, True]
A1 =
[[1 2 3]
[7 8 9]]
A2 =
[[1 3]
[4 6]
[7 9]]
A3 =
[[1 3]
[7 9]]
固有分解のスクリプト例を以下に示す。行列については、拙著「Pythondデータ分析」、「数学入門[上]」、「数学入門[下]」で説明している。
import numpy
as np
a = np.array([[1,2,3],[4,5,6]])
A = a.T @ a
print('A =\n', A)
w, v = np.linalg.eigh(A)
print('w =\n', w)
print('v =\n', v)
A1 = v @ np.diag(w)
@ v.T
print('A1 =\n', A1)
実行結果の出力は以下のようである。
A =
[[17 22 27]
[22 29 36]
[27 36 45]]
w =
[-5.29896871e-15 5.97327474e-01 9.04026725e+01]
v =
[[ 0.40824829 -0.80596391 -0.42866713]
[-0.81649658 -0.11238241 -0.56630692]
[ 0.40824829 0.58119908 -0.7039467 ]]
A1 =
[[17. 22. 27.]
[22. 29. 36.]
[27. 36. 45.]]
特異値が小さいものから大きいものの順になっているので、これを以下のスクリプトによって大きいものから小さいものへの順に並べ替える。
idx = np.argsort(-w)
print('idx
=', idx)
w = w[idx]
v = v[:,idx]
print('w =\n', w)
print('v =\n', v)
A2 = v @ np.diag(w)
@ v.T
print('A2 =\n', A2)
実行結果の出力は以下のようになる。
idx = [2 1 0]
w =
[ 9.04026725e+01 5.97327474e-01 -5.29896871e-15]
v =
[[-0.42866713 -0.80596391 0.40824829]
[-0.56630692 -0.11238241 -0.81649658]
[-0.7039467 0.58119908 0.40824829]]
A2 =
[[17. 22. 27.]
[22. 29. 36.]
[27. 36. 45.]]
特異値の小さいものを除くスクリプト例を以下のように用意した。
ckidx = (w > 0.01)
print('ckidx
=', ckidx)
w1 = w[ckidx]
v1 = v[:,ckidx]
print('w1 =', w1)
print('v1 =\n', v1)
A3 = v1 @ np.diag(w1)
@ v1.T
print('A3 =\n', A3)
実行結果の出力は、以下の通りである。
ckidx = [ True True False]
w1 = [90.40267253 0.59732747]
v1 =
[[-0.42866713 -0.80596391]
[-0.56630692 -0.11238241]
[-0.7039467 0.58119908]]
A3 =
[[17. 22. 27.]
[22. 29. 36.]
[27. 36. 45.]]
特異値分解のスクリプト例を以下に示す。行列については、拙著「Pythondデータ分析」、「数学入門[上]」、「数学入門[下]」で説明している。
import numpy
as np
A = [[1,2,3,4,5],
[5,4,3,2,1],[1,1,1,1,1]]
print('A =\n', A)
u, s, vh = np.linalg.svd(A, full_matrices=False)
print('u =\n', u)
print('s =\n', s)
print('vh
=\n', vh)
A1 = u @ np.diag(s)
@ vh
print('A1 =\n', A1)
実行結果は、以下のような出力になる。
A =
[[1, 2, 3, 4, 5], [5, 4, 3, 2, 1], [1, 1,
1, 1, 1]]
u =
[[-6.88247202e-01 7.07106781e-01 -1.62221421e-01]
[-6.88247202e-01 -7.07106781e-01
-1.62221421e-01]
[-2.29415734e-01 -1.38777878e-16 9.73328527e-01]]
s =
[9.74679434e+00 4.47213595e+00
3.39845521e-16]
vh =
[[-4.47213595e-01 -4.47213595e-01
-4.47213595e-01 -4.47213595e-01
-4.47213595e-01]
[-6.32455532e-01 -3.16227766e-01
-1.90719178e-16 3.16227766e-01
6.32455532e-01]
[ 6.22264954e-01 -7.09529044e-01
-1.61658331e-01 -3.71560229e-02
2.86078444e-01]]
A1 =
[[1. 2. 3. 4. 5.]
[5. 4. 3. 2. 1.]
[1. 1. 1. 1. 1.]]
特異値の小さい部分を除くため、次のスクリプトで特異値の選択リストを作成した。
idx = s > 0.01
print('idx
=', idx)
実行結果の出力は以下のようである。
idx = [ True True False]
このidxにより選択を行うスクリプトを以下のように用意した。
u1 = u[:, idx]
# 列の選択
s1 = s[idx]
vh1 = vh[idx]
# 行の選択
print('u1 =\n', u1)
print('s1 =', s1)
print('vh1 =\n', vh1)
A2 = u1 @ np.diag(s1)
@ vh1
print('A2 =\n', A2)
実行結果の出力は、以下のようである。
u1 =
[[-6.88247202e-01 7.07106781e-01]
[-6.88247202e-01 -7.07106781e-01]
[-2.29415734e-01 -1.38777878e-16]]
s1 = [9.74679434 4.47213595]
vh1 =
[[-4.47213595e-01 -4.47213595e-01
-4.47213595e-01 -4.47213595e-01
-4.47213595e-01]
[-6.32455532e-01 -3.16227766e-01
-1.90719178e-16 3.16227766e-01
6.32455532e-01]]
A2 =
[[1. 2. 3. 4. 5.]
[5. 4. 3. 2. 1.]
[1. 1. 1. 1. 1.]]