静かなる名辞

pythonと読書

【python】numpyのmatrix・matの使い方

 numpyにはみんな大好きndarrayと、普段あまり目にしないmatrixがある*1。matrixは行列演算が行いやすいようにデザインされており、それはそれで良いんだけどndarrayとは微妙に使い勝手が異なるせいで慣れるまでは微妙に戸惑う可能性がある。

 たとえば、対話型インタプリタでこんなコードを打ってみる。

>>> import numpy as np
>>> a = [[1,2,3],
...      [4,5,6],
...      [7,8,9]]
>>> a_arr = np.array(a)
>>> a_mat = np.matrix(a)
>>> a_arr[0][0]
1
>>> a_mat[0][0])
matrix([[1, 2, 3]])

 挙動が違う。numpy配列では、[0][0]と指定すると「1行目を切り出したndarrayを生成する」→「その0番目の要素の値を返す」という処理がされる訳だが、matrixだと「1行目の行だけ切り出した行列を作る」→「生成された1行だけの行列の1行目を切り出してまた行列を作る」という処理になってしまう。だから、何回やっても同じものが出てくる。極端な話、こう書いても同じ。

>>> a_mat[0][0][0][0]
matrix([[1, 2, 3]])

 もし(0, 0)の要素が欲しいときは次のように書くのが正しい作法である。ndarrayの場合でもこの書き方が正しくて、C言語の添字風に角括弧を続けて書くやり方だと一回ndarrayが生成される分パフォーマンスで不利になるはず(ついさっき気づいたので、ひょっとしたら過去記事で使っちゃってるのがあるかも)。

>>> a_arr[0, 0]
1
>>> a_mat[0, 0]
1

 今度は、列を切り出してみよう。これは基本に忠実にスライスすればできる。

>>> a_arr[:,0]
array([1, 4, 7])
>>> a_mat[:,0]
matrix([[1],
        [4],
        [7]])

 これも挙動が違うのがお分かりいただけただろうか。ndarrayの方では「1列目を切り出した値を格納した1次元配列」が生成されるが、matrixの方では「3行1列の行列」が出て来る。

 そして、ndarrayとmatrixでは掛け算を行った結果もまったく違ってくる。

>>> a_arr*a_arr
array([[ 1,  4,  9],
       [16, 25, 36],
       [49, 64, 81]])
>>> a_mat*a_mat
matrix([[ 30,  36,  42],
        [ 66,  81,  96],
        [102, 126, 150]])

 ndarrayの方で掛け算をして得られる値はいわゆるアダマール*2で、早い話対応する要素同士の掛け算である。一方、matrixの方は行列積をそのまま計算してくれる。

 ちなみに、matrixでも掛け算以外の演算(足し算、引き算、除算)は素直にndarrayと同様値同士で処理されるっぽい。また、matrixでアダマール積を計算したい場合、np.multiplyを使うと良い。

>>> np.multiply(a_mat, a_mat)
matrix([[ 1,  4,  9],
        [16, 25, 36],
        [49, 64, 81]])

 また、matrixでは転置行列や逆行列なども容易に計算できる。これについてはドキュメントを参照のこと。

numpy.matrix — NumPy v1.13 Manual

まとめ

 ここまでお膳立てされているものを使わないのは勿体無いので、線形代数を用いるアルゴリズムを書くときには活用してあげよう。

*1:なお、np.matrixとnp.matは厳密には別物である。この記事が参考になると思う。 numpyのmatrixとmatは違うものなんですね - Qiita

*2:アダマール積 - Wikipedia