乗算はNumPy MatrixクラスとArrayクラスでどのように異なりますか?


130

numpyのドキュメントでは、行列の操作に行列の代わりに配列を使用することを推奨しています。ただし、オクターブ(最近まで使用していた)とは異なり、*は行列乗算を実行しないため、関数matrixmultipy()を使用する必要があります。これにより、コードが非常に読みにくくなります。

誰かが私の意見を共有し、解決策を見つけましたか?


8
あなたは質問ではなく意見を求めています。私たちがあなたを助けたり、おそらくそれを読みやすくするためにあなたを導くことができるより具体的なものはありますか?
ウィーティー

2
実際には、線形代数を実行し、multiply()を使用したくない場合は、ドキュメントで行列の使用を推奨しているため、問題は何ですか?
Matti Pastell、

1
私はドキュメントを詳しく調べていません。奇妙なことに、配列は行列クラスよりもどのような利点がありますか?配列は行と列を区別しないことがわかりました。それは、配列が行列ではなくテンソルと見なされることになっているためですか?Joeが指摘したように、行列クラスが2次元であるという事実は非常に限定的です。このような設計の背後にある考え方は何ですか?たとえば、matlab / octaveのような単一のマトリックスクラスを持たないのはなぜですか?
elexhobby

主な問題は、pythonには.*要素ごとのvs行列の乗算に対するvs '*'構文がないことだと思います。それがあれば、*行列の乗算ではなく要素ごとの意味を選択したことに驚いていますが、すべてが単純になります。
チャーリーパーカー

回答:


127

matrixクラスの使用を避ける主な理由は、a)本質的に2次元であり、b)「通常の」numpy配列と比較して追加のオーバーヘッドがあることです。あなたがしているすべてが線形代数であるなら、ぜひとも、マトリックスクラスを使用してください...

配列(Python 3.5より前)では、のdot代わりにを使用しますmatrixmultiply

例えば

import numpy as np
x = np.arange(9).reshape((3,3))
y = np.arange(3)

print np.dot(x,y)

または、numpyの新しいバージョンでは、単に使用します x.dot(y)

個人的には、*行列の乗算を意味する演算子よりもはるかに読みやすいと思います...

Python 3.5の配列の場合は、を使用しますx @ y


10
乗算のスタックがある場合、たとえばx ' A' * A xの場合は読み取り不可能です。
elexhobby、

14
@elexhobby-でもx.T.dot(A.T).dot(A).dot(x)それは読めないじゃないですか、imo 主に行列の乗算を行う場合は、必ずnumpy.matrix
Joe Kington、

7
ところで、なぜ行列乗算は「ドット」と呼ばれるのですか?ドットプロダクトとはどのような意味ですか?
amcnabb 2013年

8
@amcnabb-行列乗算は、教科書では「ドット積」と呼ばれることがあります(これらの本では、考えているドット積は「スカラー積」または「スカラードット積」と呼ばれます)。結局のところ、スカラードット積は2つのベクトルの行列乗算にすぎないため、「ドット」を使用して行列乗算を一般的に意味することは、それほど大きな拡張ではありません。その特定の表記は、少なくとも私の経験では、数学よりも工学や科学のテキストでより一般的(?)に思われます。numpyでのその有病率はnumpy.matrixmultiply、タイプするのが難しいためです。
Joe Kington

7
@amcnabbポイントは、ドットが曖昧さなしに任意の次元に一般化されることです。これがnumpy.dot行列の乗算と同等になります。表記が本当に嫌いな場合は、matrixクラスを使用してください。
Henry Gomersall 2013

80

NumPy 配列の演算とNumPy 行列の演算で知っておくべき重要な点は次のとおりです。

  • NumPy行列はNumPy配列のサブクラスです

  • NumPy 配列演算は要素ごとに行われます(ブロードキャストが考慮された後)。

  • NumPy 行列演算は、線形代数の通常の規則に従います

説明するいくつかのコードスニペット:

