從數(shù)組到矩陣的跡,NumPy常見使用總結(jié)
NumPy 是 Python 語(yǔ)言的一個(gè)擴(kuò)充程序庫(kù)。支持高效的多數(shù)組與矩陣運(yùn)算,此外也針對(duì)數(shù)組運(yùn)算提供大量的數(shù)學(xué)函數(shù)庫(kù)。NumPy 的科學(xué)計(jì)算十分高效,因此彌補(bǔ)了 Python 在運(yùn)算效率上的不足。
在本文中,我們將簡(jiǎn)單介紹在機(jī)器學(xué)習(xí)和數(shù)據(jù)科學(xué)中應(yīng)用最廣的科學(xué)計(jì)算庫(kù),可以說它的高效令使用 Python 開發(fā)機(jī)器學(xué)習(xí)算法成為了可能。此外,我們也常認(rèn)為正是因?yàn)?NumPy,Python 才可以像 MATLAB 那樣高效地執(zhí)行矩陣運(yùn)算。
以下將開啟我們的 NumPy 之旅:
- import numpy as np
如上在 Python 內(nèi)導(dǎo)入 NumPy 庫(kù),「np」簡(jiǎn)寫即我們調(diào)用 NumPy 時(shí)約定俗成的命名。下面,我們分別創(chuàng)建了一個(gè) Python 數(shù)組和 NumPy 數(shù)組:
- # 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])
以下分別打印了這兩個(gè)變量的值與類型:
- 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'>
那么我們?yōu)槭裁匆褂?NumPy 數(shù)組而不使用標(biāo)準(zhǔn)的 Python 數(shù)組呢?原因可能是 NumPy 數(shù)組遠(yuǎn)比標(biāo)準(zhǔn)數(shù)組緊密,在使用同樣單精度變量下,NumPy 數(shù)組所需要的內(nèi)存較小。此外,NumPy 數(shù)組是執(zhí)行更快數(shù)值計(jì)算的優(yōu)秀容器。
np.arange()
下面是另一種定義數(shù)組元素的方式:
- np.arange(0,10,2)
- ====================================================================
- array([0, 2, 4, 6, 8])
其中 arange([start],stop,[step]) 聲明了該數(shù)組元素起始與終止的值,而 step 定義了給定區(qū)間內(nèi)采樣的步幅大小。在以上代碼中,我們生成一個(gè)從零開始到 10 結(jié)束(不包含 10),并且每次加 2 的數(shù)組。注意數(shù)組元素取值服從左閉右開原則,即取 0 而不取 10,停止數(shù)值并不能取到。
下面是另一個(gè)案例:
- np.arange(2,29,5)
- ====================================================================
- array([ 2, 7, 12, 17, 22, 27])
其實(shí)在 NumPy 中的數(shù)組可以等價(jià)的稱之為矩陣或向量。所以當(dāng)我們稱矩陣的維度是 2×3 時(shí),這并沒有錯(cuò)誤,我們同樣還是在描述一個(gè)多維數(shù)組。如下展示了一個(gè) 2×3 階矩陣:
- array([ 2, 7, 12,],
- [17, 22, 27])
現(xiàn)在我們可以討論默認(rèn) NumPy 數(shù)組的形狀(shape),即等同于討論矩陣的維度。形狀是 np 數(shù)組一個(gè)非常重要的屬性,下面使用 shape 方法調(diào)用變量 A 的形狀:
- A = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
- A.shape
- ====================================================================
- (9,)
這是一個(gè)秩為 1 的矩陣,因此我們看到輸出的形狀只有一個(gè)元素。我們可以使用 reshape() 函數(shù)將該數(shù)組轉(zhuǎn)化為我們想要的維度,如下,我們將 B 的形狀轉(zhuǎn)化為 3×3,reshape() 方法將會(huì)返回一個(gè)多維數(shù)組,因此它的左右分別有兩個(gè)方括號(hào)。
因?yàn)?Python 定義的列表沒有 reshape() 方法,該博客給出的標(biāo)準(zhǔn)數(shù)組會(huì)報(bào)錯(cuò)。我們只能對(duì) NumPy 數(shù)組執(zhí)行 reshape。此外,執(zhí)行 reshape 方法要求轉(zhuǎn)化前和轉(zhuǎn)化后的元素?cái)?shù)量是一樣的。
- 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() 函數(shù)生成一個(gè)元素全是零的矩陣。如下在給定需要生成矩陣的形狀后,其就能自動(dòng)填充零值:
- np.zeros((4,3))
- ====================================================================
- array([[ 0., 0., 0.],
- [ 0., 0., 0.],
- [ 0., 0., 0.],
- [ 0., 0., 0.]])
np.zeros((n,m)) 將返回一個(gè) n*m 階矩陣,其中每個(gè)值都為零。
np.eye()
eye() 方法將生成一個(gè)單位矩陣:
- 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) 將生成一個(gè) n 階單位方陣,即一個(gè) n 階矩陣,其主對(duì)角線元素都為 1,其它元素都為 0。
np.dot()
矩陣乘法在機(jī)器學(xué)習(xí)中十分重要,以下展示了怎樣使用 NumPy 執(zhí)行矩陣乘法。我們一般使用 np.dot() 執(zhí)行矩陣乘法,即點(diǎn)積。執(zhí)行該乘法的前提是左邊矩陣的列數(shù)(每行的元素)必須等于右邊矩陣的行數(shù),否則就會(huì)報(bào)錯(cuò)。此外,根據(jù)矩陣乘法的定義,左乘和右乘也不一樣,這一點(diǎn)我們需要注意。
若 A=(2,3),而 B=(3,2),那么 A 左乘 B 就要求 A 的列數(shù) 3 等于 B 的函數(shù) 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]])
以上定義了兩個(gè)矩陣,下面則執(zhí)行矩陣 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() 會(huì)將整個(gè)矩陣的所有元素加和為一個(gè)標(biāo)量值:
- # add all the elements of matrix.
- sum_val = np.sum(M)
- sum_val
- ====================================================================
- 45.0
此外,我們還可以提供參數(shù)以確定到底是沿矩陣的行累加還是沿矩陣的列累加。如下我們給定參數(shù) axis=1,其代表將每一行的元素累加為一個(gè)標(biāo)量值。
- # sum along the rows
- np.sum(M,axis=1)
- ====================================================================
- array([ 6., 15., 24.])
第一行累加為 6、第二行累加為 15、第三行累加為 24。此外,給定參數(shù) axis=0 則表示沿列累加:
- # sum along the cols
- np.sum(M,axis=0)
- ====================================================================
- array([ 12., 15., 18.])
第一列累加為 12、第二列累加為 15、第三列累加為 18。
np.random.rand()
我們可以使用 np.random.rand() 隨機(jī)生成矩陣,即給定矩陣的形狀,其中每個(gè)元素都是隨機(jī)生成的。如下隨機(jī)生成了一個(gè) 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 ]])
當(dāng)然我們也能擴(kuò)展到隨機(jī)生成更高維度的矩陣:
- # 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()
如果我們需要手動(dòng)地給一個(gè)數(shù)組添加一個(gè)或多個(gè)元素,那么我們可以使用 np.append()。
- # generate an array using np.arange()
- A = np.arange(5,15,2)
- A
- ====================================================================
- array([ 5, 7, 9, 11, 13])
下面使用 np.append() 添加一個(gè)元素到數(shù)組 A 中:
- A = np.append(A,19)
- A
- ====================================================================
- array([ 5, 7, 9, 11, 13, 19])
np.append() 同樣可以將一個(gè)具體的數(shù)組添加到已有的數(shù)組中:
- A = np.append(A,[3,55,34,553])A====================================================================array([ 5, 7, 9, 11, 13, 19, 3, 55, 34, 553])
如上我們將一個(gè)列表及其元素添加到了 np 數(shù)組中。
np.diff()
若給定一個(gè)數(shù)組,我們?cè)撊绾吻笕≡摂?shù)組兩個(gè)元素之間的差?NumPy 提供了 np.diff() 方法以求 A[n+1]-A[n] 的值,該方法將輸出一個(gè)由所有差分組成的數(shù)組。
- 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])
我們需要注意執(zhí)行差分運(yùn)算后的數(shù)組要比原數(shù)組的元素少 1 位。其中 n=1 代表執(zhí)行一次求差分,并返回差分的數(shù)組。而 n=2 代表執(zhí)行兩次差分,并返回第二次求差分后的數(shù)組。第二次求差分是在第一次差分結(jié)果數(shù)組上進(jìn)行的。如下對(duì) A 求兩次差分等價(jià)于對(duì)上文 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()
若我們希望將多個(gè)向量或矩陣按一定的方法堆疊成新的矩陣,那么 np.vstack() 和 np.column_stack() 方法將幫助我們實(shí)現(xiàn)這一操作。以下定義三個(gè)行向量:
- # lets define 3 lists.
- a = [1,2,3]
- b = [4,5,6]
- c = [7,8,9]
堆疊一共有兩種變體,即按行堆疊還是按列堆疊。按行堆疊即將需要的向量或矩陣作為新矩陣的一個(gè)行,按列堆疊即一個(gè)向量作為新矩陣的一列。以下展示了 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() 可以將每個(gè)元素作為一列,例如 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 數(shù)組索引
NumPy 數(shù)組的索引方式和 Python 列表的索引方式是一樣的,從零索引數(shù)組的第一個(gè)元素開始我們可以通過序號(hào)索引數(shù)組的所有元素。例如 A[i] 索引數(shù)組 A 中的第 i+1 個(gè)元素。此外,我們還能索引一串元素:
- A=np.array([5, 7, 9, 11, 13, 19, 3, 55, 34, 553])
- A[2:5]
- ===================================================================
- array([ 9, 11, 13])
如上 A[2:5] 索引了數(shù)組 A 中第 3 到第 5 個(gè)元素,注意 Python 列表和數(shù)組的索引都是左閉右開,即 A 中包含 2 索引的元素而不包含 5 索引的元素:
- A[lowerbound(inclusive): upperbound(exclusive)]
廣播操作
廣播操作是 NumPy 非常重要的一個(gè)特點(diǎn),它允許 NumPy 擴(kuò)展矩陣間的運(yùn)算。例如它會(huì)隱式地把一個(gè)數(shù)組的異常維度調(diào)整到與另一個(gè)算子相匹配的維度以實(shí)現(xiàn)維度兼容。所以將一個(gè)維度為 [3,2] 的矩陣與一個(gè)維度為 [3,1] 的矩陣相加是合法的,NumPy 會(huì)自動(dòng)將第二個(gè)矩陣擴(kuò)展到等同的維度。
為了定義兩個(gè)形狀是否是可兼容的,NumPy 從最后開始往前逐個(gè)比較它們的維度大小。在這個(gè)過程中,如果兩者的對(duì)應(yīng)維度相同,或者其一(或者全是)等于 1,則繼續(xù)進(jìn)行比較,直到最前面的維度。若不滿足這兩個(gè)條件,程序就會(huì)報(bào)錯(cuò)。
如下展示了一個(gè)廣播操作:
- 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.]])
嚴(yán)格數(shù)學(xué)意義上,a 和 b 是不能執(zhí)行矩陣乘法的,因?yàn)樗鼈兊木S度不符合要求。但在 NumPy 的廣播機(jī)制下,維度為 1 的項(xiàng)何以擴(kuò)展到相應(yīng)的維度,所以它們就能夠執(zhí)行運(yùn)算。
矩陣的運(yùn)算
以下執(zhí)行了矩陣的轉(zhuǎn)置操作:
- a = np.array([[1,0],[2,3]])
- >>> print a
- [[1 0]
- [2 3]]
- >>> print a.transpose()
- [[1 2]
- [0 3]]
運(yùn)算矩陣的跡:
- >>> print np.trace(a)
- 4
此外,numpy.linalg 模塊中有很多關(guān)于矩陣運(yùn)算的方法,如下?lián)憔仃嚨奶卣髦蹬c特征向量:
- >>> import numpy.linalg as nplg
- >>> print nplg.eig(a)
- (array([ 3., 1.]), array([[ 0. , 0.70710678],
- [ 1. , -0.70710678]]))
原文:https://hackernoon.com/@rakshithvasudev
【本文是51CTO專欄機(jī)構(gòu)“機(jī)器之心”的原創(chuàng)譯文,微信公眾號(hào)“機(jī)器之心( id: almosthuman2014)”】