行列には階数(ランク)と呼ばれる行列の性質があります。NumPyには行列のランクを計算する関数が備わっています。本記事では、階数の性質と階数の求め方、NumPyでの利用方法を解説します。

行列の階数(ランク)

行列の階数というのはある行列A に含まれる行(列)ベクトルがいくつのベクトルの1次結合でそれぞれ表されるかという意味です。他にも捉え方も色々とありますが、行列そのものが持つある種の次元に近いイメージです。

他にもその行列を用いて写像した先の空間の基底ベクトルの本数を表していたりと、様々な意味合いを持ちます。

1つの階数の求め方を見ていきます。逆行列の求め方でも扱ったように、行基本変形を用います。

逆行列(numpy.linalg.inv関数)のページは以下のリンクから見ることができます。

NumPyで逆行列を求めるlinalg.invの使い方 /features/numpy-inv.html

行基本変形は以下の手順で求めます。

  • 特定の行を低数倍する
  • ある行とある行とを入れ替える
  • 1つの行に対して他の行の定数倍したものを足す

この行基本変形を用いて、対角成分を左上から1にし、左上から単位行列を作っていく操作を行います。この操作を限界まで行ったときにできあがった単位行列の次元がその行列の階数(ランク)となります。

では、1つ例を見てみましょう。以下のような4×5の行列A があるとします。

A\ \ = \ \left( \begin{array}{ccccc} 1 & 1 & 4 & 0 & 1 \\ 0 & 3 & 1 & 3 & 2 \\ 1 & 3 & 0 & 0 & 1 \\ 2 & 4 & 3 & 1 & 1 \\ \end{array} \right )

これを行基本変形を用いて変形していきます。

\to\ \fbox{1}\left( \begin{array}{ccccc} 1 & 1 & 4 & 0 & 1 \\ 0 & 1 & \frac{1}{3} & 1 & \frac{2}{3} \\ 0 & 2 & -4 & 0 & 0 \\ 0 & 2 & -5 & 1 & -1 \\ \end{array} \right ) \to \ \fbox{2}\left( \begin{array}{ccccc} 1 & 0 & \frac{11}{3} & -1 & \frac{1}{3} \\ 0 & 1 & \frac{1}{3} & 1 & \frac{2}{3} \\ 0 & 0 & -\frac{14}{3} & -2 & -\frac{4}{3} \\ 0 & 0 & -\frac{17}{3} & -1 & -\frac{7}{3} \\ \end{array} \right ) \\ \to \ \fbox{3}\left( \begin{array}{ccccc} 1 & 0 & \frac{11}{3} & -1 & \frac{1}{3} \\ 0 & 1 & \frac{1}{3} & 1 & \frac{2}{3} \\ 0 & 0 & 1 & \frac{6}{7} & \frac{2}{7} \\ 0 & 0 & -\frac{17}{3} & -1 & -\frac{7}{3} \\ \end{array} \right ) \to \ \fbox{4}\left( \begin{array}{ccccc} 1 & 0 & 0 & -\frac{29}{7} & -\frac{15}{21} \\ 0 & 1 & 0 & \frac{5}{7} & \frac{4}{7} \\ 0 & 0 & 1 & \frac{6}{7} & \frac{2}{7} \\ 0 & 0 & 0 & -\frac{27}{7} & -\frac{5}{7} \\ \end{array} \right ) \\ \to \ \fbox{5}\left( \begin{array}{ccccc} 1 & 0 & 0 & -\frac{29}{7} & -\frac{15}{21} \\ 0 & 1 & 0 & \frac{5}{7} & \frac{4}{7} \\ 0 & 0 & 1 & \frac{6}{7} & \frac{2}{7} \\ 0 & 0 & 0 & 1 & \frac{5}{27} \\ \end{array} \right ) \to \ \fbox{6}\left( \begin{array}{ccccc} 1 & 0 & 0 & 0 & \frac{10}{189} \\ 0 & 1 & 0 & 0 & \frac{83}{189} \\ 0 & 0 & 1 & 0 & \frac{24}{189} \\ 0 & 0 & 0 & 1 & \frac{5}{27} \\ \end{array} \right )
\fbox{1}