>>> from numpy import linalg as LA
>>> import numpy as NP

>>> a1 = NP.matrix("4 3 5; 6 7 8; 1 3 13; 7 21 9")
>>> a1
matrix([[ 4,  3,  5],
        [ 6,  7,  8],
        [ 1,  3, 13],
        [ 7, 21,  9]])

>>> a2 = NP.matrix("7 8 15; 5 3 11; 7 4 9; 6 15 4")
>>> a2
matrix([[ 7,  8, 15],
        [ 5,  3, 11],
        [ 7,  4,  9],
        [ 6, 15,  4]])

>>> a1.shape
(4, 3)

>>> a2.shape
(4, 3)

>>> a2t = a2.T
>>> a2t.shape
(3, 4)

>>> a1 * a2t         # same as NP.dot(a1, a2t) 
matrix([[127,  84,  85,  89],
        [218, 139, 142, 173],
        [226, 157, 136, 103],
        [352, 197, 214, 393]])

しかし、これらの2つのNumPy行列が配列に変換されると、この操作は失敗します。

>>> a1 = NP.array(a1)
>>> a2t = NP.array(a2t)

>>> a1 * a2t
Traceback (most recent call last):
   File "<pyshell#277>", line 1, in <module>
   a1 * a2t
   ValueError: operands could not be broadcast together with shapes (4,3) (3,4) 

ただし、NP.dot構文を使用すると、配列で機能します。この演算は、行列の乗算のように機能します。

>> NP.dot(a1, a2t)
array([[127,  84,  85,  89],
       [218, 139, 142, 173],
       [226, 157, 136, 103],
       [352, 197, 214, 393]])

NumPyマトリックスが必要ですか?つまり、NumPy配列は線形代数計算に十分でしょうか(正しい構文、つまりNP.dotを知っている場合)?

ルールは、引数(配列)が指定された線形代数演算と互換性のある形状(mxn)を持っている場合は問題ないようです。そうでない場合は、NumPyがスローします。

私が遭遇した唯一の例外(おそらく他にもあります)は逆行列の計算です。

以下は、純粋な線形代数演算を呼び出して(実際には、Numpyの線形代数モジュールから)、NumPy配列で渡されたスニペットです

配列の行列式

>>> m = NP.random.randint(0, 10, 16).reshape(4, 4)
>>> m
array([[6, 2, 5, 2],
       [8, 5, 1, 6],
       [5, 9, 7, 5],
       [0, 5, 6, 7]])

>>> type(m)
<type 'numpy.ndarray'>

>>> md = LA.det(m)
>>> md
1772.9999999999995

固有ベクトル/固有値のペア:

>>> LA.eig(m)
(array([ 19.703+0.j   ,   0.097+4.198j,   0.097-4.198j,   5.103+0.j   ]), 
array([[-0.374+0.j   , -0.091+0.278j, -0.091-0.278j, -0.574+0.j   ],
       [-0.446+0.j   ,  0.671+0.j   ,  0.671+0.j   , -0.084+0.j   ],
       [-0.654+0.j   , -0.239-0.476j, -0.239+0.476j, -0.181+0.j   ],
       [-0.484+0.j   , -0.387+0.178j, -0.387-0.178j,  0.794+0.j   ]]))

行列ノルム

>>>> LA.norm(m)
22.0227

QR分解

>>> LA.qr(a1)
(array([[ 0.5,  0.5,  0.5],
        [ 0.5,  0.5, -0.5],
        [ 0.5, -0.5,  0.5],
        [ 0.5, -0.5, -0.5]]), 
 array([[ 6.,  6.,  6.],
        [ 0.,  0.,  0.],
        [ 0.,  0.,  0.]]))

行列のランク

