python hstack_Python小白数据科学教程:NumPy (下)
點擊“簡說Python”,選擇“置頂/星標公眾號”
福利干貨,第一時間送達!
本文作者:王圣元
轉(zhuǎn)載自:王的機器
本文偏長(1.8w+字),老表建議先收藏,然后轉(zhuǎn)發(fā)朋友圈,然后吃飯、休閑時慢慢看,基礎(chǔ)知識重在反復看,反復記,反復練。
接著上篇繼續(xù)后面兩個章節(jié),數(shù)組變形和數(shù)組計算。
4數(shù)組的變形本節(jié)介紹四大類數(shù)組層面上的操作,具體有
重塑 (reshape) 和打平 (ravel, flatten)
合并 (concatenate, stack) 和分裂 (split)
重復 (repeat) 和拼接 (tile)
其他操作 (sort, insert, delete, copy)
4.1
重塑和打平
重塑 (reshape) 和打平 (ravel, flatten) 這兩個操作僅僅只改變數(shù)組的維度
重塑是從低維到高維
打平是從高維到低維
用reshape()函數(shù)將一維數(shù)組 arr 重塑成二維數(shù)組。
arr = np.arange(12)print( arr )print( arr.reshape((4,3)) )[ 0 1 2 3 4 5 6 7 8 9 10 11][[ 0 1 2]
?[ 3 4 5]
?[ 6 7 8]
?[ 9 10 11]]
思考:為什么重塑后的數(shù)組不是
[[ 0 4 8]?[ 1 5 9]
? [ 2 6 10]
? ?[ 3 7 11]]
當你重塑高維矩陣時,不想花時間算某一維度的元素個數(shù)時,可以用「-1」取代,程序會自動幫你計算出來。比如把 12 個元素重塑成 (2, 6),你可以寫成 (2,-1) 或者 (-1, 6)。
print(?arr.reshape((2,-1)) )print( arr.reshape((-1,6)) )[[ 0 1 2 3 4 5]?[ 6 7 8 9 10 11]]
[[ 0 1 2 3 4 5]
?[ 6 7 8 9 10 11]]打平
用?ravel()?或flatten()?函數(shù)將二維數(shù)組 arr 打平成一維數(shù)組。
arr = np.arange(12).reshape((4,3))print( arr )ravel_arr = arr.ravel()print( ravel_arr )flatten_arr = arr.flatten()print( flatten_arr )[[ 0 1 2]?[ 3 4 5]
?[ 6 7 8]
?[ 9 10 11]]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
思考:為什么打平后的數(shù)組不是
[?0?3 6 9?1 4 7 10 2 5 8 11]要回答本節(jié)兩個問題,需要了解 numpy 數(shù)組在內(nèi)存塊的存儲方式。
行主序和列主序行主序?(row-major order) 指每行的元素在內(nèi)存塊中彼此相鄰,而列主序 (column-major order) 指每列的元素在內(nèi)存塊中彼此相鄰。
在眾多計算機語言中,
默認行主序的有?C?語言(下圖 order=‘C’ 等價于行主序)
默認列主序的有?Fortran 語言(下圖 order=‘F’ 等價于列主序)
在?numpy?數(shù)組中,默認的是行主序,即 order ='C'。現(xiàn)在可以回答本節(jié)那兩個問題了。
如果你真的想在「重塑」和「打平」時用列主序,只用把 order 設(shè)為 'F',以重塑舉例:
print( arr.reshape((4,3), order='F') )[[ 0 1 2]?[ 3 4 5]
?[ 6 7 8]
?[ 9 10 11]]
細心的讀者可能已經(jīng)發(fā)現(xiàn)為什么「打平」需要兩個函數(shù)?ravel()?或?flatten()?它們的區(qū)別在哪里?
知識點函數(shù)?ravel()或flatten()的不同之處是ravel()?按「行主序」打平時沒有復制原數(shù)組,按「列主序」在打平時復制了原數(shù)組
flatten()?在打平時復制了原數(shù)組
?[3 4 5]]
[0 1 2 3 4 5]
[[0 1 2]
?[3 4 5]]再看?ravel()在「列主序」打平,將打平后的數(shù)組 ravel_F 第一個元素更新為 10000,并沒有對原數(shù)組 arr 產(chǎn)生任何影響 (證明?ravel(order='F')是復制了原數(shù)組)ravel_F = arr.ravel( order='F' )ravel_F[0] = 10000print( ravel_F )print( arr )[10000 3 1 4 2 5]
[[0 1 2]
?[3 4 5]]最后看?ravel()在「行主序」打平,將打平后的數(shù)組 ravel_C 第一個元素更新為 10000,原數(shù)組 arr[0][0] 也變成了 10000 (證明?ravel()?沒有復制原數(shù)組)ravel_C = arr.ravel()ravel_C[0] = 10000print( ravel_C )print( arr )[10000 1 2 3 4 5]
[[10000 1 2]
?[ 3 4 5]]
4.2
合并和分裂
合并 (concatenate, stack) 和分裂 (split) 這兩個操作僅僅只改變數(shù)組的分合
合并是多合一
分裂是一分多
使用「合并」函數(shù)有三種選擇
有通用的 concatenate
有專門的 vstack, hstack, dstack
有極簡的 r_, c_
用下面兩個數(shù)組來舉例:
arr1 = np.array([[1, 2, 3], [4, 5, 6]])arr2 = np.array([[7, 8, 9], [10, 11, 12]])concatenatenp.concatenate([arr1,?arr2],?axis=0)np.concatenate([arr1, arr2], axis=1)[[ 1 2 3]?[ 4 5 6]
?[ 7 8 9]
?[10 11 12]]
[[ 1 2 3 7 8 9]
?[ 4 5 6 10 11 12]]
在 concatenate() 函數(shù)里通過設(shè)定軸,來對數(shù)組進行豎直方向合并 (軸 0) 和水平方向合并 (軸 1)。?
vstack, hstack,?dstack通用的東西是好,但是可能效率不高,NumPy 里還有專門合并的函數(shù)
vstack:v 代表 vertical,豎直合并,等價于?concatenate(axis=0)
hstack:h?代表?horizontal,水平合并,等價于?concatenate(axis=1)
dstack:d 代表 depth-wise,按深度合并,深度有點像彩色照片的 RGB 通道
一圖勝千言:
用代碼驗證一下:
print( np.vstack((arr1, arr2)) )print( np.hstack((arr1, arr2)) )print( np.dstack((arr1, arr2)) )[[ 1 2 3]?[ 4 5 6]
?[ 7 8 9]
?[10 11 12]]
-----------------------
[[ 1 2 3 7 8 9]
?[ 4 5 6 10 11 12]]
-----------------------
[[[ 1 7]
? [ 2 8]
? [ 3 9]]
?[[ 4 10]
? [ 5 11]
? [ 6 12]]]
和 vstack, hstack 不同,dstack 將原數(shù)組的維度增加了一維。
np.dstack((arr1, arr2)).shape(2, 3, 2)r_, c_此外,還有一種更簡單的在豎直和水平方向合并的函數(shù),r_() 和 c_()。
print( np.r_[arr1,arr2] )print( np.c_[arr1,arr2] )[[ 1 2 3]?[ 4 5 6]
?[ 7 8 9]
?[10 11 12]]
[[ 1 2 3 7 8 9]
?[ 4 5 6 10 11 12]]
除此之外,r_()?和?c_() 有什么特別之處么?(如果完全和 vstack() 和hstack() 一樣,那也沒有存在的必要了)
知識點1. 參數(shù)可以是切片。print( np.r_[-2:2:1, [0]*3, 5, 6] )[-2 -1 0 1 0 0 0 5 6]2. 第一個參數(shù)可以是控制參數(shù),如果它用 'r' 或 'c' 字符可生成線性代數(shù)最常用的 matrix (和二維 numpy array 稍微有些不同)np.r_['r', [1,2,3], [4,5,6]]matrix([[1, 2, 3, 4, 5, 6]])3. 第一個參數(shù)可以是控制參數(shù),如果它寫成 ‘a(chǎn),b,c’ 的形式,其中a:代表軸,按「軸 a」來合并b:合并后數(shù)組維度至少是?bc:在第 c 維上做維度提升看不懂吧?沒事,先用程序感受一下:print( np.r_['0,2,0', [1,2,3], [4,5,6]] )print( np.r_['0,2,1', [1,2,3], [4,5,6]] )print( np.r_['1,2,0', [1,2,3], [4,5,6]] )print( np.r_['1,2,1', [1,2,3], [4,5,6]] )[[1]?[2]
?[3]
?[4]
?[5]
?[6]]
----------------
[[1 2 3]
?[4 5 6]]
----------------
[[1 4]
?[2 5]
?[3 6]]
----------------
[[1 2 3 4 5 6]]還看不懂吧 (但至少知道完事后的維度是 2,即字符串?‘a(chǎn),b,c’ 的 b起的作用?)?沒事,我再畫個圖。還沒懂徹底吧?沒事,我再解釋下。字符串?‘a(chǎn),b,c’?總共有四類,分別是
'0, 2, 0'
'0, 2, 1'
'1, 2, 0'
'1, 2, 1'
c = 0 代表在「軸 0」上升一維,因此得到 [[1],[2],[3]] 和?[[4],[5],[6]]?
c = 1 代表在「軸 1」上升一維,因此得到 [[1,2,3]] 和 [[4,5,6]]
a = 0, 沿著「軸 0」合并
a = 1, 沿著「軸 1」合并
使用「分裂」函數(shù)有兩種選擇
有通用的 split
有專門的 hsplit,?vsplit
用下面數(shù)組來舉例:
arr = np.arange(25).reshape((5,5))print( arr )[[ 0 1 2 3 4]?[ 5 6 7 8 9]
?[10 11 12 13 14]
?[15 16 17 18 19]
?[20 21 22 23 24]]split
和?concatenate()?函數(shù)一樣,我們可以在 split() 函數(shù)里通過設(shè)定軸,來對數(shù)組沿著豎直方向分裂 (軸 0) 和沿著水平方向分裂 (軸 1)。?
first, second, third = np.split(arr,[1,3])print( 'The first split is', first )print( 'The second split is', second )print( 'The third split is', third )The first split is [[0 1 2 3 4]]The second split is [[ 5 6 7 8 9]
[10 11 12 13 14]]
The third split is [[15 16 17 18 19]
[20 21 22 23 24]]
split() 默認沿著軸 0 分裂,其第二個參數(shù) [1,?3] 相當于是個切片操作,將數(shù)組分成三部分:
第一部分 - :1?(即第 1 行)
第二部分 -?1:3?(即第 2 到 3 行)
第二部分 -?3:??(即第 4 到 5 行)
vsplit() 和 split(axis=0) 等價,hsplit() 和 split(axis=1) 等價。一圖勝千言:
為了和上面不重復,我們只看 hsplit。
first, second, third = np.hsplit(arr,[1,3])print( 'The first split is', first )print( 'The second split is', second )print( 'The third split is', third )The first split is [[ 0]????????????????????[ 5]
????????????????????[10]
????????????????????[15]
????????????????????[20]]
The second split is [[ 1 2]
???????????????????? [ 6 7]
???????????????????? [11 12]
???????????????????? [16 17]
????????????????? ? ?[21 22]]
The third split is [[ 3 4]
????????????????????[ 8 9]
????????????????????[13 14]
????????????????????[18 19]
????????????????????[23 24]]
4.3
重復和拼接
重復 (repeat) 和拼接 (tile) 這兩個操作本質(zhì)都是復制
重復是在元素層面復制
拼接是在數(shù)組層面復制
函數(shù) repeat() 復制的是數(shù)組的每一個元素,參數(shù)有幾種設(shè)定方法:
一維數(shù)組:用標量和列表來復制元素的個數(shù)
多維數(shù)組:用標量和列表來復制元素的個數(shù),用軸來控制復制的行和列
[0 0 0 1 1 1 2 2 2]
標量參數(shù) 3 - 數(shù)組 arr 中每個元素復制 3 遍。
列表print( arr.repeat([2,3,4]) )[0 0 1 1 1 2 2 2 2]列表參數(shù) [2,3,4] - 數(shù)組 arr 中每個元素分別復制 2, 3, 4 遍。
標量和軸arr2d = np.arange(6).reshape((2,3))print( arr2d )print( arr2d.repeat(2, axis=0) )[[0 1 2]?[3 4 5]]
[[0 1 2]
?[0 1 2]
?[3 4 5]
[3 4 5]]
標量參數(shù) 2 和軸 0 - 數(shù)組 arr2d 中每個元素沿著軸 0 復制 2 遍。
列表和軸print( arr2d.repeat([2,3,4], axis=1) )[[0 0 1 1 1 2 2 2 2]?[3 3 4 4 4 5 5 5 5]]
列表參數(shù) [2,3,4]?和軸 1 - 數(shù)組 arr2d 中每個元素沿著軸 1 分別復制 2, 3, 4 遍。
拼接函數(shù)?tile()?復制的是數(shù)組本身,參數(shù)有幾種設(shè)定方法:
標量:把數(shù)組當成一個元素,一列一列復制
形狀:把數(shù)組當成一個元素,按形狀復制
?[3 4 5]]
[[0 1 2 0 1 2]
?[3 4 5 3 4 5]]
標量參數(shù) 2?- 數(shù)組 arr 按列復制 2 遍。
形狀print( np.tile(arr2d, (2,3)) )[[0 1 2 0 1 2 0 1 2]?[3 4 5 3 4 5 3 4 5]
?[0 1 2 0 1 2 0 1 2]
?[3 4 5 3 4 5 3 4 5]]
標量參數(shù) (2,3)?- 數(shù)組 arr 按形狀復制 6 (2×3) 遍,并以 (2,3) 的形式展現(xiàn)。
4.4
其他操作
本節(jié)討論數(shù)組的其他操作,包括排序 (sort),插入 (insert),刪除 (delete) 和復制 (copy)。
排序排序包括直接排序 (direct sort) 和間接排序 (indirect sort)。
直接排序arr = np.array([5,3,2,6,1,4])print( 'Before sorting', arr )arr.sort()print(?'After?sorting',?arr?)Before sorting [5 3 2 6 1 4]After sorting [1 2 3 4 5 6]
sort()函數(shù)是按升序 (ascending order) 排列的,該函數(shù)里沒有參數(shù)可以控制 order,因此你想要按降序排列的數(shù)組,只需
print( arr[::-1] )[6 5 4 3 2 1]現(xiàn)在讓人困惑的地方來了。
知識點用來排序 numpy 用兩種方式:arr.sort()
np.sort( arr )
?[26 27 28 0]
?[ 9 14 24 13]]
第一種?arr.sort(),對第一列排序,發(fā)現(xiàn) arr 的元素改變了。arr[:, 0].sort() print( arr )[[ 9 32 23 30]
?[24 27 28 0]
?[26 14 24 13]]
第二種 np.sort(arr),對第二列排序,但是 arr?的元素不變。np.sort(arr[:,1])array([ 14, 27, 32])print( arr )[[ 9 32 23 30]
?[24 27 28 0]
?[26 14 24 13]]
此外也可以在不同的軸上排序,對于二維數(shù)組,在「軸 0」上排序是「跨行」排序,在「軸 1」上排序是「跨列」排序。
arr.sort(axis=1)print( arr )[[ 9 23 30 32]?[ 0 24 27 28]
?[13 14 24 26]]間接排序
有時候我們不僅僅只想排序數(shù)組,還想在排序過程中提取每個元素在原數(shù)組對應(yīng)的索引(index),這時 argsort() 就派上用場了。以排列下面五個學生的數(shù)學分數(shù)為例:
score = np.array([100, 60, 99, 80, 91])idx = score.argsort()print( idx )[1 3 4 2 0]這個 idx = [1 3 4 2 0] 怎么理解呢?很簡單,排序完之后分數(shù)應(yīng)該是 [60 80 91 99 100],
60,即 score[1] 排在第0位, 因此 idx[0] =1
80,即 score[3] 排在第1?位, 因此 idx[1] =3
91,即 score[4] 排在第2?位, 因此 idx[2] =4
99,即 score[2] 排在第3?位, 因此 idx[3] =2
100,即 score[0] 排在第4?位, 因此 idx[4] =0
用這個 idx 對 score 做一個「花式索引」得到?(還記得上貼的內(nèi)容嗎?)
print( score[idx] )[ 60 80 91 99 100]再看一個二維數(shù)組的例子。
arr = np.random.randint( 40, size=(3,4) )print( arr )[[24 32 23 30]?[26 27 28 0]
?[ 9 14 24 13]]
對其第一行 arr[0] 排序,獲取索引,在應(yīng)用到所用行上。
arr[:, arr[0].argsort()]array([[23, 24, 30, 32],? ? ? ?[28, 26, 0, 27],
? ? ? ?[24, 9, 13, 14]])
這不就是「花式索引」嗎?來我們分解一下以上代碼,先看看索引。
print( arr[0].argsort() )[2, 0, 3, 1]「花式索引」來了,結(jié)果和上面一樣的。
arr[:, [2, 0, 3, 1]]array([[23, 24, 30, 32],? ? ? ?[28, 26, 0, 27],
? ? ? ?[24, 9, 13, 14]])插入和刪除
和列表一樣,我們可以給 numpy 數(shù)組
用insert()函數(shù)在某個特定位置之前插入元素
用delete()函數(shù)刪除某些特定元素
[ 0 100 1 2 3 4 5]
[0 2 4 5]復制
用copy()函數(shù)來復制數(shù)組 a 得到 a_copy,很明顯,改變 a_copy 里面的元素不會改變 a。
a = np.arange(6)a_copy = a.copy()print( 'Before changing value, a is', a )print( 'Before changing value, a_copy is', a_copy )a_copy[-1] = 99print( 'After changing value, a_copy is', a_copy )print( 'After changing value, a is', a )Before changing value, a is [0 1 2 3 4 5]Before changing value, a_copy is [0 1 2 3 4 5]
After changing value, a_copy is [ 0 1 2 3 4 99]
After changing value, a is [0 1 2 3 4 5]5數(shù)組的計算
本節(jié)介紹四大類數(shù)組計算,具體有
元素層面 (element-wise) 計算
線性代數(shù) (linear algebra) 計算
元素整合 (element aggregation) 計算
廣播機制 (broadcasting) 計算
5.1
元素層面計算
Numpy 數(shù)組元素層面計算包括:
二元運算?(binary operation):加減乘除
數(shù)學函數(shù):倒數(shù)、平方、指數(shù)、對數(shù)
比較運算?(comparison)
先定義兩個數(shù)組 arr1 和 arr2。
arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])arr2 = np.ones((2,3)) * 2print( arr1 )print( arr2 )[[1. 2. 3.][4. 5. 6.]]
[[2. 2. 2.]
[2. 2. 2.]]加、減、乘、除print( arr1 + arr2 + 1 )print(?arr1 - arr2 )print(?arr1?*?arr2?)print(?arr1?/?arr2?)[[4. 5. 6.]
?[7. 8. 9.]]
[[-1. 0. 1.]
?[ 2. 3. 4.]]
[[ 2. 4. 6.]
?[ 8. 10. 12.]]
[[0.5 1. 1.5]
?[2. 2.5 3. ]]倒數(shù)、平方、指數(shù)、對數(shù)print( 1 / arr1 )print( arr1 ** 2 )print( np.exp(arr1) )print( np.log(arr1) )[[1. 0.5 0.33333333]
[0.25 0.2 0.16666667]]
[[ 1. 4. 9.]
[16. 25. 36.]]
[[ 2.71828183 7.3890561 20.08553692]
[ 54.59815003 148.4131591 403.42879349]]
[[0. 0.69314718 1.09861229]
[1.38629436 1.60943791 1.79175947]]比較arr1 > arr2arr1 > 3array([[False, False, True],
???????[ True, True, True]])
array([[False, False, False],
???????[ True, True, True]])
從上面結(jié)果可知
「數(shù)組和數(shù)組間的二元運算」都是在元素層面上進行的
「作用在數(shù)組上的數(shù)學函數(shù)」都是作用在數(shù)組的元素層面上的。
「數(shù)組和數(shù)組間的比較」都是在元素層面上進行的
但是在「數(shù)組和標量間的比較」時,python 好像先把 3 復制了和 arr1 形狀一樣的數(shù)組 [[3,3,3], [3,3,3]],然后再在元素層面上作比較。上述這個復制標量的操作叫做「廣播機制」,是 NumPy 里最重要的一個特點,在下一節(jié)會詳細講到。
5.2
線性代數(shù)計算
在機器學習、金融工程和量化投資的編程過程中,因為運行速度的要求,通常會向量化 (vectorization) 而涉及大量的線性代數(shù)運算,尤其是矩陣之間的乘積運算。
但是,在 NumPy 默認不采用矩陣運算,而是數(shù)組 (ndarray) 運算。矩陣只是二維,而數(shù)組可以是任何維度,因此數(shù)組運算更通用些。
如果你非要二維數(shù)組 arr2d 進項矩陣運算,那么可以通過調(diào)用以下函數(shù)來實現(xiàn):
A = np.mat(arr2d)
A = np.asmatrix(arr2d)
下面我們分別對「數(shù)組」和「矩陣」從創(chuàng)建、轉(zhuǎn)置、求逆和相乘四個方面看看它們的同異。
創(chuàng)建創(chuàng)建數(shù)組 arr2d 和矩陣 A,注意它們的輸出有 array 和 matrix 的關(guān)鍵詞。
arr2d = np.array([[1,2],[3,1]])arr2darray([[1, 2],? ? ? ?[3, 1]])A = np.asmatrix(arr2d)Amatrix([[1, 2],
? ? ? ? [3, 1]])轉(zhuǎn)置
數(shù)組用 arr2d.T 操作或 arr.tranpose() 函數(shù),而矩陣用 A.T 操作。主要原因就是 .T 只適合二維數(shù)據(jù),上貼最后也舉了個三維數(shù)組在軸 1 和軸 2 之間的轉(zhuǎn)置,這時就需要用函數(shù)?arr2d.tranpose(1, 0, 2) 來實現(xiàn)了。
print( arr2d.T )print( arr2d.transpose() )print( A.T )[[1 3]?[2 1]]
[[1 3]
?[2 1]]
[[1 3]
?[2 1]]求逆
數(shù)組用 np.linalg.inv() 函數(shù),而矩陣用 A.I 和 A**-1 操作。
print( np.linalg.inv(arr2d) )print( A.I )print( A**-1 )[[-0.2 0.4]?[ 0.6 -0.2]]
[[-0.2 0.4]
?[ 0.6 -0.2]]
[[-0.2 0.4]
?[ 0.6 -0.2]]相乘
相乘是個很模棱兩可的概念
數(shù)組相乘是在元素層面進行,
矩陣相乘要就是數(shù)學定義的矩陣相乘 (比如第一個矩陣的列要和第二個矩陣的行一樣)
看個例子,「二維數(shù)組」相乘「一維數(shù)組」,「矩陣」相乘「向量」,看看有什么有趣的結(jié)果。
首先定義「一維數(shù)組」arr 和?「列向量」b:
arr = np.array([1,2])b = np.asmatrix(arr).Tprint( arr.shape, b.shape )(2,) (2, 1)由上面結(jié)果看出, arr 的形狀是 (2,),只含一個元素的元組只說明 arr 是一維,數(shù)組是不分行數(shù)組或列數(shù)組的。而 b 的形狀是 (2,1),顯然是列向量。
相乘都是用 * 符號,
print( arr2d*arr )print( A*b )[[1 4]?[3 2]]
[[5]
?[5]]
由上面結(jié)果可知,
二維數(shù)組相乘一維數(shù)組得到的還是個二維數(shù)組,解釋它需要用到「廣播機制」,這是下節(jié)的重點討論內(nèi)容。現(xiàn)在大概知道一維數(shù)組 [1 2] 第一個元素 1 乘上 [1 3] 得到 [1 3],而第二個元素 2 乘上 [2 1] 得到 [4 2]。
而矩陣相乘向量的結(jié)果和我們學了很多年的線代結(jié)果很吻合。
再看一個例子,「二維數(shù)組」相乘「二維數(shù)組」,「矩陣」相乘「矩陣」
print( arr2d*arr2d )print( A*A )[[1 4]?[9 1]]
[[7 4]
?[6 7]]
由上面結(jié)果可知,
雖然兩個二維數(shù)組相乘得到二維數(shù)組,但不是根據(jù)數(shù)學上矩陣相乘的規(guī)則得來的,而且由元素層面相乘得到的。兩個 [[1 2], [3,1]] 的元素相乘確實等于?[[1 4], [9,1]]。?
而矩陣相乘矩陣的結(jié)果和我們學了很多年的線代結(jié)果很吻合。
問題來了,那么怎么才能在數(shù)組上實現(xiàn)「矩陣相乘向量」和「矩陣相乘矩陣」呢?用點乘函數(shù) dot()。
print( np.dot(arr2d,arr) )print( np.dot(arr2d,arr2d) )[5 5][[7 4]
?[6 7]]
結(jié)果對了,但還有一個小小的差異
矩陣相乘列向量的結(jié)果是個列向量,寫成 [[5],[5]],形狀是 (2,1)
二維數(shù)組點乘一維數(shù)組結(jié)果是個一維數(shù)組,寫成 [5, 5],形狀是 (2,)
由此我們來分析下 NumPy 里的 dot() 函數(shù),計算數(shù)組和數(shù)組之間的點乘結(jié)果。
點乘函數(shù)本節(jié)的內(nèi)容也來自〖張量 101〗,通常我們也把 n 維數(shù)組稱為張量,點乘左右兩邊最常見的數(shù)組就是
向量?(1D)?和向量?(1D)
矩陣?(2D)?和向量?(1D)
矩陣?(2D)?和矩陣?(2D)
分別看看三個簡單例子。
例一:np.dot(向量,?向量)?實際上做的就是內(nèi)積,即把兩個向量每個元素相乘,最后再加總。點乘結(jié)果?10?是個標量?(0D?數(shù)組),形狀?= ()。
x = np.array( [1, 2, 3] )y = np.array( [3, 2, 1] )z = np.dot(x,y)print( z.shape )print( z )()10
例二:np.dot(矩陣,?向量)?實際上做的就是普通的矩陣乘以向量。點乘結(jié)果是個向量?(1D?數(shù)組),形狀?= (2, )。
x = np.array( [1, 2, 3] )y = np.array( [[3, 2, 1], [1, 1, 1]] )z = np.dot(y,x)print( z.shape )print( z )(2,)[10 6]
例三:np.dot(矩陣,?矩陣)?實際上做的就是普通的矩陣乘以矩陣。點乘結(jié)果是個矩陣?(2D?數(shù)組),形狀?= (2, 3)。
x = np.array( [[1, 2, 3], [1, 2, 3], [1, 2, 3]] )y = np.array( [[3, 2, 1], [1, 1, 1]] )z = np.dot(y,x)print( z.shape )print( z )(2, 3)[[ 6 12 18]
[ 3 6 9]]
從例二和例三看出,當 x 第二個維度的元素?(x.shape[1])?和 y 第一個維度的元素?(y.shape[0])?個數(shù)相等時,np.dot(X, Y)?才有意義,點乘得到的結(jié)果形狀 = (X.shape[0], y.shape[1])。
上面例子都是低維數(shù)組?(維度?≤ 2)?的點乘運算,接下來我們看兩個稍微復雜的例子。
例四:當?x?是?3D?數(shù)組,y?是?1D?數(shù)組,np.dot(x, y)?是將?x?和?y?最后一維的元素相乘并加總。此例?x?的形狀是?(2, 3,?4),y?的形狀是?(4, ),因此點乘結(jié)果的形狀是?(2, 3)。
x = np.ones( shape=(2, 3, 4) )y = np.array( [1, 2, 3, 4] )z = np.dot(x,y)print( z.shape )print( z )(2, 3)[[10. 10. 10]
[10. 10. 10]]
例五:當?x?是?3D?數(shù)組,y?是?2D?數(shù)組,np.dot(x, y)?是將?x?的最后一維和?y?的倒數(shù)第二維的元素相乘并加總。此例?x?的形狀是?(2, 3,?4),y?的形狀是?(4, 2),因此點乘結(jié)果的形狀是?(2, 3, 2)。
x = np.random.normal( 0, 1, size=(2, 3, 4) )y = np.random.normal( 0, 1, size=(4, 2) )z = np.dot(x,y)print( z.shape )print( z )(2, 3, 2)[[[ 2.11753451 -0.27546168]
? [-1.23348676 0.42524653]
? [-4.349676 -0.3030879 ]]
?[[ 0.15537744, 0.44865273]
? [-3.09328194, -0.43473885]
? [ 0.27844225, -0.48024693]]]
例五的規(guī)則也適用于?nD?數(shù)組和?mD?數(shù)組?(當?m ≥ 2?時)?的點乘。
5.3
元素整合計算
在數(shù)組中,元素可以以不同方式整合 (aggregation)。拿求和 (sum) 函數(shù)來說,我們可以對數(shù)組
所有的元素求和
在某個軸 (axis) 上的元素求和
先定義數(shù)組
arr?=?np.arange(1,7).reshape((2,3))arrarray([[1, 2, 3],???????[4, 5, 6]])
不難看出它是一個矩陣,分別對全部元素、跨行 (across rows)、跨列 (across columns) 求和:
print( 'The total sum is', arr.sum() )print( 'The sum across rows is', arr.sum(axis=0) )print( 'The sum across columns is', arr.sum(axis=1) )The total sum is 21The sum across rows is [5 7 9]
The sum across columns is [ 6 15]
分析上述結(jié)果:
1, 2, 3, 4, 5, 6 的總和是 21
跨行求和 = [1 2 3] + [4 5 6] = [5 7 9]
跨列求和 = [1+2+3 4+5+6] = [6 15]
行和列這些概念對矩陣 (二維矩陣) 才適用,高維矩陣還是要用軸 (axis) 來區(qū)分每個維度。讓我們拋棄「行列」這些特殊概念,擁抱「軸」這個通用概念來重看數(shù)組 (一到四維) 把。
規(guī)律:n 維數(shù)組就有 n 層方括號。最外層方括號代表「軸 0」即 axis=0,依次往里方括號對應(yīng)的 axis 的計數(shù)加 1。
嚴格來說,numpy 打印出來的數(shù)組可以想象帶有多層方括號的一行數(shù)字。比如二維矩陣可想象成
????[[1, 2, 3],[4, 5, 6]]
三維矩陣可想象成
????[[[1,2,3],?[4,5,6]],?[[7,8,9],?[10,11,12]]]
由于屏幕的寬度不夠,我們才把它們寫成一列列的,如下
????[?[?[1, 2, 3]
? ? ? ? ?[4, 5, 6]?]?
? ? ??[?[7, 8, 9]?
? ? ? ? ?[10, 11, 12]?]?]
但在你腦海里,應(yīng)該把它們想成一整行。這樣會便于你理解如何按不同軸做整合運算。
有了軸的概念,我們再來看看 sum() 求和函數(shù)。
一維數(shù)組分析結(jié)果:
1, 2, 3 的總和是 6
在軸 0(只有一個軸) 上的元素求和是 6
用代碼驗證一下:
arr = np.array([1,2,3])print(?'The?total?sum?is',?arr.sum()?)print( 'The sum on axis0 is', arr.sum(axis=0) )The total sum is 6The sum on axis0 is 6
求和一維數(shù)組沒什么難度,而且也看不出如果「按軸求和」的規(guī)律。下面看看二維數(shù)組。
二維數(shù)組分析結(jié)果:
1 到 6 的總和是 6
軸 0?上的元素 (被一個紅方括號[]包住的) 是[1, 2, 3]和[4, 5, 6],求和得到[[5, 6, 7]]
軸 1?上的元素 (被兩個藍方括號[]?包住的) 分別是?1, 2, 3?和?4, 5, 6,求和得到?[[1+2+3, 4+5+6]]=?[[6, 15]]
用代碼驗證一下:
arr = np.arange(1,7).reshape((2,3))print( arr )[[1 2 3]?[4 5 6]]print(?'The?total?sum?is',?arr.sum()?)print( 'The sum on axis0 is', arr.sum(axis=0) )print( 'The sum on axis1 is', arr.sum(axis=1) )The total sum is 21
The sum on axis0 is [5 7 9]
The sum on axis1 is [ 6 15]
結(jié)果是對的,但是好像括號比上圖推導出來的少一個。原因np.sum()里面有個參數(shù)是 keepdims,意思是「保留維度」,默認值時 False,因此會去除多余的括號,比如 [[5, 7, 9]] 會變成 [5, 7, 9]。
如果把?keepdims 設(shè)置為 True,那么打印出來的結(jié)果和上圖推導的一模一樣。
print(?arr.sum(axis=0,?keepdims=True)?)print(?arr.sum(axis=1,?keepdims=True)?)[[5 7 9]][[ 6]
[15]]三維數(shù)組
分析結(jié)果:
1 到 12 的總和是 78
軸 0?上的元素是一個紅方括號[]?包住的兩個?[[ ]],對其求和得到一個?[?[[ ]]?]
軸 1?上的元素是兩個藍方括號[]?包住的兩個[ ],對其求和得到兩個?[[ ]],即?[?[[ ]],?[[ ]]?]
軸 2?上的元素是四個綠方括號[]?包住的三個標量,對其求和得到四個[],即?[?[[ ],?[ ]],?[[ ],?[ ]]?]
用代碼驗證一下:
arr = np.arange(1,13).reshape((2,2,3))print(arr)[[[ 1 2 3]? [ 4 5 6]]
?[[ 7 8 9]
? [10 11 12]]]print( 'The total sum is', arr.sum() )print( 'The sum on axis0 is', arr.sum(axis=0) )print( 'The sum on axis1 is', arr.sum(axis=1) )print( 'The sum on axis2 is', arr.sum(axis=2) )The total sum is 78
The sum on axis0 is [[ 8 10 12] [14 16 18]]
The sum on axis1 is [[ 5 7 9] [17 19 21]]
The sum on axis2 is [[ 6 15] [24 33]]
打印出來的結(jié)果比上圖推導結(jié)果少一個括號,也是因為 keepdims 默認為 False。
四維數(shù)組不解釋了,彩色括號畫的人要抓狂了。通用規(guī)律:當在某根軸上求和,明晰該軸的元素,再求和。具體說來:
在軸 0上求和,它包含是兩個[],對其求和
在軸 1?上求和,它包含是兩個?[],對其求和
在軸 2?上求和,它包含是兩個?[],對其求和
在軸 3?上求和,它包含是三個標量,對其求和
用代碼驗證一下:
arr = np.arange(1,25).reshape((2,2,2,3))print(arr)[[[[ 1 2 3]? ?[ 4 5 6]]
??[[ 7 8 9]
? ?[10 11 12]]]
?[[[13 14 15]
???[16 17 18]]
??[[19 20 21]
???[22 23 24]]]]print( 'The total sum is', arr.sum() )print( 'The sum on axis0 is', arr.sum(axis=0) )print( 'The sum on axis1 is', arr.sum(axis=1) )print( 'The sum on axis2 is', arr.sum(axis=2) )print( 'The sum on axis3 is', arr.sum(axis=3) )is on axis1 on axis2 on axis3
打印出來的結(jié)果比上圖推導結(jié)果少一個括號,也是因為 keepdims 默認為 False。
小節(jié)除了?sum?函數(shù),整合函數(shù)還包括?min,?max,?mean,?std?和?cumsum,分別是求最小值、最大值、均值、標準差和累加,這些函數(shù)對數(shù)組里的元素整合方式和?sum?函數(shù)相同,就不多講了。總結(jié)來說我們可以對數(shù)組
所有的元素整合
在某個軸 (axis) 上的元素整合
整合函數(shù)= {sum,?min,?max,?mean,?std,?cumsum}
5.4
廣播機制計算
當對兩個形狀不同的數(shù)組按元素操作時,可能會觸發(fā)「廣播機制」。具體做法,先適當復制元素使得這兩個數(shù)組形狀相同后再按元素操作,兩個步驟:
廣播軸?(broadcast axis):比對兩個數(shù)組的維度,將形狀小的數(shù)組的維度?(軸)?補齊
復制元素:順著補齊的軸,將形狀小的數(shù)組里的元素復制,使得最終形狀和另一個數(shù)組吻合
在給出「廣播機制」需要的嚴謹規(guī)則之前,我們先來看看幾個簡單例子。
例一:標量和一維數(shù)組arr = np.arange(5)print( arr )print( arr + 2 )[0 1 2 3 4][2 3 4 5 6]
元素 2 被廣播到數(shù)組 arr 的所有元素上。
例二:一維數(shù)組和二維數(shù)組arr = np.arange(12).reshape((4,3))print(?arr )print(?arr.mean(axis=0) )print( arr - arr.mean(axis=0) )[[ 0 1 2]?[ 3 4 5]
?[ 6 7 8]
?[ 9 10 11]]
[4.5 5.5 6.5]
[[-4.5 -4.5 -4.5]
?[-1.5 -1.5 -1.5]
?[ 1.5 1.5 1.5]
?[ 4.5 4.5 4.5]]
沿軸 0 的均值的一維數(shù)組被廣播到數(shù)組 arr 的所有的行上。
現(xiàn)在我們來看看「廣播機制」的規(guī)則:
廣播機制的規(guī)則知識點當我們對兩個數(shù)組操作時,如果它們的形狀
不相容 (incompatible),廣播機制不能進行
相容 (compatible),廣播機制可以進行
因此,進行廣播機制分兩步
檢查兩個數(shù)組形狀是否兼容,即從兩個形狀元組最后一個元素,來檢查
它們是否相等
是否有一個等于 1
一旦它們形狀兼容,確定兩個數(shù)組的最終形狀。
用個例子來應(yīng)用以上廣播機制規(guī)則
a = np.array([[1,2,3]])b = np.array([[4],[5],[6]])print( 'The shape of a is', a.shape )print( 'The shape of b is', b.shape )The shape of a is (1, 3)The shape of b is (3, 1)
回顧進行廣播機制的兩步
檢查數(shù)組 a 和 b 形狀是否兼容,從兩個形狀元組 (1, 3) 和 (3, 1)最后一個元素開始檢查,發(fā)現(xiàn)它們都滿足『有一個等于 1』的條件。
因此它們形狀兼容,兩個數(shù)組的最終形狀為 (max(1,3), max(3,1)) = (3, 3)
到此,a 和 b 被擴展成 (3, 3) 的數(shù)組,讓我們看看 a + b 等于多少
c = a + bprint(?'The?shape?of?c?is',?c.shape )print( 'a is', a )print( 'b is', b )print( 'c = a + b =', c )The shape of c is (3, 3)a is [[1 2 3]]
b is [[4]
? ? ? [5]
? ? ? [6]]
c = a + b = [[5 6 7]
? ? ?????????[6 7 8]
???????????? [7 8 9]]例四:維度不一樣a = np.arange(5)b = np.array(2)print( 'The dimension of a is', a.ndim, 'and the shape of a is', a.shape )print( 'The dimension of b is', b.ndim, 'and the shape of b is', b.shape )The dimension of a is 1 and the shape of a is (5,)
The dimension of b is 0 and the shape of b is ()
數(shù)組 a 和 b 形狀分別為 (5,) 和 (),首先我們把缺失的維度用 1 補齊得到 (5,) 和 (1,),再根據(jù)廣播機制那套流程得到這兩個形狀是兼容的,而且最終形狀為 (5,)。
用代碼來看看 a + b 等于多少
c = a + bprint( 'The dimension of c is', c.ndim, 'and the shape of c is', c.shape, '\n' )print( 'a is', a )print( 'b is', b )print( 'c = a + b =', c )The dimension of c is 1 and the shape of c is (5,)a is [0 1 2 3 4]
b is 2
c = a + b = [2 3 4 5 6]
現(xiàn)在對廣播機制有概念了吧,來趁熱打鐵搞清楚下面這五個例子,你就完全弄懂它了。
a = np.array( [[[1,2,3], [4,5,6]]] )b1 = np.array( [[1,1,1], [2,2,2], [3,3,3]] )b2 = np.arange(3).reshape((1,3))b3 = np.arange(6).reshape((2,3))b4 = np.arange(12).reshape((2,2,3))b5 = np.arange(6).reshape((2,1,3))print( 'The dimension of a is', a.ndim, 'and the shape of a is', a.shape )print( 'The dimension of b1 is', b.ndim, 'and the shape of b1 is', b1.shape, '\n')print( 'The dimension of a is', a.ndim, 'and the shape of a is', a.shape )print( 'The dimension of b2 is', b.ndim, 'and the shape of b2 is', b2.shape, '\n' )print( 'The dimension of a is', a.ndim, 'and the shape of a is', a.shape )print( 'The dimension of b3 is', b.ndim, 'and the shape of b3 is', b3.shape, '\n' )print( 'The dimension of a is', a.ndim, 'and the shape of a is', a.shape )print( 'The dimension of b4 is', b.ndim, 'and the shape of b4 is', b4.shape, '\n' )print( 'The dimension of a is', a.ndim, 'and the shape of a is', a.shape )print( 'The dimension of b5 is', b.ndim, 'and the shape of b5 is', b5.shape )The dimension of a is 3 and the shape of a is (1, 2, 3)The dimension of b1 is 0 and the shape of b1 is (3, 3)
The dimension of a is 3 and the shape of a is (1, 2, 3)
The dimension of b2 is 0 and the shape of b2 is (1, 3)
The dimension of a is 3 and the shape of a is (1, 2, 3)
The dimension of b3 is 0 and the shape of b3 is (2, 3)
The dimension of a is 3 and the shape of a is (1, 2, 3)
The dimension of b4 is 0 and the shape of b4 is (2, 2, 3)
The dimension of a is 3 and the shape of a is (1, 2, 3)
The dimension of b5 is 0 and the shape of b5 is (2, 1, 3)
對于數(shù)組 a 和 b1,它們形狀是 (1, 2, 3) 和 (3, 3)。元組最后一個都是 3,兼容;倒數(shù)第二個是 3 和 2,即不相等,也沒有一個是 1,不兼容!a 和 b1 不能進行廣播機制。不行就看看下面代碼:
c1 = a + b1print( c1 )print( c1.shape )ValueError: operands could not be broadcasttogether with shapes (1,2,3) (3,3)
a 和其他 b2, b3, b4, b5 都可以進行廣播機制,自己分析吧。
c2 = a + b2print( c2 )print( c2.shape )[[[1 3 5]? [4 6 8]]]
(1, 2, 3)c3?=?a?+?b3print(?c3?)print(?c3.shape?)[[[ 1 3 5]
? [ 7 9 11]]]
(1, 2, 3)c4?=?a?+?b4print(?c4?)print(?c4.shape?)[[[ 1 3 5]
? [ 7 9 11]]
?[[ 7 9 11]
? [13 15 17]]]
(2, 2, 3)c5?=?a?+?b5print(?c5?)print(?c5.shape?)[[[ 1 3 5]
? [ 4 6 8]]
?[[ 4 6 8]
? [ 7 9 11]]]
(2, 2, 3)6總結(jié)
NumPy 篇終于完結(jié)!即上貼討論過的數(shù)組創(chuàng)建、數(shù)組存載和數(shù)組獲取,本貼討論了數(shù)組變形、數(shù)組計算。
數(shù)組變形有以下重要操作:
改變維度的重塑和打平
改變分合的合并和分裂
復制本質(zhì)的重復和拼接
其他排序插入刪除復制
數(shù)組計算有以下重要操作:
元素層面:四則運算、函數(shù),比較
線性代數(shù):務(wù)必弄懂點乘函數(shù) dot()
元素整合:務(wù)必弄懂軸這個概念!
廣播機制:太重要了,神經(jīng)網(wǎng)絡(luò)無處不在!
我是老表,踏實的人更容易過好生活,本文完。
推薦閱讀:
谷歌出品|推出了史上最強的Python在線編輯器
【Data Mining】機器學習三劍客之Numpy常用用法總結(jié)
Python小白教程:入門篇(下)
數(shù)據(jù)分析從零開始實戰(zhàn)
數(shù)據(jù)分析從零開始實戰(zhàn) |?基礎(chǔ)篇(一)
數(shù)據(jù)分析從零開始實戰(zhàn) |?基礎(chǔ)篇(二)
數(shù)據(jù)分析從零開始實戰(zhàn) |?基礎(chǔ)篇(三)
數(shù)據(jù)分析從零開始實戰(zhàn) | 基礎(chǔ)篇(四)
仔細閱讀下面四篇文章,2小時快速掌握Python基礎(chǔ)知識要點。
完整Python基礎(chǔ)知識要點
Python小知識?|?這些技能你不會?(一)
Python小知識?|?這些技能你不會?(二)
Python小知識?|?這些技能你不會?(三)
Python小知識?|?這些技能你不會?(四)
我是老表,支持我請轉(zhuǎn)發(fā)分享本文。
/今日留言主題/
留言打卡活動也快2個月了,但目前為止堅持連續(xù)打卡的最長只有40天,離101天還有三分之二,所以,今天留言一句30字左右的雞湯鼓勵自己繼續(xù)學習吧。
(參與留言打卡留言字數(shù)不少于30字,否者視為打卡失敗)
留言打卡獎勵
堅持連續(xù)打卡21天,免費進入資源分享群堅持連續(xù)打卡64天,獲得50元以內(nèi)贈書一本堅持連續(xù)打卡100天,獲得100元以內(nèi)贈書一本堅持連續(xù)打卡101天,免費加入老表的知識星球所有獎品一經(jīng)兌換,打卡天數(shù)也自動清零。
總結(jié)
以上是生活随笔為你收集整理的python hstack_Python小白数据科学教程:NumPy (下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ns3 统计一个包所经历的时延_【直击U
- 下一篇: 以图搜图 图像匹配_图像匹配,基于深度学