Abnormally Distributed

統計解析担当のライフサイエンス研究者 -> データサイエンティスト@コンサル

行列のランクが意味すること(Pythonで可視化)

最近、こちらの本を参考に、線形代数を復習している。

プログラミングのための線形代数

プログラミングのための線形代数


行列は「写像」であるという観点で、行列式固有値などが空間上で何を意味するかを説明していて、個人的には目から鱗、という感じだった。線形代数は大学の教養過程や大学院の授業でも習ったけど、こういう観点の解説があれば良かったなと思う。

 
また、プログラミングのためのと銘打っているだけに、逆行列固有値などをコンピュータで効率よく計算するための手法に関する解説がある点も、普通の数学の教科書とは差別化されている。

 
今回は、こちら本を読んで、行列のランクが意味することをよく理解できたので、備忘録を書いておく。
行列のランクは重回帰分析や主成分分析のような基本的なデータ解析手法とも関わってくる大事な概念である。

ここでは、視覚的に把握するため、2x2正方行列Aによる写像を考え、ランクが何を表すかを説明する。

A=\begin{pmatrix}
1 & 2 \\
3 & 1 \\
\end{pmatrix}
 
この行列Aによる写像は、点(1, 0) が (1, 2) に、点 (0, 1) が (3, 1) に映るような写像となる。
ここで、 下図左のような原点を中心とした正方形上のデータ点を考えると、行列Aによる写像は、中央の図のようになる。回転や伸縮の結果、歪んだ四角形となることがわかる。

f:id:kibya:20190203102348p:plain
行列Aによる写像


また、歪んだ四角形上の点を元の正方形上の点に戻す写像は、Aの逆行列による写像である。
PAx = x \Leftrightarrow PA = I \Leftrightarrow P = A^{-1}
 

さて、ここから本題の行列のランクについて説明する。
行列Aのランク(rank(A))とは「行列Aの行ベクトル(または列ベクトル)のうち、1次独立なものの数」である。
ベクトルの11次独立というのは、他のベクトルを定数倍したり足し合わせたりして表すことができないベクトルの組を意味する。


2次の正方行列の場合、行は2つしかないので、当然ランクは最大で2となる。
実は上の行列Aはランクが2の場合である。
n次正方行列のランクがnのとき、この行列は正則行列と呼ばれ、逆行列が存在する。

では、2次の正方行列でランクが1の場合はどうなるか。
例えば、下記の行列Bによる写像を考える。

B=\begin{pmatrix}
1 & 2 \\
0.5 & 1 \\
\end{pmatrix}
 
行列Bの2列目は1列目の2倍として表すことができる。つまり、行列Bの列のうち、1次独立なものは1つしか存在しないので、rank(B)は1である。
行列Aの場合と同じように、正方形上の点のBによる写像を確認すると、すべての点が1つの直線上に乗ってしまう。
実はこの正方形上に限らず、平面状のどの点もこの直線上に写像される。

f:id:kibya:20190203104217p:plain
行列Bによる写像


Bによる写像ではすべての点が1直線上に乗ってしまうので、元の2次元空間の情報は失われてしまう。
つまり逆写像は存在せず、逆行列も存在しないということになる。

このように、n次正方行列Aについて、rank(A) < nとなることをランク落ちという。
 ランク落ちした行列による写像は、元のn次元空間を「つぶして」しまい、rank(A)次元に落とすような写像となる。
このとき、Aの逆行列は存在しない。


まとめると、n次正方行列Aについて、下記の5つの表現は同じことを意味する。

 

Pythonコードはこちら。

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid")
%matplotlib inline

# 正方形上の点
x = np.linspace(-1, 1, 9)
tmp1 = np.array([(i, j) for i in [-1, 1] for j in x]) 
tmp2 = np.array([(j, i) for i in [-1, 1] for j in x]) 
data = np.vstack([tmp1, tmp2])

lab = np.abs(data.sum(axis=1)) == 1

plt.figure(figsize=(14, 5))
plt.subplot(131, aspect=1.0)
plt.scatter(data[:,0], data[:,1], s=10, color="gray")
plt.scatter(data[lab, 0], data[lab, 1], marker="s", s=20, c=["red", "blue", "green", "orange"])
plt.xlim(-5, 5)
plt.ylim(-5, 5)

# 行列Aによる写像
A = np.array([[1, 2], [3, 1]])
data2 = np.dot(data, A)

plt.subplot(132, aspect=1.0)
# plt.axes().set_aspect('equal')
plt.scatter(data2[:,0], data2[:,1], s=10, color="gray")
plt.scatter(data2[lab, 0], data2[lab, 1], marker="s", s=20, c=["red", "blue", "green", "orange"])
plt.xlim(-5, 5)
plt.ylim(-5, 5)

# 逆行列=逆写像
A_inv = np.linalg.inv(A)
data_inv = np.dot(data2, A_inv)

plt.subplot(133, aspect=1.0)
plt.scatter(data_inv[:,0], data_inv[:,1], s=10, color="gray")
plt.scatter(data_inv[lab, 0], data_inv[lab, 1], marker="s", s=20, c=["red", "blue", "green", "orange"])
plt.xlim(-5, 5)
plt.ylim(-5, 5)


B = np.array([[1, 2], [0.5, 1]])
data3 = np.dot(data, B)

plt.figure(figsize=(14, 5))
plt.subplot(131, aspect=1.0)
plt.scatter(data[:,0], data[:,1], s=10, color="gray")
plt.scatter(data[lab, 0], data[lab, 1], marker="s", s=20, c=["red", "blue", "green", "orange"])
plt.xlim(-5, 5)
plt.ylim(-5, 5)

plt.subplot(132, aspect=1.0)
plt.scatter(data3[:,0], data3[:,1], s=10)
plt.scatter(data3[lab, 0], data3[lab, 1], marker="s", s=20, c=["red", "blue", "green", "orange"])
plt.xlim(-5, 5)
plt.ylim(-5, 5)

 ちなみに、行列のランクはnumpy.linalg.matrix_rankで算出することができる。