>>> m = NP.random.rand(40).reshape(8, 5)
>>> m
array([[ 0.545,  0.459,  0.601,  0.34 ,  0.778],
       [ 0.799,  0.047,  0.699,  0.907,  0.381],
       [ 0.004,  0.136,  0.819,  0.647,  0.892],
       [ 0.062,  0.389,  0.183,  0.289,  0.809],
       [ 0.539,  0.213,  0.805,  0.61 ,  0.677],
       [ 0.269,  0.071,  0.377,  0.25 ,  0.692],
       [ 0.274,  0.206,  0.655,  0.062,  0.229],
       [ 0.397,  0.115,  0.083,  0.19 ,  0.701]])
>>> LA.matrix_rank(m)
5

マトリックス条件

>>> a1 = NP.random.randint(1, 10, 12).reshape(4, 3)
>>> LA.cond(a1)
5.7093446189400954

ただし、反転にはNumPy行列が必要です。

>>> a1 = NP.matrix(a1)
>>> type(a1)
<class 'numpy.matrixlib.defmatrix.matrix'>

>>> a1.I
matrix([[ 0.028,  0.028,  0.028,  0.028],
        [ 0.028,  0.028,  0.028,  0.028],
        [ 0.028,  0.028,  0.028,  0.028]])
>>> a1 = NP.array(a1)
>>> a1.I

Traceback (most recent call last):
   File "<pyshell#230>", line 1, in <module>
   a1.I
   AttributeError: 'numpy.ndarray' object has no attribute 'I'

しかし、ムーア・ペンローズの擬似はうまく作品に思えます

>>> LA.pinv(m)
matrix([[ 0.314,  0.407, -1.008, -0.553,  0.131,  0.373,  0.217,  0.785],
        [ 1.393,  0.084, -0.605,  1.777, -0.054, -1.658,  0.069, -1.203],
        [-0.042, -0.355,  0.494, -0.729,  0.292,  0.252,  1.079, -0.432],
        [-0.18 ,  1.068,  0.396,  0.895, -0.003, -0.896, -1.115, -0.666],
        [-0.224, -0.479,  0.303, -0.079, -0.066,  0.872, -0.175,  0.901]])

>>> m = NP.array(m)

>>> LA.pinv(m)
array([[ 0.314,  0.407, -1.008, -0.553,  0.131,  0.373,  0.217,  0.785],
       [ 1.393,  0.084, -0.605,  1.777, -0.054, -1.658,  0.069, -1.203],
       [-0.042, -0.355,  0.494, -0.729,  0.292,  0.252,  1.079, -0.432],
       [-0.18 ,  1.068,  0.396,  0.895, -0.003, -0.896, -1.115, -0.666],
       [-0.224, -0.479,  0.303, -0.079, -0.066,  0.872, -0.175,  0.901]])

3
mInv = NP.linalg.inv(m)は配列の逆を計算します
db1234

ここで注意すべき重要な点は、*は要素ごとの乗算であり、ドットは真の行列乗算です。stackoverflow.com/a/18255635/1780570を
Minh Triet

IMPの注:numpy行列は配列を優先して回避する必要があります。ドキュメントからの注記->「線形代数であっても、このクラスの使用は推奨されなくなりました。代わりに通常の配列を使用してください。クラスは将来削除される可能性があります。」stackoverflow.com/a/61156350/6043669
HopeKing


15

行列を処理する場合と同様に、配列を処理する場合にドット演算子が異なる答えを出す状況があります。たとえば、次のように仮定します。

>>> a=numpy.array([1, 2, 3])
>>> b=numpy.array([1, 2, 3])

それらを行列に変換しましょう:

>>> am=numpy.mat(a)
>>> bm=numpy.mat(b)

これで、2つのケースの異なる出力を確認できます。

>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1.  2.  3.]
 [2.  4.  6.]
 [3.  6.  9.]]

具体的には、*は要素ごとの乗算、dotは真の行列乗算です。stackoverflow.com/a/18255635/1780570を
Minh Triet

これは、数の多い配列aT == aとして、転置が何もしないためです。
patapouf_ai 2016

