NumPy配列ndarrayの対角成分の抽出、対角行列の作成(diag, diagonal)
二次元のNumPy配列numpy.ndarray
の対角成分を抽出するにはnumpy.diag()
関数を使う。一次元の配列からそれを対角成分とする対角行列を作成することもできる。
numpy.diag()
はnumpy.ndarray
を引数とする関数だが、numpy.ndarray
のメソッドとして対角成分を抽出するnumpy.ndarray.diagonal()
も用意されている。
ここでは以下の内容について説明する。
numpy.diag()
で対角成分を抽出- 基本的な使い方
- 開始位置の指定: 引数
k
- 正方行列ではない場合
- 多次元配列の場合
- 注意: 戻り値はread-onlyのビュー(v1.14.5の場合)
numpy.diag()
で対角行列を生成- 基本的な使い方
- 開始位置の指定: 引数
k
- 単位行列の生成
diagonal()
メソッドの使い方
下三角行列・上三角行列の抽出・生成については以下の記事を参照。
numpy.diag()で対角成分を抽出
基本的な使い方
以下のnumpy.ndarray
を例とする。
import numpy as np
a = np.arange(9).reshape((3, 3))
print(a)
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
np.diag()
の引数にnumpy.ndarray
を指定すると、対角成分が一次元配列として返される。
print(np.diag(a))
# [0 4 8]
開始位置の指定: 引数k
上の例のようにデフォルトは(0, 0)
を開始位置として対角成分を抽出するが、引数k
にオフセットを整数で指定できる。
正の整数を指定すると開始位置が右側(上側)に移動。範囲外を指定してもエラーにはならず、空の配列が返される。
print(np.diag(a, k=1))
# [1 5]
print(np.diag(a, k=2))
# [2]
print(np.diag(a, k=3))
# []
負の整数は開始位置が左側(下側)に移動。
print(np.diag(a, k=-1))
# [3 7]
print(np.diag(a, k=-2))
# [6]
print(np.diag(a, k=-3))
# []
正方行列ではない場合
正方行列でないnumpy.ndarray
もnp.diag()
の引数として指定可能。引数k
もそのまま使える。
a = np.arange(12).reshape((3, 4))
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print(np.diag(a))
# [ 0 5 10]
print(np.diag(a, k=1))
# [ 1 6 11]
print(np.diag(a, k=-1))
# [4 9]
多次元配列の場合
三次元以上の多次元配列を指定するとエラーとなる。
a = np.arange(27).reshape((3, 3, 3))
print(a)
# [[[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]]
#
# [[ 9 10 11]
# [12 13 14]
# [15 16 17]]
#
# [[18 19 20]
# [21 22 23]
# [24 25 26]]]
# print(np.diag(a))
# ValueError: Input must be 1- or 2-d.
一次元配列の場合は次に示すようにその配列を対角成分とする対角行列を返す。
注意: 戻り値はread-onlyのビュー(v1.14.5の場合)
np.diag()
の戻り値がビューかコピーかはバージョンによって異なる。
whether it returns a copy or a view depends on what version of numpy you are using.
numpy.diag — NumPy v1.15 Manual
以下の例はバージョン1.14.5
の場合。このバージョンではnp.diag()
はread-only(書き換え禁止)のビューとして返される。
a = np.arange(9).reshape((3, 3))
print(a)
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
a_diag = np.diag(a)
print(a_diag)
# [0 4 8]
# a_diag[0] = 100
# ValueError: assignment destination is read-only
要素の値を書き換えたい場合はflags.writeable
属性をTrue
とする。ビューなので元の配列の要素の値も変更される。
a_diag.flags.writeable = True
a_diag[0] = 100
print(a_diag)
# [100 4 8]
print(a)
# [[100 1 2]
# [ 3 4 5]
# [ 6 7 8]]
ビューではなくコピーを取得したい場合はcopy()
を使う。この場合、元の配列の要素の値は変更されない。
a_diag_copy = np.diag(a).copy()
print(a_diag_copy)
# [100 4 8]
a_diag_copy[1] = 100
print(a_diag_copy)
# [100 100 8]
print(a)
# [[100 1 2]
# [ 3 4 5]
# [ 6 7 8]]
numpy.diag()で対角行列を生成
基本的な使い方
np.diag()
は対角行列の生成にも使える。
引数に一次元配列を指定するとその配列を対角成分とした対角行列が生成される。
import numpy as np
a = np.array([10, 20, 30])
print(a)
# [10 20 30]
print(np.diag(a))
# [[10 0 0]
# [ 0 20 0]
# [ 0 0 30]]
引数はnumpy.ndarray
でなくPython組み込みのリストやタプルでもOK。
print(np.diag([100, 200, 300]))
# [[100 0 0]
# [ 0 200 0]
# [ 0 0 300]]
開始位置の指定: 引数k
対角成分の抽出と同様に引数k
で開始位置を指定できる。
第一引数の配列の要素がすべて収まる正方行列が返される。
print(np.diag(a, k=1))
# [[ 0 10 0 0]
# [ 0 0 20 0]
# [ 0 0 0 30]
# [ 0 0 0 0]]
print(np.diag(a, k=-2))
# [[ 0 0 0 0 0]
# [ 0 0 0 0 0]
# [10 0 0 0 0]
# [ 0 20 0 0 0]
# [ 0 0 30 0 0]]
単位行列の生成
単位行列(対角成分がすべて1
の対角行列)はnp.diag()
を使って生成することもできるが、専用の関数np.identity()
が便利。
np.identity()
では第一引数にサイズ、第二引数に型dtype
を指定する(デフォルトはNone
で多くの場合はfloat
になる)。
print(np.diag([1, 1, 1]))
# [[1 0 0]
# [0 1 0]
# [0 0 1]]
print(np.identity(3))
# [[1. 0. 0.]
# [0. 1. 0.]
# [0. 0. 1.]]
print(np.identity(3, int))
# [[1 0 0]
# [0 1 0]
# [0 0 1]]
np.identity()
を使うと配列をone-hot表現に簡単に変換できる。以下の記事を参照。
diagonal()メソッドの使い方
numpy.ndarray
のメソッドとして、対角成分を抽出するdiagonal()
もある。
import numpy as np
a = np.arange(9).reshape((3, 3))
print(a)
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
print(a.diagonal())
# [0 4 8]
diagonal()
では引数offset
がdiag()
の引数k
に相当する。
print(a.diagonal(offset=1))
# [1 5]
print(a.diagonal(offset=3))
# []
print(a.diagonal(offset=-2))
# [6]
正方行列でない場合も同様。
a = np.arange(12).reshape((3, 4))
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print(a.diagonal())
# [ 0 5 10]
print(a.diagonal(offset=1))
# [ 1 6 11]
diagonal()
メソッドはnp.diag()
関数のように対角行列の生成はできない。一次元配列から呼び出すとエラーになるので注意。
a = np.arange(3)
print(a)
# [0 1 2]
# a.diagonal()
# ValueError: diag requires an array of at least two dimensions