2015年1月9日 星期五

[Python] 使用Numpy建立矩陣之基本觀念釐清


---------------------------------------------------基本觀念釐清-------------------------------------------------

陣列跟矩陣到底有什麼不同?首先,陣列(array)依照其維度可分成一維、二維與多維。

若陣列只有一維,我們通常稱它為向量(vector),例如:[1, 2, 3]。

若陣列為二維且每一列長度皆相同,我們就會稱它為矩陣(matrix),例如:[[1, 2, 3], [4, 5, 6]]。



------------------------------------------------------我是正文----------------------------------------------------

好,有以上觀念就足夠了,接著談談怎麼使用Numpy套件處理矩陣運算。程式碼部分我會用IPython的界面呈現方式來表示,[In]代表輸入,[Out]代表對應的輸出。


先匯入Numpy套件:
[In] import numpy as np

假設我們要建立一個最最最基本的矩陣,有兩種方式,以下我們 分成矩陣A1矩陣A2來看

[In] A1 = np.array([[1, 2], [3, 4]])
[In] print(A1)
[Out] [[1 2
             3 4]]

或是

[In] A2 = np.matrix([[1, 2], [3, 4]])
[In] print(A2)
[Out] [[1 2
             3 4]]

這兩者有什麼不同? 讓我們查一下類型:

[In] type(A1)
[Out] numpy.ndarray
[In] type(A2)
[Out] numpy.matrixlib.defmatrix.matrix

矩陣A1與矩陣A2長得一模一樣卻是不同物件?沒錯,矩陣A1是一個Numpy的ndarray物件,ndarray就是n維陣列(n-dimension array),而矩陣A2是一個Numpy的matrix物件,matrix理所當然就是二維陣列(2-dimension array)。

這邊要注意一下!在Numpy中,matrix物件一定是二維陣列但是這個二維陣列不一定能夠稱作矩陣!你可以用Numpy創造如下東西:

[In] A = np.matrix([[1, 2], [3, 4, 5]])
[In] print(A)
[Out] [[[1, 2] [3, 4, 5]]]

雖然A是matrix物件,但是它並不能稱作是矩陣,因為其每一列長度並不相同,在這邊它就只是個二維陣列,Numpy雖然認得它,但是無法在其上發揮太多作用,這部分可以多參考官方文件或是stackoverflow的一些解說。

回歸正題,matrix物件只能表達二維陣列,如果試圖用matrix物件表達三維陣列就會發出ValueError

[In] A = np.matrix([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
...
ValueError: matrix must be 2-dimensional

不同於matrix物件,ndarray物件可以表達n維陣列,如果將一維陣列想成向量,ndarray物件就可以想成是一維陣列(向量)的組合,下面用ndarray物件表達三維陣列就沒有問題:

[In] A = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
[In] A
[Out] array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
[In] np.ndim(A)
[Out] 3

如果用物件導向角度來看,matrix物件就是ndarray物件的子類別(subclass),所以matrix物件繼承了ndarray物件的所有屬性(attributes)與方法(methods)。

所以到底該用哪一個啊?以下小小比較一下:

對於矩陣乘法,例如矩陣A乘矩陣B,我們從小到大都習慣用「A*B」來表示吧?使用matrix物件 ,矩陣A乘矩陣B直接就是用「A*B」代表:

[In] A = np.matrix([[1, 2], [3, 4]])
[In] B = np.matrix([[5, 6], [7, 8]])
[In] A*B
[Out] matrix([[19, 22], [43, 50]])

而ndarray物件則不是這麼一回事,使用ndarray物件,「A*B」其實是代表矩陣A與矩陣B的「對應元素相乘(element-wise product)」,「dot(A, B)」才是我們熟悉的矩陣A乘矩陣B:

[In] A = array([[1, 2], [3, 4]])
[In] B = array([[5, 6], [7, 8]])
[In] (np.ndim(A), np.ndim(B))
[Out] (2, 2)

[In] A*B
[Out] array([[ 5, 12], [21, 32]])
[In] np.ndim(A*B)
[Out] 2

[In] np.dot(A, B)
[Out] array([[19, 22], [43, 50]])
[In] np.ndim(np.dot(A,B))
[Out] 2

為何ndarray物件的A*B代表的是矩陣A與矩陣B的對應元素相乘?要追朔這原理很容易,以下換個例子可能更清楚:

[In] V1 = array([1, 2, 3])
[In] np.ndim(V1)
[Out] 1   #V1維度為1,是一維陣列(向量)

[In] V2 = array([10, 10, 10])
[In] np.ndim(V2)
[Out] 1   #V2維度為1,也是一維陣列(向量)

[In] V1*V2
[Out] array([10, 20, 30]) #對應元素相乘

我們上面提過,ndarray物件可以看成是向量的組合,兩個ndarray物件相乘,也就是向量組合與向量組合相乘,當然就是兩邊相同位置的向量進行對應元素相乘啦!

回過頭來,matrix物件可以直接使用「.T」、「.H」以及「.I」求轉置矩陣(transpose)、共軛轉置矩陣(conjugate transpose)以及反矩陣(inverse),然而ndarray物件只有「.T」可以使用。

另外,「**」用在這兩者上也不同,如果矩陣A是一個matrix物件,則「A**2」是代表「A*A」的意思。如果矩陣A是一個ndarray物件,由於ndarray物件可以看成是向量的組合,「A**2」就是A中每個向量與自己進行對應元素相乘(自己取平方)囉!

總結一下,雖然matrix物件對於使用上比較直觀,但是其只能處理二維陣列,如果哪天要處理三維或更高維陣列怎麼辦?還是得要用ndarray物件。

這就產生一個問題了,由於兩者操作上不太一樣,如果寫code時同時使用兩者,維護上就會稍嫌麻煩,因此,建議寫code時統一使用ndarray物件就好。當然,也不是說永遠不要用matrix物件,反正兩者可以互相轉換(註1),用得方便即可。



----------------------------------------------------我是註解------------------------------------------------------
(註1) np.asmatrix(A)與np.asarray(A)



希望以上對有困惑的人有所幫助,歡迎轉載,註明來源即可。




沒有留言:

張貼留言