at = np.array([[1]、[2]、[3]])と書くと、numpy.dot(at、b)でも同じ結果が得られます。matixと配列の違いは、ドットではなく、転置です。
patapouf_ai 2016

あるいは、実際にa = numpy.array([[1,2,3]])と書けば、aTは実際に転置され、すべてが行列のように機能します。
patapouf_ai 2016

8

http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.htmlからの参照

...、numpy.matrixクラスの使用はお勧めしません。これは、2D numpy.ndarrayオブジェクトでは実現できないことは何も追加せず、どのクラスが使用されているかを混乱させる可能性があるためです。例えば、

>>> import numpy as np
>>> from scipy import linalg
>>> A = np.array([[1,2],[3,4]])
>>> A
    array([[1, 2],
           [3, 4]])
>>> linalg.inv(A)
array([[-2. ,  1. ],
      [ 1.5, -0.5]])
>>> b = np.array([[5,6]]) #2D array
>>> b
array([[5, 6]])
>>> b.T
array([[5],
      [6]])
>>> A*b #not matrix multiplication!
array([[ 5, 12],
      [15, 24]])
>>> A.dot(b.T) #matrix multiplication
array([[17],
      [39]])
>>> b = np.array([5,6]) #1D array
>>> b
array([5, 6])
>>> b.T  #not matrix transpose!
array([5, 6])
>>> A.dot(b)  #does not matter for multiplication
array([17, 39])

scipy.linalg操作は、numpy.matrixまたは2D numpy.ndarrayオブジェクトに同様に適用できます。


7

このトリックはあなたが探しているものかもしれません。一種の単純な演算子オーバーロードです。

次に、次のような推奨されるInfixクラスのようなものを使用できます。

a = np.random.rand(3,4)
b = np.random.rand(4,3)
x = Infix(lambda x,y: np.dot(x,y))
c = a |x| b

5

PEP 465からの適切な引用- @ petr-viktorinで言及されているように、行列乗算専用の中置演算子は、OPが発生していた問題を明確にします。

[...] numpyは、__mul__メソッドが異なる2つのタイプを提供します。以下のためnumpy.ndarrayのオブジェクト、*実行は乗算を要素単位、及び行列乗算は、(関数呼び出しを使用しなければなりませんnumpy.dot)。以下のためnumpy.matrixのオブジェクト、*実行するには、乗算、マトリックス、および要素ごとの乗算は、関数の構文が必要です。を使用したコードの記述numpy.ndarrayは問題なく機能します。を使用したコードの記述numpy.matrixも問題なく機能します。しかし、これらの2つのコードを統合しようとするとすぐに問題が発生します。を予期し、ndarrayを取得するコードmatrix、またはその逆は、クラッシュするか、正しくない結果を返す可能性があります

@infix演算子の導入により、Pythonマトリックスコードの統一と簡略化が促進されます。


1

関数matmul(numpy 1.10.1以降)は両方のタイプで正常に動作し、結果をnumpyマトリックスクラスとして返します。

import numpy as np

A = np.mat('1 2 3; 4 5 6; 7 8 9; 10 11 12')
B = np.array(np.mat('1 1 1 1; 1 1 1 1; 1 1 1 1'))
print (A, type(A))
print (B, type(B))

C = np.matmul(A, B)
print (C, type(C))

出力:

(matrix([[ 1,  2,  3],
        [ 4,  5,  6],
        [ 7,  8,  9],
        [10, 11, 12]]), <class 'numpy.matrixlib.defmatrix.matrix'>)
(array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]]), <type 'numpy.ndarray'>)
(matrix([[ 6,  6,  6,  6],
        [15, 15, 15, 15],
        [24, 24, 24, 24],
        [33, 33, 33, 33]]), <class 'numpy.matrixlib.defmatrix.matrix'>)

先に述べたように python 3.5以降次の@ような新しい行列乗算演算子を使用することもできます

C = A @ B

上記と同じ結果が得られます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.