從數組到矩陣的跡,NumPy常見使用總結
NumPy 是 Python 語言的一個擴充程序庫。支持高效的多數組與矩陣運算,此外也針對數組運算提供大量的數學函數庫。NumPy 的科學計算十分高效,因此彌補了 Python 在運算效率上的不足。
在本文中,我們將簡單介紹在機器學習和數據科學中應用最廣的科學計算庫,可以說它的高效令使用 Python 開發機器學習算法成為了可能。此外,我們也常認為正是因為 NumPy,Python 才可以像 MATLAB 那樣高效地執行矩陣運算。
以下將開啟我們的 NumPy 之旅:
- import numpy as np
如上在 Python 內導入 NumPy 庫,「np」簡寫即我們調用 NumPy 時約定俗成的命名。下面,我們分別創建了一個 Python 數組和 NumPy 數組:
- # python array
- a = [1,2,3,4,5,6,7,8,9]
- # numpy array
- A = np.array([1,2,3,4,5,6,7,8,9])
以下分別打印了這兩個變量的值與類型:
- print(a)
- print(A)
- print(type(a))
- print(type(A))
- ====================================================================
- [1, 2, 3, 4, 5, 6, 7, 8, 9]
- [1 2 3 4 5 6 7 8 9]
- <class 'list'>
- <class 'numpy.ndarray'>
那么我們為什么要使用 NumPy 數組而不使用標準的 Python 數組呢?原因可能是 NumPy 數組遠比標準數組緊密,在使用同樣單精度變量下,NumPy 數組所需要的內存較小。此外,NumPy 數組是執行更快數值計算的優秀容器。
np.arange()
下面是另一種定義數組元素的方式:
- np.arange(0,10,2)
- ====================================================================
- array([0, 2, 4, 6, 8])
其中 arange([start],stop,[step]) 聲明了該數組元素起始與終止的值,而 step 定義了給定區間內采樣的步幅大小。在以上代碼中,我們生成一個從零開始到 10 結束(不包含 10),并且每次加 2 的數組。注意數組元素取值服從左閉右開原則,即取 0 而不取 10,停止數值并不能取到。
下面是另一個案例:
- np.arange(2,29,5)
- ====================================================================
- array([ 2, 7, 12, 17, 22, 27])
其實在 NumPy 中的數組可以等價的稱之為矩陣或向量。所以當我們稱矩陣的維度是 2×3 時,這并沒有錯誤,我們同樣還是在描述一個多維數組。如下展示了一個 2×3 階矩陣:
- array([ 2, 7, 12,],
- [17, 22, 27])
現在我們可以討論默認 NumPy 數組的形狀(shape),即等同于討論矩陣的維度。形狀是 np 數組一個非常重要的屬性,下面使用 shape 方法調用變量 A 的形狀:
- A = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
- A.shape
- ====================================================================
- (9,)
這是一個秩為 1 的矩陣,因此我們看到輸出的形狀只有一個元素。我們可以使用 reshape() 函數將該數組轉化為我們想要的維度,如下,我們將 B 的形狀轉化為 3×3,reshape() 方法將會返回一個多維數組,因此它的左右分別有兩個方括號。
因為 Python 定義的列表沒有 reshape() 方法,該博客給出的標準數組會報錯。我們只能對 NumPy 數組執行 reshape。此外,執行 reshape 方法要求轉化前和轉化后的元素數量是一樣的。
- B = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
- B.reshape(3,3)
- ====================================================================
- array([[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]])
我們可以如下輸出 B 的形狀:
- B.shape
- ====================================================================
- (3,3)
np.zeros()
下面,我們可以使用 np.zero() 函數生成一個元素全是零的矩陣。如下在給定需要生成矩陣的形狀后,其就能自動填充零值:
- np.zeros((4,3))
- ====================================================================
- array([[ 0., 0., 0.],
- [ 0., 0., 0.],
- [ 0., 0., 0.],
- [ 0., 0., 0.]])
np.zeros((n,m)) 將返回一個 n*m 階矩陣,其中每個值都為零。
np.eye()
eye() 方法將生成一個單位矩陣:
- np.eye(5)
- ====================================================================
- array([[ 1., 0., 0., 0., 0.],
- [ 0., 1., 0., 0., 0.],
- [ 0., 0., 1., 0., 0.],
- [ 0., 0., 0., 1., 0.],
- [ 0., 0., 0., 0., 1.]])
np.eye(n) 將生成一個 n 階單位方陣,即一個 n 階矩陣,其主對角線元素都為 1,其它元素都為 0。
np.dot()
矩陣乘法在機器學習中十分重要,以下展示了怎樣使用 NumPy 執行矩陣乘法。我們一般使用 np.dot() 執行矩陣乘法,即點積。執行該乘法的前提是左邊矩陣的列數(每行的元素)必須等于右邊矩陣的行數,否則就會報錯。此外,根據矩陣乘法的定義,左乘和右乘也不一樣,這一點我們需要注意。
若 A=(2,3),而 B=(3,2),那么 A 左乘 B 就要求 A 的列數 3 等于 B 的函數 3。下面展示了 NumPy 矩陣乘法:
- # generate an identity matrix of (3 x 3)
- I = np.eye(3)
- I
- ====================================================================
- array([[ 1., 0., 0.],
- [ 0., 1., 0.],
- [ 0., 0., 1.]])
- # generate another (3 x 3) matrix to be multiplied.
- D = np.arange(1,10).reshape(3,3)
- D
- ====================================================================
- array([[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]])
以上定義了兩個矩陣,下面則執行矩陣 D 左乘矩陣 I:
- # perform actual dot product.
- M = np.dot(D,I)
- M
- ====================================================================
- array([[ 1., 2., 3.],
- [ 4., 5., 6.],
- [ 7., 8., 9.]])
np.sum()
np.sum() 會將整個矩陣的所有元素加和為一個標量值:
- # add all the elements of matrix.
- sum_val = np.sum(M)
- sum_val
- ====================================================================
- 45.0
此外,我們還可以提供參數以確定到底是沿矩陣的行累加還是沿矩陣的列累加。如下我們給定參數 axis=1,其代表將每一行的元素累加為一個標量值。
- # sum along the rows
- np.sum(M,axis=1)
- ====================================================================
- array([ 6., 15., 24.])
第一行累加為 6、第二行累加為 15、第三行累加為 24。此外,給定參數 axis=0 則表示沿列累加:
- # sum along the cols
- np.sum(M,axis=0)
- ====================================================================
- array([ 12., 15., 18.])
第一列累加為 12、第二列累加為 15、第三列累加為 18。
np.random.rand()
我們可以使用 np.random.rand() 隨機生成矩陣,即給定矩陣的形狀,其中每個元素都是隨機生成的。如下隨機生成了一個 2×2 矩陣:
- # generate random values in a 2 x 3 matrix form
- np.random.rand(2,3)
- ====================================================================
- array([[ 0.2248368 , 0.49652272, 0.76189091],
- [ 0.73520939, 0.48107188, 0.3883801 ]])
當然我們也能擴展到隨機生成更高維度的矩陣:
- # generate random values in a 12 x 13 matrix form
- np.random.rand(12,13)
- ====================================================================
- array([[ 0.43385691, 0.15503296, 0.19860119, 0.65346609, 0.16774261,0.56058978, 0.84974275, 0.05887681, 0.27276929, 0.88750259,0.25141674, 0.05663906, 0.54186252],
- [ 0.2635477 , 0.88291404, 0.42043263, 0.83565607, 0.92982761,0.79879409, 0.91323242, 0.37954769, 0.60198588, 0.44773903,0.70699903, 0.3892703 , 0.94314732],
- [ 0.12593268, 0.97838364, 0.81297353, 0.3368167 , 0.33501746,0.99619471, 0.22476839, 0.93321408, 0.41301684, 0.01808732,0.61321647, 0.22462791, 0.468457 ],
- [ 0.63765001, 0.13884884, 0.67648642, 0.65589694, 0.80931411,0.46202022, 0.40819602, 0.03863341, 0.16494124, 0.69603883,0.96849077, 0.19150476, 0.8968954 ],
- [ 0.25646945, 0.21928867, 0.70952192, 0.80569537, 0.84562245,0.54595757, 0.00684613, 0.19142737, 0.94387805, 0.80871064,0.73648968, 0.80105002, 0.16716087],
- [ 0.3894393 , 0.61933361, 0.41088568, 0.88781578, 0.40932049,0.90947387, 0.71984125, 0.81259019, 0.69020009, 0.56480145,0.43041522, 0.02650665, 0.7738148 ],
- [ 0.21326808, 0.2036178 , 0.30368209, 0.51081501, 0.64345557,0.99061654, 0.96805793, 0.19446453, 0.25974565, 0.74033622,0.37379014, 0.67444828, 0.82899251],
- [ 0.47571066, 0.82012796, 0.50881338, 0.3900192 , 0.34356749,0.36440024, 0.58048805, 0.74650051, 0.24974157, 0.70129048,0.99920892, 0.29142188, 0.09263266],
- [ 0.4140815 , 0.25578684, 0.5485647 , 0.07581615, 0.28539059,0.93805043, 0.56897052, 0.23606972, 0.78568646, 0.609795,0.70741831, 0.51003452, 0.53791667],
- [ 0.53967367, 0.78513565, 0.94739241, 0.03891731, 0.15962705,0.45470422, 0.56172944, 0.49735169, 0.35216862, 0.87391629,0.43953245, 0.18160601, 0.78307107],
- [ 0.1725005 , 0.89132449, 0.05287284, 0.2113003 , 0.69802999,0.12609322, 0.83490382, 0.34199806, 0.90740966, 0.33934554,0.02015816, 0.13498658, 0.06695927],
- [ 0.14066135, 0.34828447, 0.0780561 , 0.00126867, 0.57958087,0.93641585, 0.70294758, 0.21712057, 0.24902555, 0.53284372,0.19795993, 0.69817631, 0.71156616]])
np.append()
如果我們需要手動地給一個數組添加一個或多個元素,那么我們可以使用 np.append()。
- # generate an array using np.arange()
- A = np.arange(5,15,2)
- A
- ====================================================================
- array([ 5, 7, 9, 11, 13])
下面使用 np.append() 添加一個元素到數組 A 中:
- A = np.append(A,19)
- A
- ====================================================================
- array([ 5, 7, 9, 11, 13, 19])
np.append() 同樣可以將一個具體的數組添加到已有的數組中:
- A = np.append(A,[3,55,34,553])A====================================================================array([ 5, 7, 9, 11, 13, 19, 3, 55, 34, 553])
如上我們將一個列表及其元素添加到了 np 數組中。
np.diff()
若給定一個數組,我們該如何求取該數組兩個元素之間的差?NumPy 提供了 np.diff() 方法以求 A[n+1]-A[n] 的值,該方法將輸出一個由所有差分組成的數組。
- A=np.array([5, 7, 9, 11, 13, 19, 3, 55, 34, 553])
- B = np.diff(A,n=1)
- B
- ====================================================================
- array([ 2, 2, 2, 2, 6, -16, 52, -21, 519])
我們需要注意執行差分運算后的數組要比原數組的元素少 1 位。其中 n=1 代表執行一次求差分,并返回差分的數組。而 n=2 代表執行兩次差分,并返回第二次求差分后的數組。第二次求差分是在第一次差分結果數組上進行的。如下對 A 求兩次差分等價于對上文 B 再求一次差分。
- # parameter n indicates that this diff() must be run twice.
- np.diff(A,n=2)
- ===================================================================
- array([ 0, 0, 0, 4, -22, 68, -73, 540])
np.vstack() 和 np.column_stack()
若我們希望將多個向量或矩陣按一定的方法堆疊成新的矩陣,那么 np.vstack() 和 np.column_stack() 方法將幫助我們實現這一操作。以下定義三個行向量:
- # lets define 3 lists.
- a = [1,2,3]
- b = [4,5,6]
- c = [7,8,9]
堆疊一共有兩種變體,即按行堆疊還是按列堆疊。按行堆疊即將需要的向量或矩陣作為新矩陣的一個行,按列堆疊即一個向量作為新矩陣的一列。以下展示了 np.vstack((a,b,c)) 如何將向量 a、b、c 分別作為新矩陣的第一行、第二行和第三行:
- # directly stack with lists passed in the same order.
- np.vstack((a,b,c))
- ===================================================================
- array([[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]])
- np.vstack((b,a,c))
- ===================================================================
- array([[4, 5, 6],
- [1, 2, 3],
- [7, 8, 9]])
此外,np.column_stack() 可以將每個元素作為一列,例如 np.column_stack((a,b,c)) 就將向量 a 作為第一列、b 作為第二列、c 作為第三列:
- np.column_stack((a,b,c))
- ===================================================================
- array([[1, 4, 7],
- [2, 5, 8],
- [3, 6, 9]])
- np.column_stack((b,a,c))
- ===================================================================
- array([[4, 1, 7],
- [5, 2, 8],
- [6, 3, 9]])
np 數組索引
NumPy 數組的索引方式和 Python 列表的索引方式是一樣的,從零索引數組的第一個元素開始我們可以通過序號索引數組的所有元素。例如 A[i] 索引數組 A 中的第 i+1 個元素。此外,我們還能索引一串元素:
- A=np.array([5, 7, 9, 11, 13, 19, 3, 55, 34, 553])
- A[2:5]
- ===================================================================
- array([ 9, 11, 13])
如上 A[2:5] 索引了數組 A 中第 3 到第 5 個元素,注意 Python 列表和數組的索引都是左閉右開,即 A 中包含 2 索引的元素而不包含 5 索引的元素:
- A[lowerbound(inclusive): upperbound(exclusive)]
廣播操作
廣播操作是 NumPy 非常重要的一個特點,它允許 NumPy 擴展矩陣間的運算。例如它會隱式地把一個數組的異常維度調整到與另一個算子相匹配的維度以實現維度兼容。所以將一個維度為 [3,2] 的矩陣與一個維度為 [3,1] 的矩陣相加是合法的,NumPy 會自動將第二個矩陣擴展到等同的維度。
為了定義兩個形狀是否是可兼容的,NumPy 從最后開始往前逐個比較它們的維度大小。在這個過程中,如果兩者的對應維度相同,或者其一(或者全是)等于 1,則繼續進行比較,直到最前面的維度。若不滿足這兩個條件,程序就會報錯。
如下展示了一個廣播操作:
- a = np.array([1.0,2.0,3.0,4.0, 5.0, 6.0]).reshape(3,2)
- b = np.array([3.0])
- a * b
- ====================================================================
- array([[ 3., 6.],
- [ 9., 12.],
- [ 15., 18.]])
嚴格數學意義上,a 和 b 是不能執行矩陣乘法的,因為它們的維度不符合要求。但在 NumPy 的廣播機制下,維度為 1 的項何以擴展到相應的維度,所以它們就能夠執行運算。
矩陣的運算
以下執行了矩陣的轉置操作:
- a = np.array([[1,0],[2,3]])
- >>> print a
- [[1 0]
- [2 3]]
- >>> print a.transpose()
- [[1 2]
- [0 3]]
運算矩陣的跡:
- >>> print np.trace(a)
- 4
此外,numpy.linalg 模塊中有很多關于矩陣運算的方法,如下據算矩陣的特征值與特征向量:
- >>> import numpy.linalg as nplg
- >>> print nplg.eig(a)
- (array([ 3., 1.]), array([[ 0. , 0.70710678],
- [ 1. , -0.70710678]]))
原文:https://hackernoon.com/@rakshithvasudev
【本文是51CTO專欄機構“機器之心”的原創譯文,微信公眾號“機器之心( id: almosthuman2014)”】