NumPyが提供するndarrayオブジェクトで行列を扱う際には、逆行列や行列式、行列の固有値と固有ベクトルが簡単に求められます。その基本を見ていきましょう。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
本連載はPythonについての知識を既にある程度は身に付けている方を対象として、Pythonでデータ処理を行う上で必須ともいえるNumPyやpandas、Matplotlibなどの各種ライブラリの基本的な使い方を学んでいくものです。そして、それらの使い方をある程度覚えた上で、それらを活用してデータ処理を行うための第一歩を踏み出すことを目的としています。
ある正方行列A(行数と列数が同じ行列)があったときに、Aとの行列積を計算すると単位行列(行列の左上から右下に向けた対角要素の値が1、他の要素の値が0となるような行列)となるような行列のことを逆行列と呼び、A-1と表記します。Iを単位行列とすると、A・A-1=A-1・A=Iとなるような行列のことです。ただし、全ての行列に逆行列が存在するわけではないことには注意が必要です(逆行列が存在する行列のことを正則行列と呼びます)。
以下に例を示しましょう。
import numpy as np
a = np.array([[1, 2], [3, 4]])
print(a)
# 出力結果:
#[[1 2]
# [3 4]]
ここでは2行2列の正方行列aを作成しています。この行列(2次元配列)には逆行列が存在します。2行2列の行列の逆行列は次の計算式で求められます。
上で作成した行列であれば、逆行列は次のようになります。
これを基に逆行列を作成したのが以下です。
inva = np.array([[-2, 1], [1.5, -0.5]])
print(inva)
# 出力結果:
#[[-2. 1. ]
# [ 1.5 -0.5]]
invaという名前にしたのは逆行列の英語表記である「inverse matrix」から頭の「inv」をもらったからです。では、invaがaの逆行列かどうかを確認してみましょう。これにはaとinvaの行列積を計算して、その結果が単位行列となるかどうかを見るだけです。ここではnumpy.dot関数を使っていますが、@演算子やnumpy.matmul関数を使っても同様です。
result = np.dot(a, inva)
print(result)
# 出力結果:
#[[1. 0.]
# [0. 1.]]
result = np.dot(inva, a)
print(result)
# 出力結果:
#[[1. 0.]
# [0. 1.]]
aとinvaの行列積、invaとaの行列積はどちらも単位行列となっていることが確認できました。よって、invaはaの逆行列(aはinvaの逆行列)であるということです。
上では逆行列を求める式から計算をしていましたが、NumPyでは逆行列をnp.linalg.inv関数で簡単に計算できます(しかも2行2列の行列に限らず任意の正則行列の逆行列を求められます)。引数は1つだけで、逆行列を求めたい行列(二次元配列)を与えます。以下はその例です。
inva = np.linalg.inv(a)
print(inva)
# 出力結果:
#[[-2. 1. ]
# [ 1.5 -0.5]]
出力結果を見ると分かるように先と同じ結果が得られたようです。では、ここでもaとinvaの行列積を計算してみましょう。ここでは@演算子を使ってみます。
result = a @ inva
print(result)
# 出力結果:
#[[1.0000000e+00 0.0000000e+00]
# [8.8817842e-16 1.0000000e+00]]
先ほどとは微妙に異なる値が表示されました。これは浮動小数点数値の計算誤差によるものでしょう。「8.8817842e-16」という値にギョッとする人もいるかもしれませんが、これは指数表記なので小数点以下にゼロが10個以上並んでいることに注意してください。実質的にはゼロと同等なので、計算結果は単位行列といってもよいでしょう。とはいえ、スッキリはしません。
浮動小数点数値などをどのように表示するかは、numpy.set_printoptions関数で設定可能です(現在の設定を読み取るにはnumpy.get_printoptions関数を使います)。
詳しいことは上記のリンクから公式のドキュメントを参照していただくとして、幾つかのパラメーターを紹介しておきます。
パラメーター | 説明 |
---|---|
precision | 小数点以下の精度。デフォルトは8。floatmodeの値によって表示される小数部の精度は変化する点には注意 |
suppress | Trueなら常に固定小数点数値として表示。Falseなら多次元配列の要素の最小値が10-4より小さいか、要素の最小値と最大値の絶対値の比率が1000倍よりも大きいときには指数表記が行われる。デフォルトはFalse |
nanstr | NaN値の文字列表記の指定。デフォルトは「nan」 |
infstr | 無限大を表す値の文字列表記の指定。デフォルトは「inf」 |
sign | 浮動小数点数値の符号の表示方法。'+'なら正値でも常に符号を表示。' 'なら正値は符号の位置に空白文字を表示。'-'なら符号を省略。デフォルトは'-' |
floatmode | precisionパラメーターの解釈方法の指定。 'fixed'なら常にprecisionで指定された桁数で小数点以下を表示 'unique'なら各値の小数部の桁数が変化する 'maxprec'ならprecisionで指定された桁数を最長の文字数として、各要素の値をその精度で表示する。ただし、より少ない桁数で表示できるものについては末尾はゼロ埋めされるのではなく空白文字が表示される 'maxprec_equal'ならprecisionで指定された桁数を最小の文字数として、各要素の値をその精度で表示する。より少ない桁数で表示できるものについては末尾がゼロ埋めされる。デフォルト値は'maxprec'(ドキュメントでは'maxprec_equal’とありますが、たぶんこちらが正解のようです) |
numpy.set_printoptions関数のパラメーター |
少し例を示しましょう。
x = np.array([-1.23, 2.34567])
np.set_printoptions(floatmode='fixed', precision=4)
print(x) # [-1.2300 2.3457]
np.set_printoptions(floatmode='unique', precision=4)
print(x) # [-1.23 2.34567]
np.set_printoptions(floatmode='maxprec_equal', precision=4)
print(x) # [-1.2300 2.3457]
np.set_printoptions(floatmode='maxprec', precision=4)
print(x) # [-1.23 2.3457]
上はprecisionとfloatmodeの2つのパラメーターを指定したものです(といっても、precisionは全て4としています)。このとき、最初の例('fixed')では全ての要素が指定された桁数で小数点以下の値を表示しています。次の例('unique')では、要素の値を表示するのに必要な桁数が使われています(precisionの指定は無視されます)。3つ目の例('maxprec_equal')では小数点以下の桁数はprecisionで指定された4桁となって、値の丸めが発生する一方で、全ての要素の末尾が最大の桁数に合わせてゼロ埋めされています。最後の例('maxprec')でも小数手にかの桁数はprecisionで指定された4桁となって丸めが発生している点は'maxprec_equal'とは異なり末尾がゼロ埋めではなく空白文字で埋められています。
そこでfloatmodeを'unique'にして先ほど計算したaとinvaの行列積を表示してみます。すると次のような値となっていることが分かりました。
np.set_printoptions(floatmode='unique')
print(result)
# 出力結果:
#[[1. 0. ]
# [0.0000000000000008881784197001252 0.9999999999999996 ]]
floatmodeを'unique'にしたので各値の表示桁数がバラバラになっていますが、これを見れば、単位行列との誤差はほんのわずかであることが実感できるでしょう。
さらにsuppressをTrueに指定して、precisionとfloatmodeをデフォルト値(8と'maxprec')にすれば表示は次のようになります。
np.set_printoptions(suppress=True, precision=8, floatmode='maxprec')
print(result)
# 出力結果:
#[[1. 0.]
# [0. 1.]]
これで(表示と気分が)少しスッキリしました。
Copyright© Digital Advantage Corp. All Rights Reserved.