2行目を\frac{1}{3} する。
3行目から1倍した1行目の値を引く。
4行目から2倍した1行目の値を引く。

\fbox{2}

1行目から1倍した2行目の値を引く。
3行目から2倍した2行目の値を引く。
4行目から2倍した2行目の値を引く。

\fbox{3}

3行目を-\frac{3}{14} 倍する。

\fbox{4}

1行目の値から3行目を\frac{11}{3} 倍した値を引く。
2行目の値から3行目を\frac{1}{3} 倍した値を引く。
4行目の値から3行目を-\frac{17}{3} 倍した値を引く。

\fbox{5}

4行目の値を-\frac{7}{27} 倍する。

\fbox{6}

1行目の値から4行目を-\frac{29}{7} 倍した値を引く。
2行目の値から4行目を\frac{5}{7} 倍した値を引く。
3行目の値から4行目を\frac{6}{7} 倍した値を引く。

以上の操作をすると4次元の単位行列が出てくることがわかります。これにより、この行列の階数(ランク)は4と求めることができます。この行列A は4つの行ベクトル\vec{a_1}, \vec{a_2}, \vec{a_3}, \vec{a_4} の線形結合を用いることでそれぞれの行を表現できるということになります。

ひとまずこれが手計算で求める1つの手法です。他にも求め方はありますがここでは割愛します。

np.linalg.matrix_rank関数

このような面倒なこともNumPyでは簡単に求めることができます。ここではその関数であるnp.linalg.matrix_rank()関数について扱っていきたいと思います。

この関数ではランクを求める際、特異値分解(singular value decomposition, SVD)によって特異値の数を求め、その特異値の数がランクと一致しているのを利用して値を求めています。

APIドキュメント

まずはこの関数のAPIドキュメントを見てみましょう。

numpy.linalg.matrix_rank(M, tol=None)

args:

パラメータ名 概要
M {(M,)もしくは(…,M,N)の形状を持つ}配列に相当するもの 階数(ランク)を求めたいベクトルもしくは行列。
tol Noneもしくは float (省略可能)初期値None
ランクを計算する上で0とみなす要素のしきい値を指定します。

returns:

与えられた行列またはベクトルの階数(ランク)を返します。

関数の引数は2つしかありません。1つ目は階数(ランク)を求めたいベクトル、または行列を指定し、2つ目にはSVDを行うときにでる特異値で0とみなすしきい値を指定します。2つ目の引数ですが、特に指定しなくてもNumPyの方で勝手に値を設定して計算を行ってくれますので基本的には特別指定する必要性はないです。

サンプルコード

では、実際のコードで使い方を見ていきましょう。ただ引数に階数(ランク)を求めたい行列するだけなので非常にシンプルです。

まずは先程手計算でランクを求めた行列A について見ていきましょう。

In [1]: import numpy as np

In [2]: A = np.array([[1, 1, 4, 0, 1],
   ...:               [0, 3, 1, 3, 2],
   ...:               [1, 3, 0, 0, 1],
   ...:               [2, 4, 3, 1, 1]]) # Aを定義する。
   ...:               

In [3]: np.linalg.matrix_rank(A) # 階数(rank)を確かめる。
Out[3]: 4

他の行列で見てみましょう。
ちょっと意図的に階数が少なくなるようにしてみます。

In [4]: B = np.array([
   ...: [1, 2, 3, 0],
   ...: [2, 4, 6, 0],
   ...: [1, 0, 1, 2],
   ...: [1, 0, 0, 3]]) # 1行目と2行目の値の比を等しくする。

In [5]: np.linalg.matrix_rank(B) # 階数を計算してみる。
Out[5]: 3

参考